Spring Boot 中自定义异常处理

转载自:https://blog.csdn.net/u013360850/article/details/93101903

Spring Boot 中提供了默认的异常处理,但是对于应用来说,这些信息并不应该直接返回或者不够明确,需要结合自己的情况进行定制。

自定义处理异常有两种方式:

  • org.springframework.web.servlet.HandlerExceptionResolver#resolveException方法
  • org.springframework.web.bind.annotation.RestControllerAdvice或org.springframework.web.bind.annotation.ControllerAdvice和org.springframework.web.bind.annotation.ExceptionHandler注解来实现

当两种方式都实现时,HandlerExceptionResolver要先于ControllerAdvice执行

使用 HandlerExceptionResolver 处理异常

public class CustomExceptionHandlerResolver implements HandlerExceptionResolver {

    private static final ObjectMapper OBJECT_MAPPER;
    
    static {
        OBJECT_MAPPER = new ObjectMapper();
        OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        OBJECT_MAPPER.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    }
    
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
    
        String message = "服务器错误";
    
        if (o instanceof HandlerMethod) {
            if (e instanceof IllegalArgumentException) {
                message = "参数错误";
            } else if (e instanceof SecurityException) {
                message = "不允许访问";
            } else if (e instanceof NullPointerException) {
                message = "空指针异常";
            }
        } else if (e instanceof NoHandlerFoundException) {
            message = "未找到相应资源";
        } else if (e instanceof HttpMediaTypeNotSupportedException) {
            message = "请求类型不支持";
        }
    
        httpServletResponse.setCharacterEncoding(StandardCharsets.UTF_8.name());
        httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
    
        try {
            httpServletResponse.getWriter()
                               .write(
                                       OBJECT_MAPPER.writeValueAsString(
                                               CustomResponseContent.builder()
                                                                    .code(500)
                                                                    .status("fail")
                                                                    .message(message)
                                                                    .build()
                                       )
                               );
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    
        return new ModelAndView();
    }
}

还需要将该配置添加到应用中

@Configuration
public class CustomWebMvcConfigurer implements WebMvcConfigurer {

    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        resolvers.add(new CustomExceptionHandlerResolver());
    }
}

使用 RestControllerAdvice/ControllerAdvice 和 ExceptionHandler 处理异常

@RestControllerAdvice
@ControllerAdvice
public class CustomControllerExceptionResolver {

    @ExceptionHandler({NullPointerException.class})
    @ResponseBody
    public ResponseEntity<?> handlerNullPointerException(HttpServletRequest request, Throwable throwable) {
        CustomResponseContent responseContent = CustomResponseContent.builder()
                                                                     .code(500)
                                                                     .status("fail")
                                                                     .message("空指针异常")
                                                                     .build();
        return new ResponseEntity<>(responseContent, HttpStatus.INTERNAL_SERVER_ERROR);
    }
    
    @ExceptionHandler({IllegalArgumentException.class})
    @ResponseBody
    public ResponseEntity<?> handlerIllegalArgumentException(HttpServletRequest request, Throwable throwable) {
        CustomResponseContent responseContent = CustomResponseContent.builder()
                                                                     .code(400)
                                                                     .status("fail")
                                                                     .message("参数错误")
                                                                     .build();
        return new ResponseEntity<>(responseContent, HttpStatus.BAD_REQUEST);
    }
    
    @ExceptionHandler({MethodArgumentNotValidException.class})
    @ResponseBody
    public ResponseEntity<?> handlerMethodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) {
        String errorMessage = e.getBindingResult()
                               .getAllErrors()
                               .stream()
                               .map(f -> ((FieldError) f).getField() + ":" + f.getDefaultMessage())
                               .collect(Collectors.joining(";"));
    
        CustomResponseContent responseContent = CustomResponseContent.builder()
                                                                     .code(400)
                                                                     .status("fail")
                                                                     .message(errorMessage)
                                                                     .build();
        return new ResponseEntity<>(responseContent, HttpStatus.BAD_REQUEST);
    }
    
    @ExceptionHandler({Exception.class})
    @ResponseBody
    public ResponseEntity<?> handlerException(HttpServletRequest request, Throwable throwable) {
        CustomResponseContent responseContent = CustomResponseContent.builder()
                                                                     .code(500)
                                                                     .status("fail")
                                                                     .message(throwable.getMessage())
                                                                     .build();
        return new ResponseEntity<>(responseContent, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

403/404… 等错误

添加了上述的异常处理后,还有一部分异常无法处理,如404 错误,这是因为这些错误的异常处理并不经过异常处理器,而是被转发到 /error的路径下,默认由org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController处理,所以,继承org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController并添加相应实现即可自定义处理该异常返回 JSON 数据

@Controller
@RequestMapping("/error")
public class CustomNoHandlerExceptionResolver extends AbstractErrorController {

    public CustomNoHandlerExceptionResolver(ErrorAttributes errorAttributes) {
        super(errorAttributes);
    }
    
    @Override
    public String getErrorPath() {
        return null;
    }
    
    @RequestMapping
    @ResponseBody
    public ResponseEntity<?> error(HttpServletRequest request, HttpServletResponse response) {
        Optional<String> originRequestUri = Optional.ofNullable((String) request.getAttribute("javax.servlet.error.request_uri"));
        String reasonPhrase = HttpStatus.valueOf(response.getStatus()).getReasonPhrase();
    
        CustomResponseContent responseContent = CustomResponseContent.builder()
                                                                     .code(response.getStatus())
                                                                     .status("fail")
                                                                     .message(originRequestUri.orElse("/error") + ":" + reasonPhrase)
                                                                     .build();
    
        return new ResponseEntity<>(responseContent, HttpStatus.valueOf(response.getStatus()));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值