Spring REST服务报Invalid chunk header异常及400错误求助
Spring REST控制器处理application/octet-stream时的Invalid chunk header异常排查
首先,先梳理下你的问题场景:你有一个接收application/octet-stream类型请求的Spring REST控制器,通过@RequestBody byte[]解析GZ文件请求体,但部分请求会抛出HttpMessageNotReadableException,嵌套异常为java.io.IOException: Invalid chunk header,最终返回客户端400响应。你的控制器代码如下:
public ResponseEntity<String> test(@RequestBody byte[] request, @RequestHeader HttpHeaders headers, HttpServletRequest servletRequest) {}
对应的核心异常栈:
org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message; nested exception is java.io.IOException: Invalid chunk header at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:229) ~[spring-webmvc-4.3.10.RELEASE.jar:4.3.10.RELEASE] ... at org.apache.catalina.core.ApplicationFilterChain.doFilter(...)
异常原因分析
这个错误本质是HTTP分块传输编码(Chunked Transfer Encoding)的解析失败,常见触发原因有以下几种:
- 客户端请求格式不规范:如果客户端使用分块编码发送请求,但分块头部不符合HTTP规范(比如不是有效的十六进制数字,或者缺少分块结束标记
0\r\n\r\n),Tomcat的解析器就会抛出该异常。 - 中间代理/网关篡改请求:如果请求经过Nginx、Apache等反向代理,代理的某些配置(比如
proxy_buffering、自动修改Transfer-Encoding头)可能破坏分块数据格式,导致后端解析失败。 - 框架/容器版本bug:你使用的Spring 4.3.10和Tomcat 7.0.57都是较老的版本,这两个组件在早期版本中确实存在一些分块解析的已知问题,比如处理特殊分块场景时的逻辑漏洞。
- 请求头冲突:如果请求同时设置了
Content-Length和Transfer-Encoding: chunked,这种不符合HTTP规范的请求会让解析器陷入混乱,触发异常。
解决方法
针对上述原因,你可以按以下步骤排查和修复:
验证客户端请求的正确性
- 让客户端检查请求是否合规:确保分块头部是有效的十六进制字符串,且请求末尾带有正确的结束块(
0\r\n\r\n)。 - 如果客户端不需要分块传输,建议直接发送带
Content-Length头的请求,避免分块编码带来的解析风险。
- 让客户端检查请求是否合规:确保分块头部是有效的十六进制字符串,且请求末尾带有正确的结束块(
检查中间代理配置
- 若存在反向代理(如Nginx),检查是否有修改分块数据的配置:比如关闭
proxy_buffering(或调整相关参数),确保代理不会篡改Transfer-Encoding头和分块内容。 - 确认代理没有自动添加/删除请求头,导致请求格式不符合HTTP规范。
- 若存在反向代理(如Nginx),检查是否有修改分块数据的配置:比如关闭
升级框架和容器版本
- 升级到Spring 4.3.x系列的最新稳定版(如4.3.30.RELEASE),该版本修复了很多早期的消息解析bug。
- 同步升级Tomcat到7.0.x的最新补丁版(如7.0.109),Tomcat在后续版本中修复了不少分块解析的问题。
绕过Spring消息转换器,手动读取请求体
如果上述方法无效,可以尝试直接从HttpServletRequest获取输入流读取数据,跳过Spring的@RequestBody解析逻辑:import org.apache.commons.io.IOUtils; // 可使用Apache Commons IO,或自行实现流读取逻辑 public ResponseEntity<String> test(HttpServletRequest servletRequest, @RequestHeader HttpHeaders headers) throws IOException { // 直接读取原始输入流 InputStream inputStream = servletRequest.getInputStream(); byte[] requestBytes = IOUtils.toByteArray(inputStream); // 后续处理GZ文件的业务逻辑 // ... return ResponseEntity.ok("处理完成"); }这种方式能直接获取原始请求数据,避免Spring消息转换器在分块解析时出错。
自定义消息转换器配置
你也可以尝试自定义ByteArrayHttpMessageConverter,调整其解析行为:@Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { ByteArrayHttpMessageConverter converter = new ByteArrayHttpMessageConverter(); converter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM)); converters.add(0, converter); // 优先使用自定义转换器 super.configureMessageConverters(converters); } }确保转换器正确支持
application/octet-stream类型,且无额外解析逻辑干扰分块数据读取。
内容的提问来源于stack exchange,提问作者Nikhil Kakade
相关产品推荐
相关产品推荐

