如何优雅统一处理Filter异常


因为我们无法通过@ControllerAdvice+@ExceptionHandler的方式去处理Filter过滤器抛出的异常(理由希望读者自己能明白),所以此处我提供较为优雅的处理方式作为参考。

传统Spring MVC


指导思想步骤:


  1. catch住Filter所有异常
  2. 把Exception放进请求attr属性里
  3. 把该请求forward转发到专门处理错误的Controller里
  4. 该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);


image.png


相关阅读:

web九大组件之—HandlerExceptionResolver异常处理器使用详解【享学Spring MVC】


总结


本文呼吁,在实际生产中,请务必重视对异常的处理,我想表达的包含两层含义:


1.什么时候该抛异常,什么情况下它不是异常。(异常会使得JVM停顿,所以异常的使用请不要泛滥)


2.对于异常的统一处理,请务必要分而治之。不是所有异常都叫Exception~

1. 合理的处理异常,这对于微服务架构在服务治理层面具有重要的意义,这也是对一个优秀架构师的考验之一


本文推荐多使用@ExceptionHandler方式去处理异常,因为它不仅书写方便、容易管理,而且还有缓存,效率也稍高一些。

Tips:@ExceptionHandler仅能处理HandlerMethod方式的异常。理论上是还可以有非HandlerMethod的控制处理器的,但实际上真的还有吗?还有吗?有吗?


@ExceptionHandler or HandlerExceptionResolver?如何优雅处理全局异常?【享学Spring MVC】(中)
@ExceptionHandler or HandlerExceptionResolver?如何优雅处理全局异常?【享学Spring MVC】(中)
@ExceptionHandler or HandlerExceptionResolver?如何优雅处理全局异常?【享学Spring MVC】(上)
@ExceptionHandler or HandlerExceptionResolver?如何优雅处理全局异常?【享学Spring MVC】(上)
web九大组件之---HandlerExceptionResolver异常处理器使用详解【享学Spring MVC】(下)
web九大组件之---HandlerExceptionResolver异常处理器使用详解【享学Spring MVC】(下)
web九大组件之---HandlerExceptionResolver异常处理器使用详解【享学Spring MVC】(中)
web九大组件之---HandlerExceptionResolver异常处理器使用详解【享学Spring MVC】(中)
web九大组件之---HandlerExceptionResolver异常处理器使用详解【享学Spring MVC】(上)
web九大组件之---HandlerExceptionResolver异常处理器使用详解【享学Spring MVC】(上)
@ResponseStatus标注的方法执行,会修改响应头中的状态码; Spring会把@ControllerAdvice的类内部使用@ExceptionHandler方法应用到所有的 @RequestMapping注解的方法上 ExceptionHandler注解方式 注:@ExceptionHandler标注的方法,方法签名灵活、多变。