servlet过滤器无法被全局异常处理器捕获处理

在Servlet的Filter中使用自定义异常通常需要一些特别的处理,因为过滤器是在请求到达Servlet之前运行的,所以过滤器中抛出的异常不会被Spring的@ControllerAdvice全局异常处理器捕获。但是,你可以通过HandlerExceptionResolver来手动触发全局异常处理。

具体来说,你可以在过滤器中捕获异常,然后使用HandlerExceptionResolver的resolveException方法来处理这些异常。这样,你就可以将过滤器中的异常转发给全局异常处理器,从而实现统一的异常处理。

例如,你可以创建一个过滤器,如下所示:

java
@Component
public class MyFilter implements Filter {
@Autowired
private HandlerExceptionResolver handlerExceptionResolver;

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    try {
        chain.doFilter(request, response);
    } catch (Exception e) {
        handlerExceptionResolver.resolveException((HttpServletRequest) request, (HttpServletResponse) response, null, e);
    }
}

}
在这个过滤器中,如果doFilter方法抛出异常,它会被捕获并使用HandlerExceptionResolver来处理。这样,你就可以在全局异常处理器中定义如何处理这些异常了。

这种方法允许你在过滤器中使用自定义异常,并且可以利用Spring的异常处理机制来统一处理这些异常。这样,你就可以在过滤器中进行一些预处理工作,比如验证用户权限、检查请求参数等,如果发现问题就抛出自定义异常,然后在全局异常处理器中统一处理这些异常,返回给用户统一的错误响应。

下面是一个例子说明

  1. 定义自定义异常类 GoodsException
    首先,我们定义一个GoodsException类,用于商品业务中的异常处理。

java
public class GoodsException extends RuntimeException {
public GoodsException(String message) {
super(message);
}

public GoodsException(String message, Throwable cause) {
    super(message, cause);
}

}

  1. 创建全局异常处理器 GlobalExceptionHandler
    接下来,我们创建一个全局异常处理器,用于处理GoodsException以及其他异常。

java
@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(GoodsException.class)
@ResponseBody
public ResponseEntity<Object> handleGoodsException(GoodsException ex, WebRequest request) {
    // 可以根据异常类型定义不同的错误响应
    Map<String, Object> body = new HashMap<>();
    body.put("timestamp", LocalDateTime.now());
    body.put("status", HttpStatus.BAD_REQUEST.value());
    body.put("error", "Bad Request");
    body.put("message", ex.getMessage());
    body.put("path", request.getPathInfo());
    return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(Exception.class)
@ResponseBody
public ResponseEntity<Object> handleException(Exception ex, WebRequest request) {
    // 通用异常处理
    Map<String, Object> body = new HashMap<>();
    body.put("timestamp", LocalDateTime.now());
    body.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
    body.put("error", "Internal Server Error");
    body.put("message", ex.getMessage());
    body.put("path", request.getPathInfo());
    return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
}

}

  1. 创建过滤器 GoodsFilter
    然后,我们创建一个过滤器GoodsFilter,在过滤器中抛出GoodsException异常,并使用HandlerExceptionResolver来处理这个异常。

java
@Component
public class GoodsFilter implements Filter {

@Autowired
private HandlerExceptionResolver handlerExceptionResolver;

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;

    try {
        // 业务逻辑检查,如果发现问题则抛出GoodsException
        boolean isValid = checkGoods(httpRequest);
        if (!isValid) {
            throw new GoodsException("商品验证失败");
        }
        chain.doFilter(request, response);
    } catch (GoodsException ex) {
        handlerExceptionResolver.resolveException(httpRequest, httpResponse, null, ex);
    } catch (Exception ex) {
        handlerExceptionResolver.resolveException(httpRequest, httpResponse, null, ex);
    }
}

private boolean checkGoods(HttpServletRequest request) {
    // 这里添加检查商品的逻辑
    // 例如,检查请求参数中的goodsId是否有效
    String goodsId = request.getParameter("goodsId");
    return "123".equals(goodsId); // 假设商品ID为123时认为是有效的
}

}

  1. 配置过滤器
    最后,确保你的过滤器被Spring容器管理。如果你使用的是Spring Boot,可以通过@WebFilter注解自动注册过滤器,或者在配置类中使用FilterRegistrationBean来注册。

java
@Configuration
public class FilterConfig {

@Autowired
private GoodsFilter goodsFilter;

@Bean
public FilterRegistrationBean<GoodsFilter> goodsFilterRegistration() {
    FilterRegistrationBean<GoodsFilter> registration = new FilterRegistrationBean<>();
    registration.setFilter(goodsFilter);
    registration.addUrlPatterns("/goods/*");
    return registration;
}

}
这样,当请求匹配到/goods/*路径时,GoodsFilter过滤器会被调用。如果过滤器中的checkGoods方法发现商品验证失败,它会抛出GoodsException异常。这个异常会被HandlerExceptionResolver捕获,并由GlobalExceptionHandler中的handleGoodsException方法处理,最终返回一个统一的错误响应给客户端。

原理解析

在 Servlet 的 Filter 中抛出自定义异常 MyException 时,如果不调用 handlerExceptionResolver.resolveException(httpRequest, httpResponse, null, ex),异常处理逻辑不会自动触发,因为 Filter 和 Spring MVC 的异常处理机制是分开的。

细节解释
Filter 和 Spring MVC 的分离:

Filter 是在 Servlet 容器层面执行的,而 Spring MVC 的异常处理机制(如 HandlerExceptionResolver)是在 Spring MVC 框架层面执行的。

当异常在 Filter 中抛出时,Servlet 容器并不知道如何处理这个异常,因为它不属于 Spring MVC 的异常处理机制。

HandlerExceptionResolver 的作用:

HandlerExceptionResolver 是 Spring MVC 框架中的一个接口,用于处理控制器(Controller)中抛出的异常。

通过调用 handlerExceptionResolver.resolveException(httpRequest, httpResponse, null, ex),你可以手动将异常交给 Spring MVC 的异常处理机制来处理。

手动触发异常处理:

在 Filter 中捕获到异常后,调用 handlerExceptionResolver.resolveException(httpRequest, httpResponse, null, ex) 可以将异常传递给 Spring MVC 的异常处理机制。

这样,Spring MVC 就可以使用注册的 HandlerExceptionResolver 来处理异常,并返回自定义的 ExceptionResponse。


总结

关键就是HandlerExceptionResolver 这个接口,它有很多实现类,不同的实现类处理不同的异常处理实现

SimpleMappingExceptionResolver:通过配置异常类和视图名称的映射关系,将异常映射到特定的视图。

DefaultHandlerExceptionResolver:处理 Spring MVC 框架定义的标准异常,如 HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException 等。

ResponseStatusExceptionResolver:根据异常类上标注的 @ResponseStatus 注解来设置响应状态码。

ExceptionHandlerExceptionResolver:处理控制器中使用 @ExceptionHandler 注解定义的异常处理方法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容