SpringBoot全局异常处理捕获Filter内部异常

通常在项目中都会使用到全局异常处理,但是如果添加有拦截器,对拦截器中的异常进行捕获的时候,就会发现全局异常处理失效,无法对拦截器的异常进行捕获。

全局异常不能捕获拦截器异常的原因

SpringBoot下全局异常处理的几种方式:

  1. BasicExceptionController——SpringBoot默认处理异常方式,用于异常跳转到/error,可实现自定义错误页面请求。
  2. @ExceptionHandle注解——只能在控制器中定义异常处理方法。
  3. @ControllerAdvice+@ExceptionHandler——增强控制前Controller实现异常拦截。
  4. SimpleMappingExceptionResolver——拦截异常跳转到error页面。
  5. HandlerExceptionResolver——实现HandlerExceptionResolver拦截异常。

上面几种方式只能拦截到控制层的异常,而Filter在Controller之前,Controller层的异常捕获,是无法捕获到还没有请求到Controller时发生的异常的。

实现对Filter异常的捕获

从上面的几种异常处理方式可以发现,如果要捕获Filter异常,只能通过控制器层定义的全局异常处理来捕获;那么也就只能想办法让Filter中的异常发送到Controller,再由Controller抛出异常,最后由全局异常捕获。
有了上面的思路,第一个要解决的问题就是怎么让过滤器中的异常在Filter中被捕获到再发送出去。

Filter的实现方式是责任链,第一个Filter处理之后,调用第二个Filter,依次往后,直到Filter全部处理完成后结束;当某一个Filter处理中断,则依次返回结果经过前一个Filter,直到经过第一个Filter过滤后结束过滤责任链。

根据Filter过滤的特点,只需要在业务过滤器之前加上用于处理其他过滤器异常捕获的Filter就可以实现对过滤器异常的处理。

1 简单实现异常过滤Filter代码如下:

@Slf4j
@Component
public class ExceptionFilter implements Filter {
   
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        try {
            chain.doFilter(request, response);
        } catch (Exception e) {
            // 异常捕获,发送到error controller
            request.setAttribute("filter.error", e);
            //将异常分发到/error/exthrow控制器
            request.getRequestDispatcher("/error/exthrow").forward(request, response);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void destroy() {

    }
}

2 注册过滤器时,ExceptionFilter排序要再其他过滤器之前

 @Bean
    public FilterRegistrationBean exceptionFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(exceptionFilter);
        registration.setName("exceptionFilter");
        //此处尽量小,要比其他Filter靠前
        registration.setOrder(-1);
        return registration;
    }

3 实现Controller接收过过滤器发来的异常

@RestController
public class ErrorController {
    /**
     * 重新抛出异常
     */
    @RequestMapping("/error/exthrow")
    public void rethrow(HttpServletRequest request) {
        throw ((Exception) request.getAttribute("filter.error"));
    }
}

以上只是Demo示例,再实际使用中,拦截Filter尽量拦截具体的Exception。

你可能感兴趣的:(日常记录)