如何在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
相关产品推荐
相关产品推荐

