Spring Boot移除Content-Length Header失败,求有效解决方案
在Spring Boot 2.2.3 RELEASE版本应用中,未显式添加Content-Length请求头,但每个响应中自动包含该头部。现在需要为响应添加Transfer-Encoding头部,添加后通过Postman测试API时出现如下错误:
Error: Parse Error: Content-Length can't be present with Transfer-Encoding
该错误符合IETF HTTP 1.1 RFC7230文档规范。由于响应不需要Content-Length头部,我使用@WebFilter注解并设置@Order为Integer.MAX_VALUE(确保最后执行)编写了过滤器,代码如下:
@WebFilter(urlPatterns = "/*", asyncSupported = true) @Order(Integer.MAX_VALUE) public class ContentLengthHeaderFilter implements Filter { private static final Logger LOGGER = LoggerFactory.getLogger(ContentLengthHeaderFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { LOGGER.info("filter init called !!!"); Filter.super.init(filterConfig); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { LOGGER.info("doFilter called ...............@@@"); chain.doFilter(request, new HttpServletResponseWrapper((HttpServletResponse) response) { @Override public void addHeader(String name, String value) { if (!HttpHeaders.CONTENT_LENGTH.equals(name)) { LOGGER.info("adding header :" + name); super.addHeader(name, value); } } @Override public void setHeader(String name, String value) { if (!HttpHeaders.CONTENT_LENGTH.equals(name)) { LOGGER.info("setting header :" + name); super.setHeader(name, value); } } }); } @Override public void destroy() { LOGGER.info("filter destroy called !!!"); Filter.super.destroy(); } }
日志显示除Content-Length外的其他头部已被正常添加:
13.10.2022 11:25:34.947+0530 INFO [o.s.web.servlet.DispatcherServlet] [http-nio-8080-exec-1] Completed initialization in 8 ms 13.10.2022 11:25:34.959+0530 INFO [a.b.c.d.ContentLengthHeaderFilter] [http-nio-8080-exec-1] doFilter called ...............@@@ 13.10.2022 11:25:36.597+0530 INFO [a.b.c.d.ContentLengthHeaderFilter] [http-nio-8080-exec-3] adding header :Location 13.10.2022 11:25:36.597+0530 INFO [a.b.c.d.ContentLengthHeaderFilter] [http-nio-8080-exec-3] adding header :X-CustomHeader 13.10.2022 11:25:36.597+0530 INFO [a.b.c.d.ContentLengthHeaderFilter] [http-nio-8080-exec-3] adding header :Transfer-Encoding
但API响应中仍存在Content-Length头部,导致上述错误。请问过滤器哪里出现问题?或者有其他移除该头部的方法吗?
问题根源
你的过滤器只拦截了addHeader和setHeader方法,但Tomcat这类Servlet容器通常会通过setContentLength、setContentLengthLong这类专门的方法来设置Content-Length头部,这些方法没被你的包装类覆盖,所以容器依然会自动添加该头部。
修正后的过滤器代码
在HttpServletResponseWrapper中额外覆盖setContentLength和setContentLengthLong方法,直接忽略内容长度的设置:
@WebFilter(urlPatterns = "/*", asyncSupported = true) @Order(Integer.MAX_VALUE) public class ContentLengthHeaderFilter implements Filter { private static final Logger LOGGER = LoggerFactory.getLogger(ContentLengthHeaderFilter.class); @Override public void init(FilterConfig filterConfig) throws ServletException { LOGGER.info("filter init called !!!"); Filter.super.init(filterConfig); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { LOGGER.info("doFilter called ...............@@@"); chain.doFilter(request, new HttpServletResponseWrapper((HttpServletResponse) response) { @Override public void addHeader(String name, String value) { if (!HttpHeaders.CONTENT_LENGTH.equals(name)) { LOGGER.info("adding header :" + name); super.addHeader(name, value); } } @Override public void setHeader(String name, String value) { if (!HttpHeaders.CONTENT_LENGTH.equals(name)) { LOGGER.info("setting header :" + name); super.setHeader(name, value); } } @Override public void setContentLength(int len) { // 忽略Content-Length设置 LOGGER.info("Ignoring setContentLength call"); } @Override public void setContentLengthLong(long len) { // 忽略Content-Length设置 LOGGER.info("Ignoring setContentLengthLong call"); } }); } @Override public void destroy() { LOGGER.info("filter destroy called !!!"); Filter.super.destroy(); } }
其他可行方案
方案1:通过Spring Boot配置禁用自动Content-Length设置
在application.properties中添加以下配置:
server.tomcat.send-http2-alpn=false server.tomcat.use-sendfile=false
该配置会让Tomcat不再自动计算并设置Content-Length,转而使用分块传输(对应Transfer-Encoding: chunked),无需编写额外代码。
方案2:使用ResponseBodyAdvice统一处理响应
实现ResponseBodyAdvice接口,在响应返回前主动移除Content-Length头部:
@ControllerAdvice public class RemoveContentLengthAdvice implements ResponseBodyAdvice<Object> { @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { return true; // 对所有接口响应生效 } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { response.getHeaders().remove(HttpHeaders.CONTENT_LENGTH); return body; } }
内容的提问来源于stack exchange,提问作者sap

