如何优雅统一处理Filter异常
因为我们无法通过@ControllerAdvice+@ExceptionHandler的方式去处理Filter过滤器抛出的异常(理由希望读者自己能明白),所以此处我提供较为优雅的处理方式作为参考。
传统Spring MVC
指导思想步骤:
- catch住Filter所有异常
- 把Exception放进请求attr属性里
- 把该请求forward转发到专门处理错误的Controller里
- 该Controller里拿出异常throw出去,从而便可交给全局异常统一处理了
附参考代码:
Filter:
@Component("helloFilter") @WebFilter(urlPatterns = "/*") public class HelloFilter extends OncePerRequestFilter { @Override protected void initFilterBean() throws ServletException { System.out.println("HelloFilter初始化..."); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { System.out.println(1 / 0); filterChain.doFilter(request, response); } catch (Exception e) { // 捕获所有异常做转发用 request.setAttribute(ErrorController.EXCEPTION_ATTR, e); request.getRequestDispatcher(ErrorController.ERROR_URL).forward(request, response); } } }
ErrorController:
@Slf4j @RestController public class ErrorController { public static final String ERROR_URL = "/do/filter/errors"; public static final String EXCEPTION_ATTR = ErrorController.class.getName() + ".error"; /** * 把Filter里的异常同意交给全局异常处理 */ @GetMapping(value = "/do/filter/errors") public void doFilterErrors(HttpServletRequest request) throws Exception { throw Exception.class.cast(request.getAttribute(EXCEPTION_ATTR)); } }
GlobalExceptionHandler:全局异常处理
@Slf4j @RestControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { // 处理所有不可知的异常,作为全局的兜底 @ExceptionHandler(Exception.class) Object handleException(Exception e) { log.error(e.getMessage(), e); return "hello error"; } }
Spring Boot
本文针对性的特别提出了SpringBoot case下的解决方案。因为SpringBoot
它会把所有的异常情况都转换为请求/error
,所以扩展它还是容易些的:
Filter:没必要自己catch了,交给SpringBoot全局处理即可
@Component("helloFilter") @WebFilter(urlPatterns = "/*") public class HelloFilter extends OncePerRequestFilter { @Override protected void initFilterBean() throws ServletException { System.out.println("HelloFilter初始化..."); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { System.out.println(1 / 0); filterChain.doFilter(request, response); } }
@RestController public class MyErrorController extends BasicErrorController { // 最终使用的是此构造函数,所以魔方着只需要使用它即可 // return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers); public MyErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties) { super(errorAttributes, serverProperties.getError()); } @RequestMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<Map<String, Object>> errorJson(HttpServletRequest request) { HttpStatus status = getStatus(request); if (status == HttpStatus.NO_CONTENT) { return new ResponseEntity<>(status); } Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); body.put("myErrorType", "this is my diy error"); return new ResponseEntity<>(body, status); } }
相关阅读:
web九大组件之—HandlerExceptionResolver异常处理器使用详解【享学Spring MVC】
总结
本文呼吁,在实际生产中,请务必重视对异常的处理,我想表达的包含两层含义:
1.什么时候该抛异常,什么情况下它不是异常。(异常会使得JVM停顿,所以异常的使用请不要泛滥)
2.对于异常的统一处理,请务必要分而治之。不是所有异常都叫Exception~
1. 合理的处理异常,这对于微服务架构在服务治理层面具有重要的意义,这也是对一个优秀架构师的考验之一
本文推荐多使用@ExceptionHandler方式去处理异常,因为它不仅书写方便、容易管理,而且还有缓存,效率也稍高一些。
Tips:@ExceptionHandler仅能处理HandlerMethod方式的异常。理论上是还可以有非HandlerMethod的控制处理器的,但实际上真的还有吗?还有吗?有吗?