Feign调Post接口异常:Incomplete output stream

背景


最近使用了一个微服务权限管理框架,在使用feign调用其他服务的post接口时,总是调不通,报错如下

bash 复制代码
feign.RetryableException: 
    Incomplete output stream executing POST http://xxx

表示 Feign 在发送请求体(request body)时,输出流还没写完就被中断了

经过多方查找,发现是由于框架在实现RequestInterceptor 的配置类上,错误处理了Content-Length

分析


原先的配置类代码如下

java 复制代码
@Configuration
public class FeignConfig implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            return;
        }

        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        Enumeration<String> headerNames = request.getHeaderNames();
        if (headerNames != null) {
            while (headerNames.hasMoreElements()) {
                String name = headerNames.nextElement();
                Enumeration<String> values = request.getHeaders(name);
                while (values.hasMoreElements()) {
                    String value = values.nextElement();
                    template.header(name, value);
                }
            }
        }
    }
}

这段代码是一个 Feign 请求拦截器(RequestInterceptor 的实现,

主要用于在微服务间通过 Feign 进行 HTTP 调用时,自动透传 当前 HTTP 请求的请求头(Headers),确保关键信息(如认证 Token、追踪 ID 等)在服务调用链中不丢失。

其中,关键的代码在于

java 复制代码
Enumeration<String> values = request.getHeaders(name);
while (values.hasMoreElements()) {
    String value = values.nextElement();
    template.header(name, value);
}

作用是:取原始请求的所有 Header 字段名称,将原始请求的 Header 键值对注入到 Feign 请求模板,确保下游服务能接收到相同的头信息

但是,这里忽略了一个问题,将所有的 Header 字段都进行了注入,包括Content-Length

Content-Length 是 HTTP 报文头部(Header)中的一个标准字段,用来 指明请求体或响应体(body)的字节长度,单位是字节(byte)

场景 请求体类型 是否自动生成 说明
浏览器(fetch / axios JSON / 表单 ✅ 是 浏览器自动设置
Postman / curl 任意 ✅ 是 工具自动处理
Java RestTemplate JSON / 表单 ✅ 是 Spring 自动计算
Java HttpClient(JDK 11+) 字符串 / JSON ✅ 是 自动生成
Feign(常见调用) @RequestBody JSON ✅ 是 自动编码并设置
Feign 上传文件 MultipartFile ⚠️ 不一定 多数用 chunked,不设置 Content-Length
手写 HttpURLConnection 自定义 OutputStream ❌ 否 需要手动设置

我们的接口调用路径是这样的:

浏览器 -> 微服务A 接口1 -> feign 拦截器 -> 微服务B 接口2

结合上面的表格,在这个过程中,我们的Content-Length都应该是自动生成的

但是,我们在feign拦截器中,将微服务A 接口1Content-Length一并复制给了微服务B 接口2

Content-Length 是根据 HTTP 请求体或响应体的字节长度(byte) 计算出来的

而两者的请求体 字节长度不同,计算出来的Content-Length也不会相同

这样造成的结果便是:Content-Length不匹配问题,即太大或者太小

如果 Content-Length 数值设置错误,可能导致:

  • 数据截断(太小)
  • 请求挂起或超时(太大)

如图所示

最终微服务B 接口2得到了错误的Content-Length:10

实际上,它却发送了 100字节的数据,表现为表现为 写流中断Incomplete output stream

解决方法


既然知道了原因,那么修改也就简单了

我们可以在遍历Header字段时,不注入Content-Length,使其自动生成,即

java 复制代码
String name = headerNames.nextElement();
if (!"content-length".equals(name)) {
    Enumeration<String> values = request.getHeaders(name);
    while (values.hasMoreElements()) {
        String value = values.nextElement();
        template.header(name, value);
    }
}

这样,就避免了Content-Length不一致情况,修改之后,微服务接口接口也能成功调用了

参考资料


相关推荐
Mryan200521 分钟前
✨ 使用 Flask 实现头像文件上传与加载功能
后端·python·flask
程序员是干活的26 分钟前
Java EE前端技术编程脚本语言JavaScript
java·大数据·前端·数据库·人工智能
某个默默无闻奋斗的人32 分钟前
【矩阵专题】Leetcode48.旋转图像(Hot100)
java·算法·leetcode
张同学的IT技术日记36 分钟前
重构 MVC:让经典架构完美适配复杂智能系统的后端业务逻辑层(内附框架示例代码)
c++·后端·重构·架构·mvc·软件开发·工程应用
℡余晖^37 分钟前
每日面试题14:CMS与G1垃圾回收器的区别
java·jvm·算法
南囝coding1 小时前
Coze 开源了!所有人都可以免费使用了
前端·后端·产品
CDwenhuohuo1 小时前
滚动提示组件
java·前端·javascript
围巾哥萧尘1 小时前
macOS 终端美化安装指南🧣
后端
wei3872452321 小时前
集训总结2
java·数据库·mysql
GoodTime1 小时前
CodeBuddy IDE深度体验:全球首个产设研一体AI工程师的真实使用报告
前端·后端·架构