filter中forward()失效因响应已提交,需先检查response.iscommitted();若已提交则重定向,未提交方可forward并手动设状态码和异常属性。

Filter里捕获异常后request.getRequestDispatcher().forward()失效
因为forward()要求响应未提交,而异常发生时往往已输出部分HTML或触发了HTTP状态码变更。一旦response.isCommitted()返回true,再调用forward()会抛出IllegalStateException: Cannot forward after response has been committed。
- 务必在
doFilter()中用try-catch包裹chain.doFilter(request, response),而不是放在filter方法末尾 - 捕获异常后立即检查
response.isCommitted(),为true时只能重定向:response.sendRedirect("/error.html") - 不要在
catch块里再调用chain.doFilter()或写入响应体,避免二次提交
统一错误页无法获取原始异常信息
直接forward()到/error.jsp时,request.getAttribute("javax.servlet.error.exception")只有在Servlet容器原生错误分发机制下才有效;Filter手动转发不会自动注入该属性。
- 手动把异常存进request:
request.setAttribute("error", e),再forward() - 若用Spring MVC,别混用Filter和
@ControllerAdvice——后者通过DispatcherServlet拦截,Filter更前置,两者生命周期不重叠 - JSP中取值写
${requestScope.error.message},别依赖${exception.message}(那是容器错误页专用)
HTTP状态码没同步到错误页
用户看到500页面但浏览器地址栏仍是原URL,且响应头里Status: 200 OK,说明你忘了设置状态码。Filter转发不改变已发出的状态,必须显式设置。
- 在
forward()前加response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR) - 如果用了
sendRedirect(),状态码会自动变成302,此时原始错误码丢失——除非你在重定向URL里带参数:sendRedirect("/error.html?code=500&msg=" + URLEncoder.encode(e.getMessage(), "UTF-8")) - 静态错误页(如
error.html)无法读取Java异常对象,适合做兜底;动态页(error.jsp或error.ftl)才能渲染堆栈
多个Filter嵌套时异常被吞掉
比如A Filter捕获异常并forward,但B Filter在它之前已调用response.getWriter().write(...),导致响应提前提交,A的forward失败,最终用户只看到空白页或500。
立即学习“Java免费学习笔记(深入)”;
- 所有Filter都应遵循“先判提交,再操作”原则,尤其涉及输出流、writer、cookie、header修改的操作
- 日志里加一句
log.warn("Response already committed before error handling", e),比静默失败更容易定位 - 开发期用
curl -v看响应头中的Content-Length和Status,比浏览器F12更早发现问题










