You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Apache HttpClient 5的拦截器中替换响应实体?

Apache HttpClient 5 响应拦截器中装饰替换实体的实现方法

问题背景

我们有一段使用Apache HttpClient 4的遗留代码,可在响应拦截器中替换响应实体:

public class GzipResponseInterceptor implements HttpResponseInterceptor
{
    @Override
    public void process(HttpResponse response, HttpContext context) throws HttpException, IOException
    {
        response.setEntity(new GzipDecompressingEntity(response.getEntity()));
    }
}

而版本5的HttpResponse接口没有获取或设置实体的方法,process方法包含EntityDetails参数可获取实体相关信息,但无法直接用其替换实体。

拦截器的Javadoc提到可装饰实体,但未说明具体实现方式:

Interceptors can also manipulate content entities enclosed with messages. Usually this is accomplished by using the 'Decorator' pattern where a wrapper entity class is used to decorate the original entity.

请问该如何实现这一操作?

实现方案

在HttpClient 5中,要实现实体的装饰替换,需通过装饰器模式包装原始实体流,并借助EntityDetails参数修改实体属性、替换内容流,具体实现如下:

代码实现(异步场景)

import org.apache.hc.core5.http.EntityDetails;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.HttpResponseInterceptor;
import org.apache.hc.core5.http.nio.AsyncEntityProducer;
import org.apache.hc.core5.http.nio.support.AbstractAsyncEntityProducer;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.util.Args;

import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;

public class GzipResponseInterceptor implements HttpResponseInterceptor {

    @Override
    public void process(HttpResponse response, EntityDetails entityDetails, HttpContext context) throws HttpException, IOException {
        if (entityDetails == null) {
            return;
        }
        // 校验响应是否采用gzip编码
        String contentEncoding = entityDetails.getContentEncoding();
        if ("gzip".equalsIgnoreCase(contentEncoding)) {
            // 获取原始实体生产者并替换为装饰后的实例
            AsyncEntityProducer originalProducer = (AsyncEntityProducer) entityDetails.getContent();
            entityDetails.setContent(new GzipDecompressingEntityProducer(originalProducer));
            // 更新实体属性:移除gzip编码标记,重置内容长度(解压后长度未知)
            entityDetails.setContentEncoding(null);
            entityDetails.setContentLength(-1);
        }
    }

    // 自定义Gzip解压实体生产者,装饰原始AsyncEntityProducer
    private static class GzipDecompressingEntityProducer extends AbstractAsyncEntityProducer {

        private final AsyncEntityProducer originalProducer;

        public GzipDecompressingEntityProducer(AsyncEntityProducer originalProducer) {
            super(originalProducer.getContentType(), originalProducer.getContentEncoding());
            this.originalProducer = Args.notNull(originalProducer, "Original producer");
        }

        @Override
        protected InputStream generateContent() throws IOException {
            // 将原始实体流包装为GZIPInputStream实现解压
            return new GZIPInputStream(originalProducer.produceContent(null));
        }

        @Override
        public void releaseResources() {
            // 释放原始生产者的资源
            originalProducer.releaseResources();
        }
    }
}

关键说明

  • HttpClient 5的拦截器process方法新增EntityDetails参数,通过它可以直接获取并替换实体的content(即AsyncEntityProducer,异步场景)或EntityProducer(同步场景)。
  • 装饰类GzipDecompressingEntityProducer继承自AbstractAsyncEntityProducer,核心是重写generateContent方法,将原始流包装为解压流。
  • 必须同步更新EntityDetails的属性:移除gzip内容编码,避免后续处理重复解压;重置内容长度为-1(因为解压后长度无法提前确定)。

同步场景适配

如果使用同步HttpClient,只需将AsyncEntityProducer替换为EntityProducer,自定义装饰类实现EntityProducer接口即可,核心逻辑一致。

内容的提问来源于stack exchange,提问作者SPU42

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 19:12:42