SpringBoot 统一结果与异常处理

2022-06-17  本文已影响0人  上岸大虾米

SpringBoot 统一结果与异常处理

1. controller层 现状与优化
1.1 现状
@GetMapping("select")
public Result select(@RequestParam Long productId){
    return Result.success(service.sameProduct(productId));
}

存在以下问题:

  1. return Result.success为controller层固定写法,冗余且不够简洁

  2. 不能直观看到返回值类型

1.2 优化后
@GetMapping("select")
public List<String> select(@RequestParam Long productId){
    return service.sameProduct(productId);
}
1.3 全局结果统一处理
@Configuration
@RestControllerAdvice
public class CommonResultResponseAdvice implements ResponseBodyAdvice<Object> {

    @Resource
    private ObjectMapper objectMapper;

    @Override
    public boolean supports(@NotNull MethodParameter returnType, @NotNull Class<? extends HttpMessageConverter<?>> converterType) {
        // 忽略 IgnoreResult 注解标识的类或方法的返回结果转换
        return !ignoreResult(returnType, IgnoreResult.class);
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, @NotNull MethodParameter returnType, @NotNull MediaType selectedContentType, @NotNull Class<? extends HttpMessageConverter<?>> selectedConverterType, @NotNull ServerHttpRequest request, @NotNull ServerHttpResponse response) {
        String typeName = returnType.getGenericParameterType().getTypeName();
        if (Void.TYPE.getName().equals(typeName)) {
            return body;
        }
        if (body instanceof Result) {
            return body;
        }
        if (body instanceof String) {
            return objectMapper.writeValueAsString(BaseResponse.success(body));
        }
        return Result.success(body);
    }

    private boolean ignoreResult(MethodParameter returnType, Class<? extends Annotation> ignoreAnnotation) {
        Method method = returnType.getMethod();
        return method.getAnnotation(ignoreAnnotation) != null ||
                method.getDeclaringClass().getAnnotation(ignoreAnnotation) != null;
    }
}
1.4 自定义注解

忽略返回结果统一处理注解

@Target({METHOD, TYPE})
@Retention(RUNTIME)
@Documented
public @interface IgnoreResult {
}
2、异常处理现状与优化
2.1 现状
if (product != null) {
    throw new ProductException(ProductExceptionEnum.SID_EXIST, saveProductParam.getSid());
}
// 或着 ,
if (product != null) {
    CommonException.exception(ProductExceptionEnum.PARAM_ERROR);
}

public static void exception(ErrorCodeInterface errorCodeInterface) {
    throw new CommonException(errorCodeInterface);
}

数据有效性校验存在以下问题:

  1. 代码不够简洁,定义过多且大部分仅使用一次的ProductExceptionEnum
  2. 每个项目各自使用自定义异常,有一定的学习了解成本
2.2 优化后

使用Assert进行业务校验,校验失败后直接抛出异常,中断后续操作

Assert.isNull(product,String.format(ProductExceptionEnum.SID_EXIST.getMessage(),saveProductParam.getSid()));
2.3 统一异常处理
@ControllerAdvice
public class GlobalExceptionHandler {

    private static final String INTEREST_CLASS_NAME = "org.springframework.util.Assert";   

    /**
        此处捕获业务 失败 的异常,可考虑不计日志
    */
    @ExceptionHandler({IllegalArgumentException.class,IllegalStateException.class})
        @ResponseBody
        public Result illegalArgumentException(Exception e) {
            String msg = e.getMessage();
            int maxLength = 200;
            if(StringUtils.isBlank(msg) || msg.length() > maxLength){
                msg = "请求参数异常";
            }
            StackTraceElement[] stackTraces = e.getStackTrace();
            if (Stream.of(stackTraces).anyMatch(s -> s.getClassName().equals(INTEREST_CLASS_NAME))) {
                // 不计日志, 或设置warn级别,便于快速快速响应用户反馈
                log.warn("业务校验失败", e);
                return Result.error(DefaultErrorCodeEnums.ASSERT_EXCEPTION, msg);
            }else {
                 log.error("xxx", e);
                return Result.error(DefaultErrorCodeEnums.PARAM_ERROR, msg);
            }
        }
}

建议使用 org.springframework.util.Assert 或在此基础上扩展

执行顺序: 如果抛异常--> 全局异常捕获 --> 全局统一结果处理

上一篇下一篇

猜你喜欢

热点阅读