版本: unirest-java-3.12.0

翻译: 白石(https://github.com/wjw465150)

HTTP请求大家都比较熟悉了,无论是使用JDK自带的HttpURLConnection抑或是apache的HttpClients或者是OkHttp,都能比较轻松便捷的发送HTTP请求,区别只是在于代码量的多少了。今天主要和大家说了Unirest-Java这个基于apache HttpComponent的工具,api更为便捷、高效,能够使用较少的代码完成复杂的请求. 官网地址为:[https://kong.github.io/unirest-java/](https://kong.github.io/unirest-java/),而且可以看到,除了Java的api,还提供了Python、PHP、Nodejs等不同语言的支持。顺带说一句,Unirest-Java的github地址为[https://github.com/kong/unirest-java/](https://github.com/kong/unirest-java/),其另外一个很牛逼的开源产品就是微服务的API网关kong

Maven: 安装

  1. <!-- Pull in as a traditional dependency -->
  2. <dependency>
  3. <groupId>com.konghq</groupId>
  4. <artifactId>unirest-java</artifactId>
  5. <version>3.12.0</version>
  6. </dependency>
  7. <!-- OR as a snazzy new standalone jar with shaded dependencies -->
  8. <dependency>
  9. <groupId>com.konghq</groupId>
  10. <artifactId>unirest-java</artifactId>
  11. <version>3.12.0</version>
  12. <classifier>standalone</classifier>
  13. </dependency>

Gradle: 安装

  1. // Make sure you added the mavencentral repository
  2. repositories {
  3. mavenCentral()
  4. }
  5. // Pull in as a traditional dependency
  6. dependencies {
  7. // ... Your other dependencies
  8. implementation 'com.konghq:unirest-java:3.11.09'
  9. }
  10. // OR as a snazzy new standalone
  11. dependencies {
  12. // ... Your other dependencies
  13. implementation 'com.konghq:unirest-java:3.11.09:standalone'
  14. }

从以前的版本升级

参见 升级指导书

更新日志

参见 Change Log 查看最近的更改。

Requests(请求)

所以你可能想知道如何使用Unirest在Java中创建请求更容易,这里是一个基本的POST请求,它将解释一切:

  1. HttpResponse<JsonNode> response = Unirest.post("http://localhost/post")
  2. .header("accept", "application/json")
  3. .queryString("apiKey", "123")
  4. .field("parameter", "value")
  5. .field("firstname", "Gary")
  6. .asJson();

当’ asType ‘被调用时发出请求,可能的类型包括JsonStringObjectEmptyFile

路由(路径)参数

有时您想在 URL 中添加动态参数,可以通过在 URL 中添加占位符,然后使用 routeParam 函数设置路由参数来轻松实现,例如:

  1. Unirest.get("http://localhost/{fruit}")
  2. .routeParam("fruit", "apple")
  3. .asString();
  4. // Results in `http://localhost/apple`

占位符 {fruit} 将被替换为 apple

占位符的格式就像用花括号括起来一样简单:{custom_name}

所有参数值都将为您进行 URL 编码

默认的基础URL

您可以配置一个默认基础URL,用于所有不包含完整URL的请求。

这个配置将导致GET到 “http://homestar.com/runner

  1. Unirest.config().defaultBaseUrl("http://homestar.com");
  2. Unirest.get("/runner").asString();

查询参数

查询字符串参数可以一个一个地建立

  1. Unirest.get("http://localhost")
  2. .queryString("fruit", "apple")
  3. .queryString("droid", "R2D2")
  4. .asString();
  5. // Results in "http://localhost?fruit=apple&droid=R2D2"

同样,所有的参数值都是url编码的。

你也可以将查询字符串作为数组和map传入:

  1. Unirest.get("http://localhost")
  2. .queryString("fruit", Arrays.asList("apple", "orange"))
  3. .queryString(ImmutableMap.of("droid", "R2D2", "beatle", "Ringo"))
  4. .asString();
  5. // Results in "http://localhost?fruit=apple&fruit=orange&droid=R2D2&beatle=Ringo"

请求头

请求头可以通过header‘方法添加。

  1. Unirest.get("http://localhost")
  2. .header("Accept", "application/json")
  3. .header("x-custom-header", "hello")
  4. .asString();

Basic 认证

Unirest 提供了在需要时进行基本身份验证的快捷方式。 Unirest 处理 Base64 编码部分。 请确保您始终通过 HTTPS 执行此操作!

  1. Unirest.get("http://localhost")
  2. .basicAuth("user", "password1!")
  3. .asString();
  4. //this adds the header "Authorization: Basic dXNlcjpwYXNzd29yZDEh"

Body Data(消息体数据)

Entity Bodies(实体对象Bodies)

您可以轻松地将实体对象作为body发布。 这是大多数 REST 服务的默认行为。

除非你另外指定,默认的Content-Typetext/plain; charset=UTF-8

  1. Unirest.post("http://localhost")
  2. .body("This is the entire body")
  3. .asEmpty();

您还可以发布为使用配置的 ObjectMapper 序列化的对象。 (请参阅 Object Mappers了解实现细节)。Unirest 带有一个默认映射器,它将使用流行的 Google Gson 库序列化为 json

  1. Unirest.post("http://localhost")
  2. .header("Content-Type", "application/json")
  3. .body(new SomeUserObject("Bob"))
  4. .asEmpty();
  5. // This will use Jackson to serialize the object into JSON.

JSON Patch Bodies(JSON补丁Bodies)

Unirest 完全支持 JSON 补丁请求(RFC-6902 see http://jsonpatch.com/))规范,json-patch 的默认 Content-Typeapplication/json-patch+json

  1. Unirest.jsonPatch("http://localhost")
  2. .add("/fruits/-", "Apple")
  3. .remove("/bugs")
  4. .replace("/lastname", "Flintstone")
  5. .test("/firstname", "Fred")
  6. .move("/old/location", "/new/location")
  7. .copy("/original/location", "/new/location")
  8. .asJson();

将发送如下带有正文的请求

  1. [
  2. {"op":"add","path":"/fruits/-","value":"Apple"},
  3. {"op":"remove","path":"/bugs"},
  4. {"op":"replace","path":"/lastname","value":"Flintstone"},
  5. {"op":"test","path":"/firstname","value":"Fred"},
  6. {"op":"move","path":"/new/location","from":"/old/location"},
  7. {"op":"copy","path":"/new/location","from":"/original/location"}
  8. ]

Basic Forms(基本表单)

可以通过简单的field调用传递基本的 http 名称值(name value) body 参数。 此类请求的Content-Type默认为application/x-www-form-urlencoded

  1. Unirest.post("http://localhost")
  2. .field("fruit", "apple")
  3. .field("droid", "R2D2")
  4. .asEmpty();
  5. // This will post a simple name-value pair body the same as a HTML form. This looks like
  6. // `fruit=apple&droid=R2D2'

文件上传

您还可以在表单中发布二进制数据。 例如一个文件。

此类请求的 Content-Type 默认为 multipart/form-data

  1. Unirest.post("http://localhost")
  2. .field("upload", new File("/MyFile.zip"))
  3. .asEmpty();

对于大文件,您可能需要使用 InputStream。 如果需要,请传递一个文件名。 我们在这里使用 FileInputStream 但它实际上可以是任何类型的 InputStream子类。

  1. InputStream file = new FileInputStream(new File("/MyFile.zip"));
  2. Unirest.post("http://localhost")
  3. .field("upload", file, "MyFile.zip")
  4. .asEmpty();

上传进度监控

如果您要上传大文件,您可能希望向用户提供一些时间进度条。 您可以通过提供 ProgresMonitor 来监控此进度。

  1. Unirest.post("http://localhost")
  2. .field("upload", new File("/MyFile.zip"))
  3. .uploadMonitor((field, fileName, bytesWritten, totalBytes) -> {
  4. updateProgressBarWithBytesLeft(totalBytes - bytesWritten);
  5. })
  6. .asEmpty();

异步请求

有时候,在大多数情况下,您希望应用程序是异步的而不是阻塞的,Unirest在Java中使用匿名回调或直接方法放置来支持这一点。所有请求类型也支持异步版本。

  1. CompletableFuture<HttpResponse<JsonNode>> future = Unirest.post("http://localhost/post")
  2. .header("accept", "application/json")
  3. .field("param1", "value1")
  4. .field("param2", "value2")
  5. .asJsonAsync(response -> {
  6. int code = response.getStatus();
  7. JsonNode body = response.getBody();
  8. });

分页请求

有时服务提供分页请求。 这是如何完成的并没有标准化,但 Unirest 证明了一种机制,可以跟踪页面直到所有页面都被消耗掉。 您必须提供两个函数来提取下一页。 第一个是获取您想要的格式的 HttpResponse,另一个是从响应中提取 next 链接。结果是HttpResponse<T>PagedList。 分页列表有一些处理结果的方便方法。 在这里,我们得到了一个分页的 Dogs 列表,其中 next 链接位于Header中。

  1. PagedList<Doggos> result = Unirest.get("https://somewhere/dogs")
  2. .asPaged(
  3. r -> r.asObject(Doggos.class),
  4. r -> r.getHeaders().getFirst("nextPage")
  5. );

客户端证书

如果您需要使用自定义客户端证书来调用服务,您可以为 unirest 提供自定义密钥库。 您可以传递 KeyStore 对象或有效 PKCS#12 密钥库文件的路径。

  1. Unirest.config()
  2. .clientCertificateStore("/path/mykeystore.p12", "password1!");
  3. Unirest.get("https://some.custom.secured.place.com")
  4. .asString();

Proxies(代理)

有时您需要通过代理隧道。Unirest可以配置为这样做。注意,身份验证代理不能按每个请求进行配置,除非您想将其构建到URL本身中。

  1. // Configure with authentication:
  2. Unirest.config().proxy("proxy.com", 7777, "username", "password1!");
  3. // or without
  4. Unirest.config().proxy("proxy.com", 7777);
  5. // or pass it in the request. This will override any proxy done in the config
  6. // currently only unauthenticated proxies work
  7. Unirest.get(MockServer.GET)
  8. .proxy("proxy.com", 7777)
  9. .asString();

Responses(响应)

Unirest 在您调用它的 as[type] 方法时发出实际请求。 这些方法还通知 Unirest 将响应映射到什么类型。 选项有EmptyStringFileObjectbyteJson

响应返回一个HttpResponse<T>,其中HttpResponse对象拥有所有常见的响应数据,如状态和头信息。可以通过.getbody()方法访问Body(如果存在)。

Empty Responses(空响应)

如果你不关心响应回来的消息体,asEmpty 是最简单的选择。 您仍然会得到状态和标题等响应信息。

  1. HttpResponse response = Unirest.delete("http://localhost").asEmpty()

String Responses(字符串响应)

下一个最简单的响应类型是String。你想怎么处理都可以。

  1. String body = Unirest.get("http://localhost")
  2. .asString()
  3. .getBody();

Object Mapped Responses(对象映射响应)

大多数情况下,在使用 RESTful 服务时,您可能希望将响应映射到一个对象中。

为此,您需要为 Unirest 配置提供“ObjectMapper”的实现(有关详细信息,请参阅 Object Mappers)。

如果响应是 JSON,那么你很幸运,Unirest 在 Google GSON 上提供了一个基本的JsonObjectMapper

asObject(Class) 之前,有必要提供 ObjectMapper 接口的自定义实现(如果您不想使用默认映射器)。 这应该只在第一次完成,因为 ObjectMapper 的实例将被全局共享。

Unirest 提供了一些插件来实现流行的对象映射器,如 Jackson 和 Gson。 详情请参阅 mvn central

例如,

  1. // Response to Object
  2. Book book = Unirest.get("http://localhost/books/1")
  3. .asObject(Book.class)
  4. .getBody();
  5. // Generic types can be resolved by using a GenericType subclass to avoid erasure
  6. List<Book> books = Unirest.get("http://localhost/books/")
  7. .asObject(new GenericType<List<Book>>(){})
  8. .getBody();
  9. Author author = Unirest.get("http://localhost/books/{id}/author")
  10. .routeParam("id", bookObject.getId())
  11. .asObject(Author.class)
  12. .getBody();

对象或JSON解析出错时

你不能总是得到你想要的。 有时您从 Web 服务获得的结果不会映射到您期望的结果。 当asObjectasJson 请求发生这种情况时,结果主体将为空,但响应对象将包含一个 ParsingException,允许您获取错误和原始主体以供检查。

  1. UnirestParsingException ex = response.getParsingError().get();
  2. ex.getOriginalBody(); // Has the original body as a string.
  3. ex.getMessage(); // Will have the parsing exception.
  4. ex.getCause(); // of course will have the original parsing exception itself.

映射Erro对象

有时使用 REST API 的服务会返回一个可以解析的错误对象。 您可以选择将其映射到 POJO 中,例如

  1. HttpResponse<Book> book = Unirest.get("http://localhost/books/{id}")
  2. .asObject(Book.class);
  3. // This will be null if there wasn't an error
  4. Error er = book.mapError(Error.class);
  5. // You can also take advantage of this inside of the ifFailure method
  6. Unirest.get("http://localhost/books/{id}")
  7. .asObject(Book.class)
  8. .ifFailure(Error.class, r -> {
  9. Error e = r.getBody();
  10. });

不使用对象映射器将返回的Body映射成一种类型

如果您不想提供完整的 ObjectMapper 实现,您可以使用一个简单的函数来映射响应

  1. int body = Unirest.get("http://httpbin/count")
  2. .asString()
  3. .mapBody(Integer::valueOf);

File Responses(文件响应)

有时您只是想下载一个文件,或者捕获响应主体到一个文件中。Unirest可以做到这两点。告诉Unirest你想把文件放在哪里。

  1. File result = Unirest.get("http://some.file.location/file.zip")
  2. .asFile("/disk/location/file.zip")
  3. .getBody();

下载进度监控

如果您要上传大文件,您可能希望向用户提供一些时间进度条。 您可以通过提供 ProgresMonitor 来监控此进度。

  1. Unirest.get("http://localhost")
  2. .downLoadMonitor((b, fileName, bytesWritten, totalBytes) -> {
  3. updateProgressBarWithBytesLeft(totalBytes - bytesWritten);
  4. })
  5. .asFile("/disk/location/file.zip");

JSON responses(JSON 响应)

当您不需要完整的对象映射器时,Unirest 提供了一种轻量级的 JSON 响应类型。

  1. String result = Unirest.get("http://some.json.com")
  2. .asJson()
  3. .getBody()
  4. .getObject()
  5. .getJSONObject("car")
  6. .getJSONArray("wheels")
  7. .get(0)

Large Responses(大响应)

一些响应方法(asStringasJson)将整个响应流读入内存。 为了读取原始流并处理大响应,您可以使用多种函数方法,例如:

  1. Map r = Unirest.get(MockServer.GET)
  2. .queryString("firstname", "Gary")
  3. .asObject(i -> new Gson().fromJson(i.getContentReader(), HashMap.class))
  4. .getBody();

或消费者:

  1. Unirest.get(MockServer.GET)
  2. .thenConsumeAsync(r -> {
  3. // something like writing a file to disk
  4. });

错误处理

HttpResponse对象有一些处理方法,可以通过链接来处理成功和失败:

  • ifSuccess(Consumer<HttpResponse<T>> response) 将被调用,如果响应是一个200系列的响应,并且任何主体处理(如JsonObject是成功的。
  • ifFailure(Consumer<HttpResponse> response 将在状态为400+或主体处理失败时被调用。

将它们放在一起可能如下所示:

  1. Unirest.get("http://somewhere")
  2. .asJson()
  3. .ifSuccess(response -> someSuccessMethod(response))
  4. .ifFailure(response -> {
  5. log.error("Oh No! Status" + response.getStatus());
  6. response.getParsingError().ifPresent(e -> {
  7. log.error("Parsing Exception: ", e);
  8. log.error("Original body: " + e.getOriginalBody());
  9. });
  10. });

缓存

Unirest 提供了一个简单的即时内存响应缓存机制,其中包含一些条目过期选项。 这可以使用默认值、过期选项启用,或者消费者可以提供由他们选择的缓存支持的自定义缓存。 建议在高负载系统中,消费者使用专用缓存实现(如 EHCache 或 Guava)支持缓存。

Basic cache:

  1. Unirest.config().cacheResponses(true);
  2. //These 1st response will be cached in this case:
  3. Unirest.get("https://somwhere").asString();
  4. Unirest.get("https://somwhere").asString();

高级选项:

您可以使用构建器来自定义驱逐规则:

  1. Unirest.config().cacheResponses(builder()
  2. .depth(5) // Depth is the max number of entries cached
  3. .maxAge(5, TimeUnit.MINUTES)); // Max age is how long the entry will be kept.

自定义缓存

您还可以通过实现缓存接口来提供自定义缓存

  1. public static void main(String[] args){
  2. Unirest.config().cacheResponses(Cache.builder().backingCache(new GuavaCache()));
  3. }
  4. // Example backing cache using Guava
  5. public static class GuavaCache implements Cache {
  6. com.google.common.cache.Cache<Key, HttpResponse> regular = CacheBuilder.newBuilder().build();
  7. com.google.common.cache.Cache<Key, CompletableFuture> async = CacheBuilder.newBuilder().build();
  8. @Override
  9. public <T> HttpResponse get(Key key, Supplier<HttpResponse<T>> fetcher) {
  10. try {
  11. return regular.get(key, fetcher::get);
  12. } catch (ExecutionException e) {
  13. throw new RuntimeException(e);
  14. }
  15. }
  16. @Override
  17. public <T> CompletableFuture getAsync(Key key, Supplier<CompletableFuture<HttpResponse<T>>> fetcher) {
  18. try {
  19. return async.get(key, fetcher::get);
  20. } catch (ExecutionException e) {
  21. throw new RuntimeException(e);
  22. }
  23. }
  24. }

Configuration(配置)

以前版本的unirest配置分散在几个不同的地方。有时在Unirest上完成,有时在Option上完成,有时在其他地方完成。所有配置现在都通过Unirest.config()完成

  1. Unirest.config()
  2. .socketTimeout(500)
  3. .connectTimeout(1000)
  4. .concurrency(10, 5)
  5. .proxy(new Proxy("https://proxy"))
  6. .setDefaultHeader("Accept", "application/json")
  7. .followRedirects(false)
  8. .enableCookieManagement(false)
  9. .addInterceptor(new MyCustomInterceptor());

理想情况下,更改 Unirest 的配置应该完成一次,或者很少进行。 Unirest 本身和 Apache HttpAsyncClient 产生了几个后台线程。 一旦 Unirest 被激活,在没有明确关闭或重置的情况下,创建客户端所涉及的配置选项无法更改。

配置选项

Builder 方法 描述 缺省值
socketTimeout(int) 以毫秒为单位设置所有请求的套接字超时 60000
connectTimeout(int) 以毫秒为单位设置所有请求的连接超时 10000
concurrency(int, int) 设置并发率; 最大总数,每条路线的最大数量 200, 20
proxy(proxy) 设置用于协商代理服务器的代理对象。 可以包括身份验证凭据
setDefaultHeader(String, String) 设置默认头。如果存在,将覆盖
setDefaultHeader(String, Supplier<String>) 按供应商设置默认头。 适合为微服务架构设置跟踪令牌。 如果存在,将覆盖
addDefaultHeader(String, String) 添加默认头。可以存在同名的多个
addDefaultHeader(String, Supplier<String>) 根据供应商添加一个默认头。适合于为微服务体系结构设置跟踪令牌。
setDefaultBasicAuth(String, String) 添加一个默认的Basic认证头
followRedirects(boolean) 是否允许重定向 true
enableCookieManagement(boolean) 是否接受和存储 cookie true
cookieSpec(String) 设置 cookie 策略。 可接受的值:default(与 Netscape 相同)、 netscapeignoreCookiesstandard(RFC 6265 互操作性配置文件)、standard-strict(RFC 6265 严格配置文件) default
automaticRetries(boolean) 是否允许套接字超时的自动重试(最多 4 次) true
verifySsl(boolean) 是否校验SSL true
addShutdownHook(boolean) 是否自动将客户端添加到系统关闭钩子中 false
clientCertificateStore(String,String) 按路径添加 PKCS12 KeyStore 以执行客户端证书
clientCertificateStore(KeyStore,String) 添加一个 PKCS12 KeyStore 来做客户端证书
connectionTTL(long,TimeUnit) 总生存时间 (TTL) 定义了持久连接的最大生命周期,而不管它们的到期设置如何。 超过其 TTL 值的持久连接将不会被重新使用。 -1
connectionTTL(Duration) 通过 Duration 添加总生存时间 (TTL)。 适用于现代 Java API。 -1
errorHandler(Consumer<HttpResponse<?>> consumer) 设置一个全局错误处理程序,该处理程序将在任何状态 > 400 或解析错误时调用
interceptor(Interceptor value) 设置一个全局的Interceptor处理程序,在每个请求之前和之后调用它
hostNameVerifier(HostNameVerifier value) 为安全配置设置一个自定义的HostNameVerifier DefaultHostNameVerifier
defaultBaseUrl(String value) 设置一个默认的基本 URL,用于所有尚未包含scheme的请求

Global Interceptor(全局拦截器)

您可以为您的配置设置一个全局拦截器。 这是在每个请求之前和之后调用的。 这对于记录或注入公共属性很有用。

有关详细信息,请参阅 Interceptor.java

自定义Apache Clients

Unirest 在幕后利用 Apache Http Client,这不被认为是永久性要求,Unirest 的未来版本可能会用其他东西代替 Apache。

您可以设置自己的自定义 Apache HttpClient 和 HttpAsyncClient。 请注意,诸如超时或拦截器之类的 Unirest 设置不适用于自定义客户端。

  1. Unirest.config()
  2. .httpClient(ApacheClient.builder(myClient))
  3. .asyncClient(ApacheAsyncClient.builder(myAsyncClient));

您还可以覆盖 Unirest 对 Apache 请求配置的实现

  1. Unirest.config()
  2. .httpClient(ApacheClient.builder(client)
  3. .withRequestConfig((c,r) -> RequestConfig.custom().build()
  4. );

Multiple Configurations

像往常一样,Unirest 维护一个主要的单个实例。 有时您可能需要针对不同系统的不同配置。 出于测试目的,您可能还需要一个实例而不是静态上下文。

  1. // this returns the same instance used by Unirest.get("http://somewhere/")
  2. UnirestInstance unirest = Unirest.primaryInstance();
  3. // It can be configured and used just like the static context
  4. unirest.config().connectTimeout(5000);
  5. String result = unirest.get("http://foo").asString().getBody();
  6. // You can also get a whole new instance
  7. UnirestInstance unirest = Unirest.spawnInstance();

☢警告: 如果您获得了unirest的一个新实例,则您有责任在JVM关闭时关闭它。它没有被’ Unirest.shutDown(); ‘跟踪或关闭

Object Mappers(对象映射器)

Unirest 提供了一些基于流行的 JSON 库(Jackson 和 GSON)的不同对象映射器。 这些可以作为传统或shaded jars包括在内:

  1. <!-- https://mvnrepository.com/artifact/com.konghq/unirest-objectmapper-jackson -->
  2. <dependency>
  3. <groupId>com.konghq</groupId>
  4. <artifactId>unirest-objectmapper-jackson</artifactId>
  5. <version>3.11.09</version>
  6. </dependency>
  7. <!-- https://mvnrepository.com/artifact/com.konghq/unirest-object-mappers-gson -->
  8. <dependency>
  9. <groupId>com.konghq</groupId>
  10. <artifactId>unirest-object-mappers-gson</artifactId>
  11. <version>3.11.09</version>
  12. </dependency>

如果您有其他需要,您可以通过实现ObjectMapper 接口来提供您自己的对象映射器。 它只有几个方法

Metrics(指标)

Unirest 具有用于收集运行时代码指标的钩子。 这是一个简单的轻量级框架,标记了两个事件:

  1. 发出实际请求之前的那一刻
  2. 发出实际请求后的那一刻

为您提供了方法和请求路径等上下文信息,以便您可以根据需要进行收集。 最简单的形式可能是这样的:

  1. Unirest.config().instrumentWith(requestSummary -> {
  2. long startNanos = System.nanoTime();
  3. return (responseSummary,exception) -> logger.info("path: {} status: {} time: {}",
  4. requestSummary.getRawPath(),
  5. responseSummary.getStatus(),
  6. System.nanoTime() - startNanos);
  7. });

通过提供更多功能丰富的 UniMetric 实例,您可以轻松计算每条 路线(route) 的平均值、正常运行时间或其他有趣的事实。

Shutting Down(关闭)

Unirest启动一个后台事件循环,你的Java应用程序将无法退出,直到你手动关闭所有线程调用:

  1. Unirest.shutdown();

一旦关闭,再次使用 Unirest 将重新初始化系统