SpringBoot全局异常处理(最佳实践) |
您所在的位置:网站首页 › getoutputstream异常处理 › SpringBoot全局异常处理(最佳实践) |
目录
异常处理方案分类 基于请求转发 基于异常处理器 基于过滤器 常见异常处理实现方案 BasicExceptionController @ExceptionHandler @ControllerAdvice+@ExceptionHandler SimpleMappingExceptionResolver HandlerExceptionResolver Filter 全局异常处理实现方案 请求转发 异常处理器+请求转发补充 过滤器 异常处理器+过滤器补充 注意事项 方案推荐 异常处理方案分类异常处理主要分为三类: 基于请求转发的方式处理异常; 基于异常处理器的方式处理异常; 基于过滤器的方式处理异常。 基于请求转发基于请求转发的异常处理方式是真正的全局异常处理。 实现方式有: BasicExceptionController 基于异常处理器基于异常处理器的异常处理方式其实并不是真正的全局异常处理,因为它处理不了过滤器等抛出的异常。 实现方式有: @ExceptionHandler @ControllerAdvice+@ExceptionHandler SimpleMappingExceptionResolver HandlerExceptionResolver 基于过滤器基于过滤器的异常处理方式近似与全局异常处理。它能处理过滤器及之后的环节抛出的异常。 实现方式有: Filter 常见异常处理实现方案 1. BasicExceptionController这是SpringBoot默认处理异常方式:一旦程序中出现了异常SpringBoot就会请求/error的url,在SpringBoot中提供了一个叫BasicExceptionController的类来处理/error请求,然后跳转到默认显示异常的页面来展示异常信息。显示异常的页面也可以自定义,在目录src/main/resources/templates/下定义一个叫error的文件,可以是jsp也可以是html 。 此种方式是通过请求转发实现的,出现异常时,会转发到请求到/error,该接口对异常进行处理返回。是最符合全局异常处理的。 可以自定义Controller继承BasicErrorController异常处理来实现异常处理的自定义。 @Slf4j @RestController public class MyErrorController extends BasicErrorController { public MyErrorController() { super(new DefaultErrorAttributes(), new ErrorProperties()); } /** * produces 设置返回的数据类型:application/json * @param request 请求 * @return 自定义的返回实体类 */ @Override @RequestMapping(value = "", produces = {MediaType.APPLICATION_JSON_VALUE}) public ResponseEntity error(HttpServletRequest request) { // 获取错误信息 Map body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); HttpStatus status = getStatus(request); String code = body.get("status").toString(); String message = body.get("message").toString(); return new ResponseEntity(ApiUtil.fail(message), HttpStatus.OK); } }需要注意 1.该种方式能获取到的信息时有限的。一般情况只能获取到下面这几个参数(特殊情况会有补充参数)。 ![]() 现在一般项目需要的响应信息都是自定义统一格式的JSON(code、msg、data)。对于自定义业务错误码code不好得到,对于错误信息msg有时得到的也不一定是你所想要的(简单说就是一些特殊的异常描述信息不好得到)。 比如:自定义的参数校验信息 @NotNull(message = "主键不能为空")![]() message参数取到的并不是“主键不能为空”。 2.当出现抛出两次异常,第一次被异常处理器处理,第二次异常转由BasicExceptionController处理。但能取到的异常信息可能是一次的,具体原因下面有分析。 2. @ExceptionHandler该种方式只能作用于使用@ExceptionHandler注解的Controller的异常,对于其他Controller的异常就无能为力了,所以并不不推荐使用。 此种方式是通过异常处理器实现的,使用HandlerExceptionResolverComposite异常处理器中的ExceptionHandlerExceptionResolver异常处理器处理的。 @RestController public class TestController { @GetMapping("test9") public FundInfo test9() throws Exception { throw new Exception("test9 error"); } @GetMapping("test10") public FundInfo test10() throws Exception { throw new IOException("test10 error"); } @ExceptionHandler(Exception.class) public ApiResult exceptionHandler(Exception e) { return ApiUtil.custom(500, e.getMessage()); } } ❝注意:如果既在具体Controller使用了@ExceptionHandler,也定义了全局异常处理器类(@ControllerAdvice+@ExceptionHandler),优先使用Controller定义的@ExceptionHandler处理。如果处理不了,才会使用全局异常处理器处理。 ❞ 3. @ControllerAdvice+@ExceptionHandler使用 @ControllerAdvice+@ExceptionHandler注解能够进行近似全局异常处理,这种方式推荐使用。 一般说它只能处理控制器中抛出的异常,这种说法并不准确,其实它能处理DispatcherServlet.doDispatch方法中DispatcherServlet.processDispatchResult方法之前捕捉到的所有异常,包括:拦截器、参数绑定(参数解析、参数转换、参数校验)、控制器、返回值处理等模块抛出的异常。 此种方式是通过异常处理器实现的,使用HandlerExceptionResolverComposite异常处理器中的ExceptionHandlerExceptionResolver异常处理器处理的。 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ......省略代码...... try { ModelAndView mv = null; Exception dispatchException = null; try { ......省略代码...... mappedHandler = getHandler(processedRequest); ......省略代码...... HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); ......省略代码...... if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } ......省略代码...... mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } ......省略代码...... mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { ......省略代码...... } catch (Throwable err) { ......省略代码...... } finally { ......省略代码...... } }使用方式 定义一个类,使用@ControllerAdvice注解该类,使用@ExceptionHandler注解方法。@RestControllerAdvice注解是@ControllerAdvice注解的扩展(@RestControllerAdvice=@ControllerAdvice+@ResponseBody),返回值自动为JSON的形式。 /** * 全局异常处理器 */ @Slf4j @SuppressWarnings("ALL") @RestControllerAdvice public class MyGlobalExceptionHandler { @ExceptionHandler(BindException.class) @ResponseStatus(HttpStatus.OK) public ApiResult bindException(HttpServletRequest request, HttpServletResponse response, BindException exception) { return ApiUtil.fail(exception.getBindingResult().getFieldError().getDefaultMessage()); } @ExceptionHandler(org.springframework.web.bind.MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.OK) public ApiResult methodArgumentNotValidException(HttpServletRequest request, HttpServletResponse response, MethodArgumentNotValidException exception) { return ApiUtil.fail(exception.getBindingResult().getFieldError().getDefaultMessage()); } @ExceptionHandler(MissingServletRequestParameterException.class) @ResponseStatus(HttpStatus.OK) public ApiResult methodArgumentNotValidException(HttpServletRequest request, HttpServletResponse response, MissingServletRequestParameterException exception) { return ApiUtil.fail(exception.getMessage()); } @ExceptionHandler(ConstraintViolationException.class) @ResponseStatus(HttpStatus.OK) public ApiResult methodArgumentNotValidException(HttpServletRequest request, HttpServletResponse response, ConstraintViolationException exception) { System.out.println(exception.getLocalizedMessage()); Iterator |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |