如何正确序列化Spring Filter中抛出的Zalando Problem异常
问题根因
- 你当前配置的
@RestControllerAdvice属于Spring MVC组件,仅在DispatcherServlet处理请求的生命周期内生效,只能拦截Controller、MVC拦截器层抛出的异常。 - Servlet Filter的执行时机早于
DispatcherServlet,这一阶段抛出的异常不会进入MVC异常处理链路,会直接抛给Servlet容器走默认错误逻辑,因此Problem不会被正确序列化,状态码也会变成默认的500。
可行解决方案
二选一即可:
方案1:新增前置全局异常捕获Filter(推荐)
新增一个优先级最高的Filter,包裹整个后续Filter链的执行,统一捕获所有下游Filter抛出的Problem异常,用Problem库自带的处理器写入响应,不需要修改现有业务Filter代码。
代码示例:
@Component @Order(Ordered.HIGHEST_PRECEDENCE) // 设为最高优先级,保证最先进入、最后退出 class GlobalProblemFilter : OncePerRequestFilter() { @Autowired private lateinit var problemProcessor: ProblemHttpServletResponseProcessor override fun doFilterInternal( request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain ) { try { filterChain.doFilter(request, response) } catch (problem: Problem) { problemProcessor.process(response, problem) } } }
注意:如果项目集成了Spring Security,需要确认该Filter的执行顺序在Security Filter链之前,否则Security链路抛出的Problem无法被捕获。
方案2:在业务Filter内部自行处理异常
如果不想新增全局Filter,可以直接在你的UserVerifiedFilter中加try-catch逻辑,捕获到Problem后直接写入响应:
修改后的代码示例:
@Component class UserVerifiedFilter : OncePerRequestFilter() { @Autowired private lateinit var problemProcessor: ProblemHttpServletResponseProcessor @Throws(ServletException::class, IOException::class) override fun doFilterInternal( request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain ) { try { // 原有业务校验逻辑 throw Problem.builder() .withType(URI.create("https://example.org/email-unverified")) .withTitle("Email unverified") .withStatus(Status.BAD_REQUEST) .withDetail("Email needs to verified to use this endpoint") .build() } catch (problem: Problem) { problemProcessor.process(response, problem) return } filterChain.doFilter(request, response) } }
说明:ProblemHttpServletResponseProcessor是problem-spring-web库提供的内置组件,只要正确引入依赖即可自动注入,无需手动实例化。
内容的提问来源于stack exchange,提问作者edwardmp
相关产品推荐
相关产品推荐

