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不一致情况,修改之后,微服务接口接口也能成功调用了

参考资料


相关推荐
Ramos丶4 分钟前
【ABAP】 从无到有 新建一个Webdynpro程序
java·前端·javascript
sniper_fandc15 分钟前
SpringMVC详解
java·springmvc
TT哇1 小时前
【Java EE初阶】计算机是如何⼯作的
java·redis·java-ee
paopaokaka_luck2 小时前
基于SpringBoot+Vue的电影售票系统(协同过滤算法)
vue.js·spring boot·后端
IT_10248 小时前
Spring Boot项目开发实战销售管理系统——系统设计!
大数据·spring boot·后端
Fireworkitte8 小时前
Apache POI 详解 - Java 操作 Excel/Word/PPT
java·apache·excel
weixin-a153003083168 小时前
【playwright篇】教程(十七)[html元素知识]
java·前端·html
DCTANT8 小时前
【原创】国产化适配-全量迁移MySQL数据到OpenGauss数据库
java·数据库·spring boot·mysql·opengauss
ai小鬼头9 小时前
AIStarter最新版怎么卸载AI项目?一键删除操作指南(附路径设置技巧)
前端·后端·github
Touper.9 小时前
SpringBoot -- 自动配置原理
java·spring boot·后端