Feign请求头header转发下游和java.io.IOException: Incomplete output stream踩坑

  • 今天接到新需求 需要在内部调用的服务(也就是使用feign进行调用的时候) 将请求头的内容一并转发 以供下游服务使用
  • 先上代码
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;

/**
 * @Description 给Feign添加拦截器 转发请求头
 * @Author ljm
 * @Date 2022/2/18 9:46
 */
@Configuration
public class FeignInterceptorConfiguration implements RequestInterceptor {

    public static final String CONTENT_LENGTH = "content-length";

    @Override
    public void apply(RequestTemplate template) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) return;
        HttpServletRequest request = attributes.getRequest();
        Enumeration<String> headerNames = request.getHeaderNames();
        if (headerNames == null) return;
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            // 注意!注意!注意!坑之所在 此处建议只添加自己想要下发给下游的请求头
            if (CONTENT_LENGTH.equalsIgnoreCase(name)) continue;
            String values = request.getHeader(name);
            template.header(name, values);
        }
    }
}
  • 原理就是实现Feign的请求拦截器 在请求时从当前线程获取请求头并写入
  • 很简单 将该类放在调用方 就可以实现功能了
  • 接下来说一下踩的坑 也就是 标题的 java.io.IOException: Incomplete output stream
  • 首先 吐槽一下 用这个错误搜索的全是不能用的东西 一模一样的让我替换类 一篇文章抄来抄去
  • 这个错误导致的原因 就是代码中标记注释的那行
  • 最开始是没有这行代码的
  • // 注意!注意!注意!坑之所在 此处建议只添加自己想要下发给下游的请求头
  • if (CONTENT_LENGTH.equalsIgnoreCase(name)) continue;
  • 通过断点调试 发现 将请求头中的content-length转发下去后 会导致下游接收到数据后 InputStream 不能正确处理
  • 像我这次请求 就是因为我的content-length长于真正的content-length 也就是明明没有了 还要读 当然 短了也会有读不出来的问题
  • 结论 强烈建议只处理想要转发的请求头 而不要全部转发!!!
  • 可以参考我的实现方案 将需要转发的请求头 写入配置中心(也可以是配置文件、缓存等等)中
  • 下面贴个大概意思 供兄弟们参考
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;

/**
 * @Description 给Feign添加拦截器 转发请求头
 * @Author ljm
 * @Date 2022/2/18 9:46
 */
@Configuration
public class FeignInterceptorConfiguration implements RequestInterceptor {
    private final PassHeaderProperties passHeaderProperties;

    public FeignInterceptorConfiguration(PassHeaderProperties passHeaderProperties) {
        this.passHeaderProperties = passHeaderProperties;
    }

    @Override
    public void apply(RequestTemplate template) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) return;
        HttpServletRequest request = attributes.getRequest();
        Enumeration<String> headerNames = request.getHeaderNames();
        if (headerNames == null) return;
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            if (!passHeaderProperties.getHeaders().contains(name)) continue;
            String values = request.getHeader(name);
            template.header(name, values);
        }
    }
}
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;

import java.util.HashSet;
import java.util.Set;

/**
 * 放行配置
 *
 * @Author ljm
 */
@Configuration(proxyBeanMethods = false)
@RefreshScope
@ConfigurationProperties(prefix = "pass")
public class PassHeaderProperties {
    /**
     * 放行的头列表
     */
    private Set<String> headers = new HashSet<>();

    public Set<String> getHeaders() {
        return headers;
    }

    public void setHeaders(Set<String> headers) {
        this.headers = headers;
    }
}
pass:
  headers:
    - bbb
    - aaa
    - xxx
    - ...
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值