springboot 处理 filter 中抛出的异常

前言

springboot web项目开发中,全局异常处理是一个必不可少的组件,而且springboot 本身已经对此提供了很好的支持,我们只需要一个 @RestControllerAdvice 配合 一个 @ExceptionHandler 就可以很好的实现全局异常的拦截处理了。

经过

今天接到一个需求,需要对用户进行过滤,满足要求的才放行。听到这个需求,第一反应是这不是一个过滤器就搞定的事嘛,so easy! 十分钟不到,代码就出来了。

@Component public class UserTokenFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { if (tokenCheck()) { throw new UserException("用户信息获取异常");
        }
        filterChain.doFilter(request, response);
    }
}

主逻辑就是这样,校验用户的 token ,满足条件的放行,不满足条件的直接抛出异常(因为原来的代码里已经有了针对 UserException的全局异常处理)。

@Bean public FilterRegistrationBean registrationBean() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        UserTokenFilter filter = new UserTokenFilter();
        registrationBean.setFilter(filter);
        registrationBean.setName("userTokenFilter");
        registrationBean.addUrlPatterns("/*"); return registrationBean;
    }

注册过滤器,这不就搞定了嘛,难道今天又是划水摸鱼的一天?
上代码,开测!
正常请求,完美通过,50%已完成,



就在我幻想着今天接下来的时间该怎么划水的时候,现实狠狠滴给了我一巴掌!



这时,postman 返回来一个

{ "timestamp": "2022-05-05T03:01:47.438+00:00", "status": 500, "error": "Internal Server Error", "path": "/user/list" }

呃…这怎么和我设想的不一样呀,我不是有全局异常处理器么?看后台日志确实是过滤器抛出了异常,也是我们自定义的UserException,这一切都和我预想的一样,可是为什么全局异常处理器没有生效!!!

分析

其实冷静下来想一想,一个请求过来,首先要经过拦截器(如果有),过滤器(如果有),只有前面都放行了之后,请求才会真正到达处理它的 controller,而上面的例子,我们在 过滤器中抛出了异常,抛出异常以后程序不会继续向下执行,也就是请求并没有到达 controller 层。而 全局异常拦截的注解 @ControllerAdvice 从字样上就可以看出,它带有 controller,猜想是不是它只针对 controller 层出现的异常才会处理。于是翻到源码,看到这样一句话 @ControllerAdvice By default, the methods in an @ControllerAdvice apply globally to all controllers. Use selectors such as annotations, basePackageClasses, and basePackages (or its alias value) to define a more narrow subset of targeted controllers. 。回头再想想也正常,请求在过滤器就抛出了异常,没有到达 controller 层,controller 压根不知道这件事,因此没有处理,这不也是正常的么?大概原因我们知道了,那么有没有什么方法可以解决呢?
当然你可以直接在过滤器抛出异常的地方写回错误的结果,但是既然有了全局异常处理器,我们有没有什么办法可以利用它对过滤器抛出的异常也统一处理呢?

解决方法

带着这个想法,我再往上找了找解决方法,没想到很容易就找到了。解决的思路也很见到,既然 controllerAdvice 是拦截 controller 层的异常,那么有没有什么方法可以把这个异常传递到 controller 层呢?
方法是有的,我们可以在定义一个过滤器,起作用就是专门捕获我们过滤器抛出的异常,如果捕获到异常,那么我们就把这个异常传递到指定的 controller 来处理,并把异常也传递过去,而这个controller 只做一件事,就是有请求的时候拿到传递过来的异常,然后再把异常抛出去,这样全局异常处理器不就可以感知到这个异常了嘛?上代码
定义过滤器:

public class ExceptionFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { try {
            filterChain.doFilter(request, response);
        } catch (UserException e) {
            e.printStackTrace(); // 传递异常信息 request.setAttribute("filterError", e); // 指定处理该请求的处理器 request.getRequestDispatcher(CommonConstant.ERROR_CONTROLLER_PATH).forward(request, response);
        }
    }
}

定义异常处理controller:

@RestController public class ExceptionController { @RequestMapping(CommonConstant.ERROR_CONTROLLER_PATH) public void handleException(HttpServletRequest request){ throw (UserException) request.getAttribute("filterError");
    }
} public class CommonConstant { /**
     * 异常处理 controller request url
     */ public static final String ERROR_CONTROLLER_PATH = "/error/throw";
}

注册过滤器:

@Bean public FilterRegistrationBean exceptionFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        ExceptionFilter filter = new ExceptionFilter();
        registrationBean.setFilter(filter);
        registrationBean.setName("exceptionFilter");
        registrationBean.setOrder(-1); return registrationBean;
    }

开测,很幸运的我们拿到了预想的结果

{ "code": 500, "msg": "用户信息获取异常", "data": null }

附上全局异常处理器的相关代码

@RestControllerAdvice public class CustomExceptionHandler { @ExceptionHandler(UserException.class) public ResultCode HandleException(UserException e) { return ResultCode.fail(e.getCode(), e.getMsg());
    } // 针对其他异常的处理 }
#Java##Spring##程序员#
全部评论
整理的非常详细,明白了
点赞 回复 分享
发布于 2022-09-14 20:40 陕西

相关推荐

05-07 13:29
已编辑
门头沟学院 Java
北斗导航Compass低仿版:能不能先搞清楚优先级啊,怎么可能是项目问题,项目很重要吗?又没学历 又没实习大厂凭啥约面?那玩具项目 没应用在真实生产环境下的 就算做上天又有什么用?早点找个小公司实习 拿小公司实习去投大厂实习,这才是你现在该做的
投递美团等公司10个岗位 简历被挂麻了,求建议
点赞 评论 收藏
分享
牛客583549203号:腾讯还好,况且实习而已,实习生流动性很大,属于正常现象,记得和HR委婉解释
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务