WEB 统一接口返回和异常处理

需求:
Controller 层需要统一返回格式。
实现方案:

  • 静态方法(不推荐):非业务代码显示调用没必要。
  • AOP(推荐)

异常统一处理,减少显示try catch

一、ResponseBodyAdvice

ResponseBodyAdvice 是通过AOP(Aspect-Oriented Programming,面向切面编程)实现的,接口用于拦截和处理控制器返回的响应数据。具体来说,它允许你在返回给客户端之前,对返回的@ResponseBody或者@RestController的响应体进行修改。

统一结果返回类:

@Data
class ResponseWrapper<T> {
    
    
    private int code;
    private String message;
    private T data;

    public ResponseWrapper(int code, String message, T data) {
    
    
        this.code = code;
        this.message = message;
        this.data = data;
    }

    public static <T> ResponseWrapper<T> fail(HttpStatusEnum status, Object... args) {
    
    
        return new ResponseWrapper<>(status.getCode(), String.format(status.getMsg(), args), null);
    }

    public static <T> ResponseWrapper<T> fail(HttpStatusEnum status) {
    
    
        return new ResponseWrapper<>(status.getCode(), status.getMsg(), null);
    }
    
}

定义两个注解,一个用于定义开启统一结果封装,一个定义忽略统一结果封装。

@Target({
    
    ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoWrapResponse {
    
    

}

@Target({
    
    ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SkipResponseWrap {
    
    
}

切面类:

@ControllerAdvice
@RequiredArgsConstructor
public class CustomResponseBodyAdvice implements ResponseBodyAdvice<Object> {
    
    

    private final ObjectMapper objectMapper;
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
    
    
        // 判断是否需要进行封装
        boolean isAutoWrap = AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), AutoWrapResponse.class) ||
                returnType.hasMethodAnnotation(AutoWrapResponse.class);
        boolean isSkipWrap = AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), SkipResponseWrap.class) ||
                returnType.hasMethodAnnotation(SkipResponseWrap.class);

        return isAutoWrap && !isSkipWrap;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    
    
        // 返回封装后的统一格式数据
        if (body instanceof ResponseWrapper || body instanceof ResponseEntity) {
    
    
            return body;  // 已经封装的直接返回
        }

        // 要注意String 类型的情况
        if(body instanceof String) {
    
    
            try {
    
    
                return this.objectMapper.writeValueAsString(new ResponseWrapper<>(200, "成功", body));
            } catch (JsonProcessingException e) {
    
    
                throw new RuntimeException(e);
            }
        }
        return new ResponseWrapper<>(200, "成功", body);  // 统一封装格式
    }
}

注意:String 类型的 selectedConverterType 参数值是 org.springframework.http.converter.StringHttpMessageConverter,而其他数据类型的值是 org.springframework.http.converter.json.MappingJackson2HttpMessageConverter。所以如果返回结果是一个 String 类型的数据需要特殊处理。

二、统一异常处理

可以自定义业务逻辑异常,并且增加相关的异常处理。 Exception 我这里定义成返回 500,前端会跳转到自定义的500异常页面。

@RestControllerAdvice(basePackages="com.github.nan.web")
public class CustomExceptionAdvice {
    
    

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseWrapper handleMethodArgumentNotValidException(MethodArgumentNotValidException ex){
    
    
        BindingResult bindingResult =ex.getBindingResult();
        StringBuilder stringBuilder = new StringBuilder("校验失败:");
        for ( FieldError fieldError : bindingResult.getFieldErrors()) {
    
    
            stringBuilder.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(";");
        }
        return ResponseWrapper.fail(HttpStatusEnum.ILLEGAL_ARGUMENTS_MIXED, stringBuilder.toString());
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity handleException(Exception ex) {
    
    
        // 记录异常日志
        ex.printStackTrace();
        return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
    }

}

猜你喜欢

转载自blog.csdn.net/code_nn/article/details/143302779