对Controller层代码进行优化
对Controller层代码优化
Controller层的一些优化方式
统一返回类包装
返回结果枚举类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package com.wakaka.comments.common;
public enum ResultCode implements StatusCode{
SUCCESS(1000,"请求成功"),
FAILED(1001,"请求失败"),
VALIDATE_ERROR(1002,"参数校验失败");
private final int code; private final String message;
ResultCode(int code, String message) { this.code = code; this.message = message; }
@Override public int getCode() { return this.code; }
@Override public String getMsg() { return this.message; } }
|
统一返回包装类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| package com.wakaka.comments.common;
import lombok.Data;
@Data public class ResultVo {
private int code;
private String msg;
private Object data;
public ResultVo(int code, String msg, Object data) { this.code = code; this.msg = msg; this.data = data; }
public static ResultVo Success() { return new ResultVo( ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), null ); }
public static ResultVo Success(Object data) { return new ResultVo( ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), data ); }
public static ResultVo Failed() { return new ResultVo( ResultCode.FAILED.getCode(), ResultCode.FAILED.getMsg(), null ); }
public static ResultVo Failed(String msg) { return new ResultVo( ResultCode.FAILED.getCode(), msg, null ); }
public static ResultVo VALIDATE_ERROR() { return new ResultVo( ResultCode.VALIDATE_ERROR.getCode(), ResultCode.VALIDATE_ERROR.getMsg(), null ); } }
|
自动封装结果
但是我们不能把所有接口都自动封装,应该有一个判断
通过注解对接口接口是否包装进行判断
1 2 3 4 5
| @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface NotControllerResponseAdvice { }
|
继承ResponseBodyAdvice抽象类,可以通过AOP自动对返回结果包装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @RestControllerAdvice(value = "com.wakaka.comments") public class ControllerResponseAdvice implements ResponseBodyAdvice<Object> { @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { boolean flag = returnType.getParameterType().isAssignableFrom(ResultVo.class) || returnType.hasMethodAnnotation(NotControllerResponseAdvice.class); return !flag; }
@Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if (returnType.getGenericParameterType().equals(String.class)){ ObjectMapper objectMapper = new ObjectMapper(); try { return objectMapper.writeValueAsString(new ResultVo( ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), body )); } catch (JsonProcessingException e) { e.printStackTrace(); } } return new ResultVo( ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMsg(), body ); } }
|
参数校验
依赖
1 2 3 4
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
|
Controller方法
使用@Validated注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
@RestController public class TestController {
@PostMapping("/findByVo") public ResultVo findProdcut(@Validated ProductInfoVo vo){
return ResultVo.Success(vo); } @PostMapping("/test2") public String test2(){
return "sadsd"; } @PostMapping("/test3") @NotControllerResponseAdvice public String test3(){
return "sadsd"; } @PostMapping("/test4") public String test4(){ throw new ApiException( "测试" ); } @PostMapping("/test5") public String test5(){ throw new ApiException( ResultCode.FAILED,"测试" ); } }
|
实体类
例如:@NotNull,@Min注解;message为BindException返回信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
@Data public class ProductInfoVo {
@NotNull(message = "商品名称不允许为空") private String productName;
@Min(value = 0, message = "商品价格不允许为负数") private BigDecimal productPrice;
private Integer productStatus; }
|
异常处理
在加入上面参数验证注解后,不符合的请求会抛出异常,这样前端则无法接收到正常的信息,所以我们要对异常进行处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @RestControllerAdvice public class ControllerExceptionAdvice {
@ExceptionHandler(BindException.class) public ResultVo methodArgumentNotValidExceptionHandler(BindException e){ ObjectError objectError = e.getBindingResult().getAllErrors().get(0); return new ResultVo( ResultCode.VALIDATE_ERROR.getCode(), objectError.getDefaultMessage(), null ); } }
|
@RestControllerAdvice
异常处理
AOP思想,在Controller层对异常进行处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
@RestControllerAdvice public class ControllerExceptionAdvice {
@ExceptionHandler(BindException.class) public ResultVo methodArgumentNotValidExceptionHandler(BindException e){ ObjectError objectError = e.getBindingResult().getAllErrors().get(0); return new ResultVo( ResultCode.VALIDATE_ERROR.getCode(), objectError.getDefaultMessage(), null ); }
@ExceptionHandler(ApiException.class) public ResultVo apiExceptionHandler(ApiException e){ return new ResultVo( e.getCode(), e.getMsg(), e.getMessage() ); } }
|
异常类例
在方法内我们抛出了自定义异常,其实这个是无所谓的,我们可以自定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
@Getter public class ApiException extends RuntimeException{ private final int code; private final String msg;
public ApiException(String message){ super(message); this.code = AppCode.APP_ERROR.getCode(); this.msg = AppCode.APP_ERROR.getMsg(); }
public ApiException(StatusCode statusCode, String message){ super(message); this.code = statusCode.getCode(); this.msg = statusCode.getMsg(); } }
|
HandlerInterceptor
权限校验
Token啊巴拉巴拉 ,先跳过了
获得请求时间
RequestMappingHandlerMapping
抽象类
介绍
它会在DispatherServlet初始化过程自动加载,默认会自动加载所有实现HandlerMapping接口的bean。
1
| protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType)
|
会对每个方法进行扫描,我们可以在方法上加入注解,实现一些特定功能
使用示例
跳过HandlerInterceptor
示例注解
1 2 3 4
| @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface PassUrl { }
|
我们首先对RequestMappingHandlerMapping方法内的getMappingForMethod方法进行重写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Configuration public class TestMethodHandler extends RequestMappingHandlerMapping {
@Override protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo mappingForMethod = super.getMappingForMethod(method, handlerType); PassUrl annotation = AnnotationUtils.findAnnotation(method, PassUrl.class); if (annotation != null){ Set<String> patterns = mappingForMethod.getPatternsCondition().getPatterns(); TestInterceptor.addPassUrl(patterns); } return mappingForMethod; } }
|
TestIntercptor,记得注在WebMvcConfigurer实现类内注册拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class TestInterceptor implements HandlerInterceptor {
private static Set<String> passUrl = new HashSet<>();
public static void addPassUrl(Set<String> passUrls) { TestInterceptor.passUrl.addAll(passUrls); }
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String requestUrl = request.getRequestURI(); System.out.println(requestUrl); System.out.println(passUrl); if (passUrl.contains(requestUrl)){ return true; } ResultVo result = ResultVo.Failed("非法访问"); String s = JSONObject.toJSONString(result); response.getOutputStream().write(s.getBytes(StandardCharsets.UTF_8)); return false; } }
|
ThreadLocal
为每一个线程开辟单独存储空间,线程之间相互隔离
使用
注意
在SpringBoot中并不会创建新的线程,而是重复使用线程池中的线程,所以在每次请求声明周期结束后,要清空ThreadLocal中的数据.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Configuration public class LoginHandler implements HandlerInterceptor { static ThreadLocal<String> threadLocal = new ThreadLocal<>();
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { threadLocal.set(request.getParameterMap().toString()); return HandlerInterceptor.super.preHandle(request, response, handler); }
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { threadLocal.remove(); HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }
|