关于java请求c++遇到transfer encoding=chunked响应RestTemplate的简单处理

背景

我在一次项目中,遇到和c++方的接口对接,但是发现对方的响应可能由于数据大小原因,其响应方式采用了分块响应的方式,及他的响应头设置为了 transfer-encoding=chunked的方式
项目中调用接口响应截图
我再用之前常规restTemplate调用http服务接口的方式,发现无论怎样都会拿不到响应体,但是在拦截器中确实又有响应数据,为什么呢?

经过简单的查看源码,发现针对chunked的这种响应,本来是针对浏览器的,在java端的处理会忽略掉content-length这一指标,而这个指标其实是restTemplate获取相应数据的依据,也就是说,默认的restTemplate配置会根据这个响应头参数来判断在你调用
responseEntity.getBody()时能否拿到响应

我的处理的话 思路是在拦截器中获取到响应体的大小,手动设置响应头content-length的长度,下面贴上部分代码:

public class RestClientChunkedInterceptor implements ClientHttpRequestInterceptor {

    public static final String CHUNKED = "chunked";

    @SuppressWarnings("NullableProblems")
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {

        ClientHttpResponse response = execution.execute(request, body);
        if (needResetResponseContentLength(response)){
            response = wrapperResponse(response);
        }
        return response;
    }

    private BufferingClientHttpResponseWrapper wrapperResponse(ClientHttpResponse response) throws IOException {
        BufferingClientHttpResponseWrapper responseWrapper = new BufferingClientHttpResponseWrapper(response);
        int available = responseWrapper.getBody().available();
        response.getHeaders().setContentLength(available);
        return responseWrapper;
    }

    /**
     * 判断响应头是否包含transferEncoding=chunked 且contentType = application/octet-stream
     * @param response rest请求的响应体
     * @return true为重新设置content-length,false则原样返回
     */
    private boolean needResetResponseContentLength(ClientHttpResponse response){
        if (Objects.isNull(response)){
            return false;
        }
        HttpHeaders headers = response.getHeaders();
        List<String> transferEncodingList = headers.get("Transfer-Encoding");
        if (CollectionUtils.isEmpty(transferEncodingList)){
            return false;
        }
        MediaType contentType = headers.getContentType();
        if (Objects.isNull(contentType)){
            return false;
        }
        return transferEncodingList.contains(CHUNKED)
                && StringUtils.equals(MediaType.APPLICATION_OCTET_STREAM_VALUE, contentType.toString());
    }
}

再写一个可以重复获取响应body的包装类

final class BufferingClientHttpResponseWrapper implements ClientHttpResponse {

	private final ClientHttpResponse response;

	private byte[] body;


	BufferingClientHttpResponseWrapper(ClientHttpResponse response) {
		this.response = response;
	}


	@Override
	public HttpStatus getStatusCode() throws IOException {
		return this.response.getStatusCode();
	}

	@Override
	public int getRawStatusCode() throws IOException {
		return this.response.getRawStatusCode();
	}

	@Override
	public String getStatusText() throws IOException {
		return this.response.getStatusText();
	}

	@Override
	public HttpHeaders getHeaders() {
		return this.response.getHeaders();
	}

	@Override
	public InputStream getBody() throws IOException {
		if (this.body == null) {
			this.body = StreamUtils.copyToByteArray(this.response.getBody());
		}
		return new ByteArrayInputStream(this.body);
	}

	@Override
	public void close() {
		this.response.close();
	}

}

这样子,在我们使用restTemplate时,就可以不关心这个类型的响应方式,也算一种临时解决方案
但是这里还有一个问题

当响应过大的时候会导致内存的溢出

针对这种情况,还有两种实现方式自己一直没有亲自去尝试

  • 通过操作流的方式读取响应的数据
  • 通过socket访问c++接口,解析数据

对以上方式,希望对看到这篇文章的你有所帮助,如果你有更好的解决方式,或思路,欢迎评论指教~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值