在类上加个 @RestControllerAdvice注解,在方法加上 @ExceptionHandler注解,value给对应的异常类就行

注意如果是 @ControllerAdvice注解的话,返回的格式不是 json,会被 thymeleaf等解析跳转页面

代码例子:


/**
 * Service 异常处理
 *
 * @author oohmygosh
 * @since 2021-09-29
 */
@Slf4j
@RestControllerAdvice
public class ServiceExceptionHandler {
    /**
     * 是否在响应结果中展示验证错误提示信息
     */
    @Value("${spring.validation.message.enable:true}")
    private Boolean enableValidationMessage;

    /**
     * 验证异常处理 - 在 @RequestBody 上添加 @Validated 处触发
     *
     * @param request {@link HttpServletRequest}
     * @param e       {@link MethodArgumentNotValidException}
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public ApiResult<?> handleMethodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) {
        return ApiResult.failed(this.convertFiledErrors(e.getBindingResult().getFieldErrors()));
    }

    /**
     * 验证异常处理 - form参数(对象参数,没有加 @RequestBody)触发
     *
     * @param request {@link HttpServletRequest}
     * @param e       {@link BindException}
     */
    @ExceptionHandler({BindException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public ApiResult<?> handleBindException(HttpServletRequest request, BindException e) {
        return ApiResult.failed(this.convertFiledErrors(e.getBindingResult().getFieldErrors()));
    }

    /**
     * 验证异常处理 - @Validated加在 controller 类上,
     * 且在参数列表中直接指定constraints时触发
     */
    @ExceptionHandler({ConstraintViolationException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public ApiResult<?> handleConstraintViolationException(HttpServletRequest request, ConstraintViolationException ex) {
        return ApiResult.failed(this.convertConstraintViolations(ex));
    }

    /**
     * 转换FieldError列表为错误提示信息
     */
    private String convertFiledErrors(List<FieldError> fieldErrors) {
        return Optional.ofNullable(fieldErrors)
                .filter(fieldErrorsInner -> this.enableValidationMessage)
                .map(fieldErrorsInner -> fieldErrorsInner.stream()
                        .flatMap(fieldError -> Stream.of(fieldError.getField() + " " + fieldError.getDefaultMessage()))
                        .collect(Collectors.joining(", ")))
                .orElse(null);
    }

    /**
     * 转换ConstraintViolationException 异常为错误提示信息
     */
    private String convertConstraintViolations(ConstraintViolationException constraintViolationException) {
        return Optional.ofNullable(constraintViolationException.getConstraintViolations())
                .filter(constraintViolations -> this.enableValidationMessage)
                .map(constraintViolations -> constraintViolations.stream().flatMap(constraintViolation -> {
                            String path = constraintViolation.getPropertyPath().toString();
                            String errorMessage = path.substring(path.lastIndexOf(".") + 1) +
                                    " " + constraintViolation.getMessage();
                            return Stream.of(errorMessage);
                        }).collect(Collectors.joining(", "))
                ).orElse(null);
    }

    /**
     * 自定义 REST 业务异常
     *
     * @param e    异常类型
     * @param resp 响应请求
     */
    @ExceptionHandler(value = Throwable.class)
    public ApiResult<Object> handleBadRequest(Throwable e, HttpServletResponse resp) {
        /*
         * 业务逻辑异常
         */
        if (e instanceof ApiException) {
            IErrorCode errorCode = ((ApiException) e).getErrorCode();
            if (null != errorCode) {
                return ApiResult.failed(errorCode);
            }
            return ApiResult.failed(e.getMessage());
        }

        if (e instanceof AccessDeniedException) {
            return ApiResult.failed(e.getMessage());
        }

        // 参数缺失
        if (e instanceof ServletException) {
            return ApiResult.failed(e.getMessage());
        }

        // 请求参数无法读取
        if (e instanceof HttpMessageNotReadableException) {
            return ApiResult.failed(e.getMessage());
        }

        /*
          系统内部异常,打印异常栈
         */
        log.error("Error: handleBadRequest StackTrace : {}", ThrowableUtils.getStackTrace(e));
        if (e instanceof MethodArgumentTypeMismatchException) {
            resp.setStatus(301);
            return ApiResult.failed("请求参数错误");
        }
        if (e instanceof IllegalArgumentException) {
            return ApiResult.failed(e.getMessage());
        }
        return ApiResult.failed("Internal Server Error");
    }
}

文章作者: oohmygosh
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Blog
Java SpringBoot
喜欢就支持一下吧