现代应用程序很少生活在孤立的岛上,因为它们需要与其他(分布式)应用程序和服务集成。通常使用通过HTTP协议公开的API来实现集成。
本节介绍如何使用Vert.x的HTTP客户端API与第三方Web服务进行集成。
场景:备份到GitHub Gist
在GitHub的要点服务是广受欢迎的共享代码段到世界各地。其他服务可以使用它,例如中等发布平台,其中指向Gist的链接允许将代码片段嵌入到发布中。
GitHub公开了一个用于获取,创建,更新和删除Gist 的详细API。该API使用从https://api.github.com/开始的HTTPS端点和JSON有效负载。
虽然许多操作需要使用OAuth身份验证的客户端的授权,但在匿名的情况下创建Gist是可能的。我们将利用此功能将我们的维基页面备份为Gist。
一个新的按钮将被添加到wiki索引页面上:
点击备份按钮将触发创建一个Gist:
备份Gist包含每个wiki页面1个文件,内容为原始Markdown文本:
更新数据库服务
在我们深入Web客户端API并执行对其他服务的HTTP请求之前,我们需要更新数据库服务API以一次获取所有wiki页面数据。这对应于要添加到的以下SQL查询db-queries.properties
:
all-pages-data =从Pages中选择*
一个新的方法被添加到WikiDatabaseService
接口:
@Fluent
WikiDatabaseService fetchAllPagesData(Handler<AsyncResult<List<JsonObject>>> resultHandler);
实施WikiDatabaseServiceImpl
内容如下:
@Override
public WikiDatabaseService fetchAllPagesData(Handler<AsyncResult<List<JsonObject>>> resultHandler) {
dbClient.query(sqlQueries.get(SqlQuery.ALL_PAGES_DATA), queryResult -> {
if (queryResult.succeeded()) { resultHandler.handle(Future.succeededFuture(queryResult.result().getRows()));
} else {
LOGGER.error("Database query error", queryResult.cause());
resultHandler.handle(Future.failedFuture(queryResult.cause())); } });
return this;
}
Web客户端API
Vert.x核心库提供了一个createHttpClient
来自vertx
上下文对象的方法。实例io.vertx.core.http.HttpClient
提供了用于执行各种HTTP请求的低级方法,并对协议和事件流进行了细粒度的控制。
Web客户端API提供了更简洁的外观,特别是用于简化有效负载数据(非)封送处理。该API以新的依赖关系的形式出现:
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-client</artifactId>
<version>${vertx.version}</version>
</dependency>
以下是单元测试的示例用法。测试启动一个HTTP服务器,然后它使用Web客户端API执行HTTP GET请求,以检查对服务器的请求是否成功:
@Test
public void start_http_server(TestContext context) {
Async async = context.async();
vertx.createHttpServer().requestHandler(req ->
req.response().putHeader("Content-Type", "text/plain").end("Ok"))
.listen(8080, context.asyncAssertSuccess(server -> {
WebClient webClient = WebClient.create(vertx);
webClient.get(8080, "localhost", "/").send(ar -> {
if (ar.succeeded()) {
HttpResponse<Buffer> response = ar.result();
context.assertTrue(response.headers().contains("Content-Type"));
context.assertEquals("text/plain", response.getHeader("Content-Type"));
context.assertEquals("Ok", response.body().toString());
webClient.close();
async.complete();
} else {
async.resolve(Future.failedFuture(ar.cause()));
}
});
}));
}
创建匿名Gists
我们首先需要一个Web客户端对象来发出对Gist API的HTTP请求:
webClient = WebClient.create(vertx, new WebClientOptions()
.setSsl(true)
.setUserAgent("vert-x3"));
tip
由于请求是使用HTTPS完成的,因此我们需要使用SSL支持来配置Web客户端。
GitHub API需要一个有效的User-Agent
头部并请求一个GitHub帐户或组织标识符。我们覆盖默认的用户代理,vert-x3
但您可以选择使用自己的值。
然后,我们修改HttpServerVerticle
该类中的Web路由器配置以添加用于触发备份的新路由:
router.get("/backup").handler(this::backupHandler);
这个处理程序的代码如下:
private void backupHandler(RoutingContext context) {
dbService.fetchAllPagesData(reply -> {
if (reply.succeeded()) {
JsonObject filesObject = new JsonObject();
JsonObject gistPayload = new JsonObject() (1)
.put("files", filesObject)
.put("description", "A wiki backup")
.put("public", true);
reply
.result()
.forEach(page -> {
JsonObject fileObject = new JsonObject(); (2)
filesObject.put(page.getString("NAME"), fileObject);
fileObject.put("content", page.getString("CONTENT"));
});
webClient.post(443, "api.github.com", "/gists") (3)
.putHeader("Accept", "application/vnd.github.v3+json") (4)
.putHeader("Content-Type", "application/json")
.as(BodyCodec.jsonObject()) (5)
.sendJsonObject(gistPayload, ar -> { (6)
if (ar.succeeded()) {
HttpResponse<JsonObject> response = ar.result();
if (response.statusCode() == 201) {
context.put("backup_gist_url", response.body().getString("html_url")); (7)
indexHandler(context);
} else {
StringBuilder message = new StringBuilder()
.append("Could not backup the wiki: ")
.append(response.statusMessage());
JsonObject body = response.body();
if (body != null) {
message.append(System.getProperty("line.separator"))
.append(body.encodePrettily());
}
LOGGER.error(message.toString());
context.fail(502);
}
} else {
Throwable err = ar.cause();
LOGGER.error("HTTP Client error", err);
context.fail(err);
}
});
} else {
context.fail(reply.cause());
}
});
}
Gist创建请求负载是GitHub API文档中概述的JSON 文档。
每个文件都是
files
有效负载对象下的一个条目,其中标题是键,值是文本。Web客户端需要
POST
在端口443(HTTPS)上发出请求,路径必须是/gists
。Accept
在请求中使用application/vnd.github.v3+json
MIME类型是必需的,否则请求失败。指定有效负载是下一行中的JSON对象也很重要。的
BodyCodec
类提供了一个辅助指定该响应将被直接转换为Vert.xJsonObject
实例。也可以使用BodyCodec#json(Class<T>)
JSON数据并将其映射到类型的Java对象T
(这在引擎盖下使用Jackson数据映射)。sendJsonObject
是用JSON负载触发HTTP请求的帮助器。一旦成功,我们可以遍历JSON数据(
html_url
关键字)以获取新创建的Gist的用户友好URL。