You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@ControllerAdvice未处理GenericFilterBean过滤器异常如何自定义响应

过滤器自定义异常响应失效的原因及修复方案

核心根因

  • @ControllerAdvice注解的全局异常处理器,生效范围仅在DispatcherServlet的处理链路内,也就是请求进入Spring MVC的调度逻辑之后,Controller方法执行前后抛出的异常才会被捕获。而Filter的执行时机远早于DispatcherServlet,处于Servlet容器层面,Filter内抛出的异常根本不会流转到全局异常处理器的捕获链路里。
  • 你在catch块里写的response.sendError()也没有达到预期效果:这个方法被调用后,Servlet容器不会直接把传入的错误信息返回给前端,而是会触发内置的错误跳转逻辑,将请求内部转发到Spring Boot默认的/error端点,最终由内置的BasicErrorController生成默认结构的错误响应,自然看不到你自定义的错误信息。

注:示例代码里的未定义变量man属于编译期错误,实际场景下这行一般是会触发运行时异常的业务逻辑(比如空指针调用、非法参数访问等),异常会正常进入catch分支,不会阻断应用启动。

可落地的自定义响应实现方案

方案1:绕开容器错误跳转,手动写入自定义响应

完全不使用sendError方法,直接通过Response的输出流构建标准化的自定义响应,彻底跳过容器的错误转发流程。
示例代码:

catch (Exception e) {
    // 统一响应结构和全局异常处理器返回的格式保持一致
    ExceptionResponse errorBody = new ExceptionResponse(
        HttpServletResponse.SC_UNAUTHORIZED,
        false,
        "Invalid token."
    );

    // 手动设置响应基础属性
    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    response.setContentType(MediaType.APPLICATION_JSON_VALUE);
    response.setCharacterEncoding(StandardCharsets.UTF_8.name());

    // 从Spring容器中获取注入的ObjectMapper,序列化响应体
    objectMapper.writeValue(response.getWriter(), errorBody);
    return;
}

这种方式的可控性最高,不会触发任何额外的请求转发逻辑,前端拿到的响应结构完全由你定义,也能和全局异常处理器的响应格式保持统一。

方案2:复用Spring内置错误处理链路

如果不想在Filter里重复写JSON序列化逻辑,可以把捕获到的异常存入Request域,手动转发到/error端点,再通过自定义ErrorAttributes类调整/error端点的返回结构,实现和Controller层异常响应的格式统一。
示例代码:

catch (Exception e) {
    // 将异常和状态码存入request属性,供/error端点读取
    httpServletRequest.setAttribute(RequestDispatcher.ERROR_EXCEPTION, e);
    httpServletRequest.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, HttpServletResponse.SC_UNAUTHORIZED);
    httpServletRequest.setAttribute(RequestDispatcher.ERROR_MESSAGE, "Invalid token.");

    // 内部转发到/error路径,由Spring的错误处理逻辑统一生成响应
    httpServletRequest.getRequestDispatcher("/error").forward(httpServletRequest, response);
    return;
}

方案3:接入Spring Security的异常处理体系

如果项目已经集成了Spring Security,不需要在Filter里手动写响应逻辑,直接在Security配置中自定义AuthenticationEntryPoint,统一处理过滤器链路内的异常响应,实现全链路的响应格式统一。

常见踩坑提醒

  • 不要期待@ControllerAdvice能捕获Filter内的异常,两者的执行链路完全独立:Filter → Servlet容器 → DispatcherServlet → 拦截器 → Controller → 全局异常处理器,异常如果在Filter阶段抛出,根本不会走到后面的异常捕获节点。
  • 谨慎使用response.sendError(),这个方法会触发容器的默认错误跳转,你传入的错误文本默认不会直接返回给前端,必须配合自定义错误配置才能生效,非常容易写出看起来生效实际无效的代码。
  • 项目如果集成了权限框架,记得把/error路径加入放行列表,不然转发到/error时会被权限过滤器拦截,返回非预期的403错误。

内容的提问来源于stack exchange,提问作者DD DD

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.22 23:45:09