Vert.x学习之 Web Client
Vert.x Web Client
中英对照表
- Pump:泵(平滑流式数据读入内存的机制,防止一次性将大量数据读入内存导致内存溢出)
- Response Codec:响应编解码器(编码及解码工具)
- Body Codec:响应体编解码器
组件介绍
Vert.x Web Client(Web客户端)是一个异步的 HTTP 和 HTTP/2 客户端。
Web Client使得发送 HTTP 请求以及从 Web 服务器接收 HTTP 响应变得更加便捷,同时提供了额外的高级功能,例如:
JSON体的编码和解码
请求和响应泵
请求参数的处理
统一的错误处理
提交表单
制作Web Client的目的并非为了替换Vert.x Core中的 HttpClient,而是基于该客户端,扩展并保留其便利的设置和特性,例如请求连接池(Pooling),HTTP/2的支持,流水线/管线的支持等。当您需要对 HTTP 请求和响应做细微粒度控制时,您应当使用 HttpClient。
另外Web Client并未提供 WebSocket API,此时您应当使用 HttpClient。
使用Web Client
如需使用Vert.x Web Client,请先加入以下依赖:
Maven(在
pom.xml文件中):<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-client</artifactId>
<version>3.4.2</version>
</dependency>Gradle(在
build.gradle文件中):dependencies {
compile 'io.vertx:vertx-web-client:3.4.2'
}
对Vert.x Core HTTP Client的回顾
Vert.x Web Client使用Vert.x Core的API,如您对此还不熟悉,请先熟悉 HttpClient 的一些基本概念。
创建Web Client
您可使用缺省设置创建一个 WebClient:
WebClient client = WebClient.create(vertx);
您亦可使用配置选项来创建客户端:
WebClientOptions options = new WebClientOptions()
.setUserAgent("My-App/1.2.3");
options.setKeepAlive(false);
WebClient client = WebClient.create(vertx, options);
Web Client配置选项继承自 HttpClient 配置选项,使用时可根据实际情况选择。
如已在程序中创建 HttpClient,可用以下方式复用:
WebClient client = WebClient.wrap(httpClient);
发送请求
无请求体的简单请求
一般情况下,HTTP GET,OPTIONS以及HEAD请求没有请求体,可用以下方式发送无请求体的HTTP Requests(HTTP请求):
WebClient client = WebClient.create(vertx); // 发送GET请求
client
.get(8080, "myserver.mycompany.com", "/some-uri")
.send(ar -> {
if (ar.succeeded()) {
// 获取响应
HttpResponse<Buffer> response = ar.result(); System.out.println("Received response with status code" + response.statusCode());
} else {
System.out.println("Something went wrong " + ar.cause().getMessage());
}
}); //发送HEAD请求
client
.head(8080, "myserver.mycompany.com", "/some-uri")
.send(ar -> {
if (ar.succeeded()) {
// 获取响应
HttpResponse<Buffer> response = ar.result(); System.out.println("Received response with status code" + response.statusCode());
} else {
System.out.println("Something went wrong " + ar.cause().getMessage());
}
});
您可用以下链式方式向请求URI添加查询参数
client
.get(8080, "myserver.mycompany.com", "/some-uri")
.addQueryParam("param", "param_value")
.send(ar -> {});
在请求URI中的参数将会被预填充
HttpRequest<Buffer> request = client.get(8080, "myserver.mycompany.com", "/some-uri?param1=param1_value¶m2=param2_value"); // 添加param3(参数3)
request.addQueryParam("param3", "param3_value"); // 覆盖param2(参数2)
request.setQueryParam("param2", "another_param2_value");
设置请求URI将会自动清除已有的查询参数
HttpRequest<Buffer> request = client.get(8080, "myserver.mycompany.com", "/some-uri"); // 添加param1(参数1)
request.addQueryParam("param1", "param1_value"); // 覆盖param1(参数1)同时新增param2(参数2)
request.uri("/some-uri?param1=param1_value¶m2=param2_value");
填充请求体
如需要发送请求体,可使用相同的API并在最后加上 sendXXX 方法发送相应的请求体。
例如用 sendBuffer 方法发送一个缓冲体:
client
.post(8080, "myserver.mycompany.com", "/some-uri")
.sendBuffer(buffer, ar -> {
if (ar.succeeded()) {
// Ok
}
});
client
.post(8080, "myserver.mycompany.com", "/some-uri")
.sendBuffer(buffer, ar -> {
if (ar.succeeded()) {
// Ok
}
});
有时候我们并不希望将所有数据一次性全部读入内存,因为文件太大或希望同时处理多个请求,希望每个请求仅使用最小的内存。出于此目的,Web Client可用 sendStream 方法发送流式数据 ReadStream<Buffer>(例如 AsyncFile 便是一个 ReadStream<Buffer>):
client
.post(8080, "myserver.mycompany.com", "/some-uri")
.sendStream(stream, resp -> {});
Web Client会为您设置好传输泵以平滑传输流。如果流长度未知则使用分块传输(chunked transfer)。
如已知流的大小,可在HTTP协议头中设置 content-length 属性
fs.open("content.txt", new OpenOptions(), fileRes -> {
if (fileRes.succeeded()) {
ReadStream<Buffer> fileStream = fileRes.result();
String fileLen = "1024";
// 用POST方法发送文件
client
.post(8080, "myserver.mycompany.com", "/some-uri")
.putHeader("content-length", fileLen)
.sendStream(fileStream, ar -> {
if (ar.succeeded()) {
// Ok
}
});
}
});
此时POST方法不会使用分块传输。
JSON体
有时您需要在请求体中使用JSON格式,可使用 sendJsonObject 方法发送 JsonObject:
client
.post(8080, "myserver.mycompany.com", "/some-uri")
.sendJsonObject(new JsonObject()
.put("firstName", "Dale")
.put("lastName", "Cooper"), ar -> {
if (ar.succeeded()) {
// Ok
}
});
在Java,Groovy以及Kotlin语言中,您亦可使用 sendJson 方法发送POJO(Plain Old Java Object),该方法会自动调用 Json.encode 方法将 POJO 映射为 JSON:
client
.post(8080, "myserver.mycompany.com", "/some-uri")
.sendJson(new User("Dale", "Cooper"), ar -> {
if (ar.succeeded()) {
// Ok
}
});
请注意:
Json.encode方法使用Jackson的 mapper将 POJO 映射成 JSON。
表单提交
您可使用 sendForm 方法发送HTTP表单。
MultiMap form = MultiMap.caseInsensitiveMultiMap();
form.set("firstName", "Dale");
form.set("lastName", "Cooper"); // 用URL编码方式提交表单
client
.post(8080, "myserver.mycompany.com", "/some-uri")
.sendForm(form, ar -> {
if (ar.succeeded()) {
// Ok
}
});
缺省情况下,提交表单的请求头中的 content-type 属性值为 application/x-www-form-urlencoded,您亦可将其设置为 multipart/form-data:
MultiMap form = MultiMap.caseInsensitiveMultiMap();
form.set("firstName", "Dale");
form.set("lastName", "Cooper"); // 用分块方式编码提交表单
client
.post(8080, "myserver.mycompany.com", "/some-uri")
.putHeader("content-type", "multipart/form-data")
.sendForm(form, ar -> {
if (ar.succeeded()) {
// Ok
}
});
请注意:当前版本并不支持分块文件编码(multipart files,即文件上传),该功能可能在将来版本中予以支持。
填充请求头
您可使用以下方式填充请求头:
HttpRequest<Buffer> request = client.get(8080, "myserver.mycompany.com", "/some-uri");
MultiMap headers = request.headers();
headers.set("content-type", "application/json");
headers.set("other-header", "foo");
此处 Headers 是一个 MultiMap 对象,提供了增加、设置以及删除头属性操作的入口。HTTP头的某些特定属性允许设置多个值。
您亦可通过 putHeader 方法写入头属性:
HttpRequest<Buffer> request = client.get(8080, "myserver.mycompany.com", "/some-uri");
request.putHeader("content-type", "application/json");
request.putHeader("other-header", "foo");
重用请求
send 方法可被重复多次调用,这使得配置以及重用 HttpRequest 对象变得更加便捷:
HttpRequest<Buffer> get = client.get(8080, "myserver.mycompany.com", "/some-uri");
get.send(ar -> {
if (ar.succeeded()) {
// Ok
}
}); // 再次发送同样的请求
get.send(ar -> {
if (ar.succeeded()) {
// Ok
}
});
请注意,HttpRequest 对象是可变的。 所以在修改缓存中的对象之前,您应当使用 copy 方法先复制一份拷贝:
HttpRequest<Buffer> get = client.get(8080, "myserver.mycompany.com", "/some-uri");
get.send(ar -> {
if (ar.succeeded()) {
// Ok
}
}); // 获取同样的请求
get.copy()
.putHeader("an-header", "with-some-value")
.send(ar -> {
if (ar.succeeded()) {
// Ok
}
超时
您可通过 timeout 方法设置超时时间。
client
.get(8080, "myserver.mycompany.com", "/some-uri")
.timeout(5000)
.send(ar -> {
if (ar.succeeded()) {
// Ok
} else {
// 此处可填入超时处理部分代码
}
});
若请求在设定时间内没返回任何数据,则一个超时异常将会传递给响应处理代码。
处理HTTP响应
Web Client请求发送之后,返回的结果将会被包装在异步结果 HttpResponse 中。
当响应被成功接收到之后,相应的回调函数将会被触发。
client
.get(8080, "myserver.mycompany.com", "/some-uri")
.send(ar -> {
if (ar.succeeded()) { HttpResponse<Buffer> response = ar.result(); System.out.println("Received response with status code" + response.statusCode());
} else {
System.out.println("Something went wrong " + ar.cause().getMessage());
}
});
警告:缺省状况下,响应会被完全缓冲读入内存,请用
BodyCodec.pipe方法将响应写入流。
响应编解码器
缺省状况下,响应以缓冲形式提供,并不提供任何形式的解码。
可用 BodyCodec 将响应定制成以下类型:
- 普通字符串
- JSON对象
- 将JSON映射成POJO
WriteStream
响应体编解码器对二进制数据流解码,以节省您在响应处理中的代码。
使用 BodyCodec.jsonObject 将结果解码为JSON对象:
client
.get(8080, "myserver.mycompany.com", "/some-uri")
.as(BodyCodec.jsonObject())
.send(ar -> {
if (ar.succeeded()) {
HttpResponse<JsonObject> response = ar.result(); JsonObject body = response.body(); System.out.println("Received response with status code" + response.statusCode() + " with body " + body);
} else {
System.out.println("Something went wrong " + ar.cause().getMessage());
}
});
在Java,Groovy以及Kotlin语言中,JSON对象可被解码映射成POJO:
client
.get(8080, "myserver.mycompany.com", "/some-uri")
.as(BodyCodec.json(User.class))
.send(ar -> {
if (ar.succeeded()) {
HttpResponse<User> response = ar.result(); User user = response.body(); System.out.println("Received response with status code" + response.statusCode() + " with body " +
user.getFirstName() + " " + user.getLastName());
} else {
System.out.println("Something went wrong " + ar.cause().getMessage());
}
});
当响应结果较大时,请使用 BodyCodec.pipe 方法。响应体编解码器将响应结果压入 WriteStream 并在最后发出成功或失败的信号。
client
.get(8080, "myserver.mycompany.com", "/some-uri")
.as(BodyCodec.pipe(writeStream))
.send(ar -> {
if (ar.succeeded()) { HttpResponse<Void> response = ar.result(); System.out.println("Received response with status code" + response.statusCode());
} else {
System.out.println("Something went wrong " + ar.cause().getMessage());
}
});
最后,如您对响应结果不感兴趣,可用 BodyCodec.none 废弃响应体。
client
.get(8080, "myserver.mycompany.com", "/some-uri")
.as(BodyCodec.none())
.send(ar -> {
if (ar.succeeded()) { HttpResponse<Void> response = ar.result(); System.out.println("Received response with status code" + response.statusCode());
} else {
System.out.println("Something went wrong " + ar.cause().getMessage());
}
});
若无法预知响应内容类型,您依旧可以在获取结果之后,用 bodyAsXXX() 方法将其转换成特定的类型
client
.get(8080, "myserver.mycompany.com", "/some-uri")
.send(ar -> {
if (ar.succeeded()) { HttpResponse<Buffer> response = ar.result(); // 将结果解码为Json对象
JsonObject body = response.bodyAsJsonObject(); System.out.println("Received response with status code" + response.statusCode() + " with body " + body);
} else {
System.out.println("Something went wrong " + ar.cause().getMessage());
}
});
警告:这种方式仅对响应结果为缓冲体有效。
处理30x重定向
缺省状况下,客户端将会依照30x状态码自动重定向,您可使用 WebClientOptions 予以配置:
WebClient client = WebClient.create(vertx, new WebClientOptions().setFollowRedirects(false));
客户端将会执行最多达16次重定向,该参数亦可在 WebClientOptions 配置:
WebClient client = WebClient.create(vertx, new WebClientOptions().setMaxRedirects(5));
使用HTTPS
Vert.x Web Client可用与 HttpClient 相同方式配置HTTPS协议。
您可对每个请求单独设置:
client
.get(443, "myserver.mycompany.com", "/some-uri")
.ssl(true)
.send(ar -> {
if (ar.succeeded()) {
// 获取响应
HttpResponse<Buffer> response = ar.result(); System.out.println("Received response with status code" + response.statusCode());
} else {
System.out.println("Something went wrong " + ar.cause().getMessage());
}
});
或使用绝对路径:
client
.getAbs("https://myserver.mycompany.com:4043/some-uri")
.send(ar -> {
if (ar.succeeded()) {
// 获取响应
HttpResponse<Buffer> response = ar.result(); System.out.println("Received response with status code" + response.statusCode());
} else {
System.out.println("Something went wrong " + ar.cause().getMessage());
}
});
RxJava API
RxJava的 HttpRequest 提供了原版API的响应式版本,rxSend 方法返回一个可被订阅的 Single<HttpResponse<Buffer>>,故单个 Single 可被多次订阅。
Single<HttpResponse<Buffer>> single = client
.get(8080, "myserver.mycompany.com", "/some-uri")
.rxSend(); // 发送一次请求,并处理其响应,rx通常通过订阅触发各种响应
single.subscribe(response -> {
System.out.println("Received 1st response with status code" + response.statusCode());
}, error -> {
System.out.println("Something went wrong " + error.getMessage());
}); // 再次发送请求
single.subscribe(response -> {
System.out.println("Received 2nd response with status code" + response.statusCode());
}, error -> {
System.out.println("Something went wrong " + error.getMessage());
});
获取到的 Single 可与其它RxJava API自然组合成链式处理
Single<String> url = client
.get(8080, "myserver.mycompany.com", "/some-uri")
.rxSend()
.map(HttpResponse::bodyAsString); // 用flatMap将返回值内的链接作为参数传入lambda,在lambda中将其设置成发送请求,并返回Single,在下一步订阅中予以触发
url
.flatMap(u -> client.getAbs(u).rxSend())
.subscribe(response -> {
System.out.println("Received response with status code" + response.statusCode());
}, error -> {
System.out.println("Something went wrong " + error.getMessage());
});
之前的例子可写成
Single<HttpResponse<JsonObject>> single = client
.get(8080, "myserver.mycompany.com", "/some-uri")
.putHeader("some-header", "header-value")
.addQueryParam("some-param", "param value")
.as(BodyCodec.jsonObject())
.rxSend();
single.subscribe(resp -> {
System.out.println(resp.statusCode());
System.out.println(resp.body());
});
当发送请求体为 Observable<Buffer> 时,应使用 sendStream:
Observable<Buffer> body = getPayload(); Single<HttpResponse<Buffer>> single = client
.post(8080, "myserver.mycompany.com", "/some-uri")
.rxSendStream(body);
single.subscribe(resp -> {
System.out.println(resp.statusCode());
System.out.println(resp.body());
});
当订阅时,body 将会被订阅,其内容将会被用于请求中。
Vert.x学习之 Web Client的更多相关文章
- Vert.x 学习之MongoDB Client
Vert.x MongoDB Client 原文档:Vert.x MongoDB Client 组件介绍 您的 Vert.x 应用可以使用 Vert.x MongoDB Client(以下简称客户端) ...
- 零基础如何系统学习Java Web
零基础如何系统学习Java Web? 我来给你说一说 你要下决心,我要转行做开发,这样你才能学成. 你要会打字,我公司原来有一个程序员,打字都是两个手一指禅,身为程序员你一指禅怎么写出的代码,半个 ...
- Vmware web client 5.5 控制台连接不上:Connection timed out
因XP下安装vsphere client5.5后,无法连接远程vsphere.因此使用vsphere web client. 但是用vsphere web client打开控制台,报错:Conneti ...
- 虚拟化之vmware-vsphere (web) client
两种客户端 vsphere client 配置>软件>高级设置里的变量 uservars.supressshellwarning=1 vsphere web client 安装完vSphe ...
- VMware系统运维(六)VMware vSphere Web Client安装
1.开始安装VMware vSphere Web Client 2.下一步 3.接受协议,下一步,大哥求你了,下次直接将这个默认下一步吧,嘿嘿 4.选择安装位置,下一步 5.配置端口号,默认9090和 ...
- 疯狂学习java web
因工作需要,疯狂学习java web,只是这么多年一直从事C++开发,突然之间要接手同事的那么一大堆代码,真有无从下手的感觉,首先是要学习html,然后是js, 然后是jsp,当然还有各种框架,想想就 ...
- vsphere client/web client 开启ESXi SSH服务
●vsphere client 开启ESXi SSH服务 1,从主机和群集列表选择主机esxi01.2,选择[配置]-->[软件]列表-->[安全配置文件]--> [服务],点击右上 ...
- vsphere web client 使用中文的解决办法
1. 很多网站这么说的: vsphere web client的默认URL为:https://hostname:9443/vsphere-client 可以在URL后面加上一个参数来指定区域语言 英语 ...
- 菜鸟学习Spring Web MVC之一
---恢复内容开始--- 当当当!!沉寂两日,学习Spring Web MVC去了.吐槽:近日跟同行探讨了下,前端攻城师,左肩担着设计师绘图,右肩担着JAVA代码?!我虽设计过UI,但这只算是PS技巧 ...
随机推荐
- AWS Aurora数据库 Multi-Master 小测
AWS Aurora Mysql终于推出了Multi-Master,直面硬刚Oracle RAC.在多一份数据库产品选择的小兴奋之余,我们也看看新推出的Multi-Master的特点(包括优缺点). ...
- Java实现调用Bartender控制条码打印机
官方提供的主要是C#支持. 基于java调用bartender二次开发官方给了一份1998年的J#代码,,,完全用不了,,,百度谷歌搜索万能的网友的答案,发现也没有可参考的.. 最后想到了之前用到了一 ...
- Java泛型使用的简单介绍
目录 一. 泛型是什么 二. 使用泛型有什么好处 三. 泛型类 四. 泛型接口 五. 泛型方法 六. 限定类型变量 七. 泛型通配符 7.1 上界通配符 7.2 下界通配符 7.3 无限定通配符 八. ...
- 消息中间件-activemq实战整合Spring之Topic模式(五)
这一节我们看一下Topic模式下的消息发布是如何处理的. applicationContext-ActiveMQ.xml配置: <?xml version="1.0" enc ...
- ASP.NET Core 框架本质学习
本文作为学习过程中的一个记录. 学习文章地址: https://www.cnblogs.com/artech/p/inside-asp-net-core-framework.html 一. ASP.N ...
- ES 26 - 通过partial update局部更新索引文档 (partial update增量修改原理)
目录 1 什么是partial update 1.1 全量修改文档的原理 1.2 修改指定field的思路 1.3 partial update的优势 1.4 partial update的使用 2 ...
- 洛谷 P4344 [SHOI2015]脑洞治疗仪
题意简述 维护序列,支持以下操作: 0 l r:将l~r赋为0 1 l1 r1 l2 r2:将l1~r1中的1替换l2~r2中的0,多余舍弃 2 l r:询问l~r中最大连续1的长度 题解思路 珂朵莉 ...
- Docker之- 使用Docker 镜像和仓库
目录 使用Docker 镜像和仓库 什么是 Docker 镜像 列出 Docker 镜像 tag 标签 Docker Hub 拉取镜像 查找镜像 构建镜像 创建Docker Hub 账号 使用 Doc ...
- 从0开始学Git——Git的常见操作
Git常用命令 创建Git版本库 git init [目录]#创建目录版本库, 不写的话是当前目录 该命令会在目录中创建一个名为.git的隐藏目录 文件提交 添加文件: git add 文件名 #添加 ...
- Unity打包——Android和IOS
推荐阅读: 我的CSDN 我的博客园 QQ群:704621321 我的个人博客 Android包 (1)首先需要安装Android SDK和Java JDK.SDK需要添加tools目录,JD ...