Google HTTP 简介
1. 概述
在本文中,我们将了解用于 Java 的 Google HTTP 客户端库 ,它是一个快速、抽象的库,用于通过 HTTP 连接协议访问任何资源。
客户端的主要特点是:
- 一个 HTTP 抽象层,可让您解耦任何低级库
- HTTP 响应和请求内容的快速、高效和灵活的 JSON 和 XML 解析模型
- 易于使用的 HTTP 资源映射注解和抽象
该库还可以在 Java 5 及更高版本中使用,使其成为遗留(SE 和 EE)项目的重要选择。
在本文中,我们将开发一个简单的应用程序,该应用程序将连接到 GitHub API 并检索用户,同时涵盖该库的一些最有趣的功能。
2. Maven依赖
要使用该库,我们需要google-http-client依赖项:
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client</artifactId>
<version>1.23.0</version>
</dependency>
最新版本可以在Maven Central 找到。
3. 提出一个简单的请求
让我们首先向 GitHub 页面发出一个简单的 GET 请求,以展示 Google Http 客户端是如何开箱即用的:
HttpRequestFactory requestFactory
= new NetHttpTransport().createRequestFactory();
HttpRequest request = requestFactory.buildGetRequest(
new GenericUrl("https://github.com"));
String rawResponse = request.execute().parseAsString()
为了做出最简单的请求,我们至少需要:
- HttpRequestFactory这用于构建我们的请求
- HttpTransport底层 HTTP 传输层的抽象
- GenericUrl一个封装了 Url 的类
- HttpRequest处理请求的实际执行
在接下来的部分中,我们将通过一个返回 JSON 格式的实际 API 来介绍所有这些以及一个更复杂的示例。
4. 可插拔 HTTP 传输
该库有一个抽象的HttpTransport类,它允许我们在它之上构建并更改为选择的底层低级 HTTP 传输库:
public class GitHubExample {
static HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
}
在此示例中,我们使用了NetHttpTransport,它基于所有 Java SDK 中的HttpURLConnection 。这是一个很好的开始选择,因为它众所周知且可靠。
当然,可能存在我们需要一些高级定制的情况,因此需要更复杂的低级库。
对于这种情况,有ApacheHttpTransport:
public class GitHubExample {
static HttpTransport HTTP_TRANSPORT = new ApacheHttpTransport();
}
ApacheHttpTransport基于流行的Apache HttpClient ,其中包括用于配置连接的多种选择。
此外,该库提供了构建低级实现的选项,使其非常灵活。
5. JSON解析
Google Http 客户端包含另一个用于 JSON 解析的抽象。这样做的一个主要优点是低级解析库的选择是可互换的。
共有三个内置选项,它们都扩展了 JsonFactory,并且还包括实现我们自己的可能性。
5.1. 可互换的解析库
在我们的示例中,我们将使用 Jackson2 实现,它需要google-http-client-jackson2 依赖项:
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client-jackson2</artifactId>
<version>1.23.0</version>
</dependency>
在此之后,我们现在可以包含JsonFactory:
public class GitHubExample {
static HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
staticJsonFactory JSON_FACTORY = new JacksonFactory();
}
JacksonFactory是用于解析/序列化操作的最快和最流行的库。
这是以库大小为代价的(在某些情况下这可能是一个问题)。出于这个原因,谷歌还提供了GsonFactory,它是谷歌 GSON 库的一个实现,一个轻量级的 JSON 解析库。
还有可能编写我们的低级解析器实现。
5.2. @Key注解
我们可以使用*@Key*注解来指示需要从 JSON 解析或序列化为 JSON 的字段:
public class User {
@Key
private String login;
@Key
private long id;
@Key("email")
private String email;
// standard getters and setters
}
在这里,我们正在制作一个User抽象,我们从 GitHub API 批量接收(我们将在本文后面进行实际解析)。
请注意,没有@Key*注解的字段被视为内部字段,不会从 JSON 解析或序列化为 JSON*。此外,字段的可见性并不重要,getter 或 setter 方法的存在也不重要。
我们可以指定*@Key*注解的值,将其映射到正确的 JSON 键。
5.3. GenericJson
只解析我们声明并标记为*@Key*的字段。
为了保留其他内容,我们可以声明我们的类来扩展GenericJson:
public class User extends GenericJson {
//...
}
GenericJson实现了Map接口,这意味着我们可以使用 get 和 put 方法来设置/获取请求/响应中的 JSON 内容。**
6. 调用示例
要使用 Google Http 客户端连接到端点,我们需要一个HttpRequestFactory,它将使用我们之前的抽象HttpTransport和JsonFactory 进行配置:
public class GitHubExample {
static HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
static JsonFactory JSON_FACTORY = new JacksonFactory();
private static void run() throws Exception {
HttpRequestFactory requestFactory
= HTTP_TRANSPORT.createRequestFactory(
(HttpRequest request) -> {
request.setParser(new JsonObjectParser(JSON_FACTORY));
});
}
}
我们将需要的下一件事是要连接到的 URL。该库将其作为扩展GenericUrl的类来处理,在该类上声明的任何字段都被视为查询参数:
public class GitHubUrl extends GenericUrl {
public GitHubUrl(String encodedUrl) {
super(encodedUrl);
}
@Key
public int per_page;
}
在我们的GitHubUrl 中,我们声明了per_page属性,以指示在一次调用 GitHub API 时我们需要多少用户。
让我们继续使用GitHubUrl构建我们的调用:
private static void run() throws Exception {
HttpRequestFactory requestFactory
= HTTP_TRANSPORT.createRequestFactory(
(HttpRequest request) -> {
request.setParser(new JsonObjectParser(JSON_FACTORY));
});
GitHubUrl url = new GitHubUrl("https://api.github.com/users");
url.per_page = 10;
HttpRequest request = requestFactory.buildGetRequest(url);
Type type = new TypeToken<List<User>>() {}.getType();
List<User> users = (List<User>)request
.execute()
.parseAs(type);
}
请注意我们如何指定 API 调用需要多少用户,然后我们使用HttpRequestFactory构建请求。
在此之后,由于 GitHub API 的响应包含用户列表,我们需要提供一个复杂的Type,即List<User>*。
然后,在最后一行,我们进行调用并将响应解析为我们的User类列表。
7. 自定义标题
我们在发出 API 请求时通常会做的一件事是包含某种自定义标头,甚至是修改后的标头:
HttpHeaders headers = request.getHeaders();
headers.setUserAgent("Blogdemo Client");
headers.set("Time-Zone", "Europe/Amsterdam");
我们通过在创建请求之后但在执行它并添加必要的值之前获取HttpHeaders来做到这一点。
请注意,Google Http 客户端**包含一些标头作为特殊方法。**例如User-Agent标头,如果我们尝试仅使用 set 方法包含它,则会引发错误。
8. 指数退避
Google Http 客户端的另一个重要特性是可以根据某些状态代码和阈值重试请求。
我们可以在创建请求对象后立即包含我们的指数退避设置:
ExponentialBackOff backoff = new ExponentialBackOff.Builder()
.setInitialIntervalMillis(500)
.setMaxElapsedTimeMillis(900000)
.setMaxIntervalMillis(6000)
.setMultiplier(1.5)
.setRandomizationFactor(0.5)
.build();
request.setUnsuccessfulResponseHandler(
new HttpBackOffUnsuccessfulResponseHandler(backoff));
在HttpRequest中指数退避默认是关闭的,所以我们必须在 HttpRequest 中包含一个 HttpUnsuccessfulResponseHandler 的实例来激活它。
9. 记录
Google Http 客户端使用java.util.logging.Logger记录 HTTP 请求和响应详细信息,包括 URL、标头和内容。
通常,使用logging.properties文件管理日志记录:
handlers = java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level = ALL
com.google.api.client.http.level = ALL
在我们的示例中,我们使用ConsoleHandler,但也可以选择FileHandler。
属性文件配置 JDK 日志记录工具的操作。可以将此配置文件指定为系统属性:
-Djava.util.logging.config.file=logging.properties
所以设置文件和系统属性后,库会产生如下日志:
GET https://api.github.com/users?page=1&per_page=10
Accept-Encoding: gzip
User-Agent: Google-HTTP-Java-Client/1.23.0 (gzip)
Nov 12, 2017 6:43:15 PM com.google.api.client.http.HttpRequest execute
curl -v --compressed -H 'Accept-Encoding: gzip' -H 'User-Agent: Google-HTTP-Java-Client/1.23.0 (gzip)' -- 'https://api.github.com/users?page=1&per_page=10'
## Nov 12, 2017 6:43:16 PM com.google.api.client.http.HttpResponse
HTTP/1.1 200 OK
Status: 200 OK
Transfer-Encoding: chunked
Server: GitHub.com
Access-Control-Allow-Origin: *
...
Link: <https://api.github.com/users?page=1&per_page=10&since=19>; rel="next", <https://api.github.com/users{?since}>; rel="first"
X-GitHub-Request-Id: 8D6A:1B54F:3377D97:3E37B36:5A08DC93
Content-Type: application/json; charset=utf-8
...