如何在Servlet Filter中根据应用返回状态码添加自定义响应HTTP头
问题原因
当chain.doFilter(request, response)执行完成后,响应可能已经被提交(比如后端控制器已经输出响应内容并刷新了流),此时再调用setHeader修改响应头就不会生效——HTTP协议规定响应头必须在响应体之前发送,响应提交后无法再修改头信息。
解决方案
通过HttpServletResponseWrapper包装原始响应,提前捕获响应状态码,并确保在响应真正提交前设置自定义头。
1. 自定义响应包装类
这个类用于捕获并保存响应的状态码,同时委托所有操作给原始响应:
public class StatusCapturingResponseWrapper extends HttpServletResponseWrapper { private int statusCode = HttpServletResponse.SC_OK; public StatusCapturingResponseWrapper(HttpServletResponse response) { super(response); } @Override public void setStatus(int sc) { super.setStatus(sc); this.statusCode = sc; } @Override public void sendError(int sc) throws IOException { this.statusCode = sc; super.sendError(sc); } @Override public void sendError(int sc, String msg) throws IOException { this.statusCode = sc; super.sendError(sc, msg); } @Override public void setStatus(int sc, String sm) { super.setStatus(sc, sm); this.statusCode = sc; } public int getStatusCode() { return statusCode; } }
2. 修改MDCFilter逻辑
使用包装类替代原始响应传递给过滤器链,在链执行完成后通过包装类获取状态码,再设置自定义头:
public class MDCFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String trackingId = UUID.randomUUID().toString(); MDC.put("trackingId", trackingId); StatusCapturingResponseWrapper wrappedResponse = new StatusCapturingResponseWrapper((HttpServletResponse) response); try { chain.doFilter(request, wrappedResponse); } catch (Throwable throwable) { // 捕获异常时手动设置500状态码,确保头能被正确添加 wrappedResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); // 若需要向上抛出异常,可取消下面注释 // throw new ServletException(throwable); } finally { // 根据捕获到的状态码判断是否添加头 if (wrappedResponse.getStatusCode() >= 300) { wrappedResponse.setHeader("x-tracking-id", trackingId); } MDC.remove("trackingId"); } } }
关键说明
- 包装类会拦截所有修改状态码的操作,确保我们能获取到最终的响应状态。
- 由于包装类的
setHeader直接委托给原始响应,此时响应尚未提交(除非过滤器链中主动提交),所以头信息能被正常设置并发送给客户端。
内容的提问来源于stack exchange,提问作者mhrsalehi
相关产品推荐
相关产品推荐

