Apache HttpClient 4.5.x 学习总结十二:Fluent API(流式API)
第5章 流式API
5.1 易用的门面API
从4.2版本开始,HttpClient提供了一种基于流式接口概念的易用门面API。该API仅暴露HttpClient最核心的功能,适用于不需要HttpClient全部灵活性的简单场景。例如,流式门面API让用户无需手动管理连接和资源释放。
流式API调用示例:
// 执行GET请求:设置超时,返回字符串响应内容
Request.Get("http://somehost/")
.connectTimeout(1000) // 连接超时1秒
.socketTimeout(1000) // 数据传输超时1秒
.execute().returnContent().asString();
// 执行POST请求:启用Expect-Continue握手,使用HTTP/1.1协议
// 提交字符串请求体,返回字节数组响应
Request.Post("http://somehost/do-stuff")
.useExpectContinue() // 启用Expect-Continue
.version(HttpVersion.HTTP_1_1) // 指定HTTP版本
.bodyString("重要数据", ContentType.DEFAULT_TEXT) // 文本类型请求体
.execute().returnContent().asBytes();
// 执行POST请求:添加自定义标头,通过代理提交表单数据
// 并将响应内容保存到文件
Request.Post("http://somehost/some-form")
.addHeader("X-Custom-header", "自定义值") // 添加标头
.viaProxy(new HttpHost("myproxy", 8080)) // 设置代理
.bodyForm(Form.form().add("username", "vip").add("password", "secret").build()) // 表单数据
.execute().saveContent(new File("result.dump")); // 保存到文件
通过Executor执行安全上下文请求:
// 创建带认证的执行器(复用认证信息)
Executor executor = Executor.newInstance()
.auth(new HttpHost("somehost"), "用户名", "密码") // 目标主机认证
.auth(new HttpHost("myproxy", 8080), "用户名", "密码") // 代理认证
.authPreemptive(new HttpHost("myproxy", 8080)); // 启用预认证
// 复用执行器执行请求
executor.execute(Request.Get("http://somehost/"))
.returnContent().asString();
executor.execute(Request.Post("http://somehost/do-stuff")
.useExpectContinue()
.bodyString("重要数据", ContentType.DEFAULT_TEXT))
.returnContent().asString();
5.1.1 响应处理
流式门面API通常自动管理连接和资源释放,但代价是需在内存中缓冲响应内容。强烈建议使用ResponseHandler处理响应,避免内存缓冲。
// 使用ResponseHandler直接处理XML响应
Document result = Request.Get("http://somehost/content")
.execute().handleResponse(new ResponseHandler<Document>() {
public Document handleResponse(final HttpResponse response) throws IOException {
// 状态码检查(非2xx状态抛出异常)
if (response.getStatusLine().getStatusCode() >= 300) {
throw new HttpResponseException(...);
}
// 内容类型检查
if (!ContentType.APPLICATION_XML.equals(ContentType.getOrDefault(entity))) {
throw new ClientProtocolException("非XML内容类型");
}
// 直接解析响应流(不缓冲到内存)
return docBuilder.parse(entity.getContent(), charset);
}
});
核心知识点总结:
-
流式接口设计 (Fluent Interface)
- 通过链式方法调用(如
.connectTimeout(1000).socketTimeout(1000)) - 使代码更接近自然语言(如"执行获取请求->设置超时->返回内容")
- 通过链式方法调用(如
-
自动资源管理
- 自动释放连接,无需手动关闭
HttpClient或响应对象 - 适合快速开发,但需注意响应内容的内存缓冲问题
- 自动释放连接,无需手动关闭
-
关键功能封装
- 超时控制(连接/传输超时)
- 代理配置(
.viaProxy()) - 认证集成(
.auth()和.authPreemptive()) - 内容类型处理(
bodyString(),bodyForm()) - 协议特性(如HTTP/1.1, Expect-Continue握手)
-
安全执行器 (Executor)
- 复用认证信息(如基本认证/Basic Auth)
- 支持预认证(避免401响应延迟)
- 跨请求共享安全上下文
-
高效响应处理
returnContent().asXxx():简单场景但内存缓冲风险ResponseHandler:直接流处理,避免大响应内存溢出- 内置错误处理(自动检查状态码/内容类型)
通俗易懂的解释:
想象流式API像点奶茶:
- 选基础操作(
Get("地址")相当于"我要一杯奶茶")- 加定制要求(
.addHeader("加珍珠"),.timeout("5分钟做好"))- 最后取结果(
.execute()等于下单,.asString()是直接拿到做好的奶茶)关键优势:
- 自动清理:喝完奶茶店员自动收杯子(自动释放连接)
- 复杂订单:通过
.viaProxy()叫跑腿代买,.auth()用会员卡支付- 大订单处理:
- 直接喝奶茶(
asXxx()) → 杯子小可能洒出来(内存溢出)- 用吸管喝(
ResponseHandler) → 大杯奶茶也能慢慢喝(流式处理)适用场景:适合快速发请求(如调用简单API),复杂需求(如大文件下载)需改用底层API。
⚠️ 重要提醒:处理大响应(如图片/文件)时,务必使用
ResponseHandler直接流式处理到磁盘,避免.returnContent()导致内存爆满!
补充解释:基于流式接口概念的易用门面API
- 什么是“流式接口”
- 流式接口(Fluent Interface)是一种让代码像 “说一句话” 一样连贯的编程风格。核心特点是:每个方法调用后会返回一个对象(通常是当前对象),让你可以接着调用下一个方法,形成 “链式调用”。
// 普通写法
user.setName("张三");
user.setAge(20);
user.setGender("男");
// 流式接口写法
user.setName("张三").setAge(20).setGender("男");
- 什么是“门面API”
- “门面”(Facade)源于设计模式中的 “门面模式”,核心作用是:给复杂的系统套一个 “简化接口”,隐藏内部的复杂逻辑,让用户用起来更简单。
在 API 里,比如一个处理文件的系统:
内部可能需要做 “打开文件→读取内容→解析格式→转换编码→保存文件” 等复杂操作,但门面 API 可以把这些打包成一个简化的接口,比如fileTool.convert("旧文件.txt", "新文件.json"),用户不用管中间步骤,直接调用就行。
- 基于流式接口的易用门面API
- 既用 “门面” 简化了复杂操作,又用 “流式接口” 让调用过程像 “说一句话” 一样连贯。
举例:假设用一个“图片处理API”,内部需要完成“加载图片→调整尺寸→加滤镜→保存图片” 等复杂步骤(涉及很多底层逻辑)
- 门面API 的实现
ImageTool tool = new ImageTool();
tool.load("照片.jpg"); // 步骤1:加载
tool.resize(800, 600); // 步骤2:调尺寸
tool.addFilter("复古"); // 步骤3:加滤镜
tool.save("处理后.jpg"); // 步骤4:保存
- 基于流式接口的门面API实现
ImageTool.process("照片.jpg") // 门面:简化入口
.resize(800, 600) // 流式:返回对象,继续调用
.addFilter("复古")
.save("处理后.jpg");
说明:
process("照片.jpg")是门面,隐藏了 “初始化工具、加载图片” 的复杂逻辑;- 后续的
.resize().addFilter().save()是流式接口,让步骤像 “一句话” 一样连贯,读起来像 “处理这张照片,调尺寸,加滤镜,然后保存”,非常直观。
浙公网安备 33010602011771号