如何优雅统一处理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的控制处理器的,但实际上真的还有吗?还有吗?有吗?
@ExceptionHandler or HandlerExceptionResolver?如何优雅处理全局异常?【享学Spring MVC】(中)
@ExceptionHandler or HandlerExceptionResolver?如何优雅处理全局异常?【享学Spring MVC】(上)
@ResponseStatus标注的方法执行,会修改响应头中的状态码;
Spring会把@ControllerAdvice的类内部使用@ExceptionHandler方法应用到所有的 @RequestMapping注解的方法上
ExceptionHandler注解方式
注:@ExceptionHandler标注的方法,方法签名灵活、多变。