版本: 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: 安装
<!-- Pull in as a traditional dependency -->
<dependency>
<groupId>com.konghq</groupId>
<artifactId>unirest-java</artifactId>
<version>3.12.0</version>
</dependency>
<!-- OR as a snazzy new standalone jar with shaded dependencies -->
<dependency>
<groupId>com.konghq</groupId>
<artifactId>unirest-java</artifactId>
<version>3.12.0</version>
<classifier>standalone</classifier>
</dependency>
用 Gradle: 安装
// Make sure you added the mavencentral repository
repositories {
mavenCentral()
}
// Pull in as a traditional dependency
dependencies {
// ... Your other dependencies
implementation 'com.konghq:unirest-java:3.11.09'
}
// OR as a snazzy new standalone
dependencies {
// ... Your other dependencies
implementation 'com.konghq:unirest-java:3.11.09:standalone'
}
从以前的版本升级
参见 升级指导书
更新日志
参见 Change Log 查看最近的更改。
Requests(请求)
所以你可能想知道如何使用Unirest在Java中创建请求更容易,这里是一个基本的POST请求,它将解释一切:
HttpResponse<JsonNode> response = Unirest.post("http://localhost/post")
.header("accept", "application/json")
.queryString("apiKey", "123")
.field("parameter", "value")
.field("firstname", "Gary")
.asJson();
当’ asType ‘被调用时发出请求,可能的类型包括Json
, String
, Object
, Empty
和File
。
路由(路径)参数
有时您想在 URL 中添加动态参数,可以通过在 URL 中添加占位符,然后使用 routeParam
函数设置路由参数来轻松实现,例如:
Unirest.get("http://localhost/{fruit}")
.routeParam("fruit", "apple")
.asString();
// Results in `http://localhost/apple`
占位符 {fruit}
将被替换为 apple
。
占位符的格式就像用花括号括起来一样简单:{custom_name}
所有参数值都将为您进行 URL 编码
默认的基础URL
您可以配置一个默认基础URL,用于所有不包含完整URL的请求。
这个配置将导致GET到 “http://homestar.com/runner”
Unirest.config().defaultBaseUrl("http://homestar.com");
Unirest.get("/runner").asString();
查询参数
查询字符串参数可以一个一个地建立
Unirest.get("http://localhost")
.queryString("fruit", "apple")
.queryString("droid", "R2D2")
.asString();
// Results in "http://localhost?fruit=apple&droid=R2D2"
同样,所有的参数值都是url编码的。
你也可以将查询字符串作为数组和map传入:
Unirest.get("http://localhost")
.queryString("fruit", Arrays.asList("apple", "orange"))
.queryString(ImmutableMap.of("droid", "R2D2", "beatle", "Ringo"))
.asString();
// Results in "http://localhost?fruit=apple&fruit=orange&droid=R2D2&beatle=Ringo"
请求头
请求头可以通过header
‘方法添加。
Unirest.get("http://localhost")
.header("Accept", "application/json")
.header("x-custom-header", "hello")
.asString();
Basic 认证
Unirest 提供了在需要时进行基本身份验证的快捷方式。 Unirest 处理 Base64 编码部分。 请确保您始终通过 HTTPS 执行此操作!
Unirest.get("http://localhost")
.basicAuth("user", "password1!")
.asString();
//this adds the header "Authorization: Basic dXNlcjpwYXNzd29yZDEh"
Body Data(消息体数据)
Entity Bodies(实体对象Bodies)
您可以轻松地将实体对象作为body发布。 这是大多数 REST 服务的默认行为。
除非你另外指定,默认的Content-Type
是 text/plain; charset=UTF-8
Unirest.post("http://localhost")
.body("This is the entire body")
.asEmpty();
您还可以发布为使用配置的 ObjectMapper 序列化的对象。 (请参阅 Object Mappers了解实现细节)。Unirest 带有一个默认映射器,它将使用流行的 Google Gson 库序列化为 json
Unirest.post("http://localhost")
.header("Content-Type", "application/json")
.body(new SomeUserObject("Bob"))
.asEmpty();
// 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-Type
是 application/json-patch+json
Unirest.jsonPatch("http://localhost")
.add("/fruits/-", "Apple")
.remove("/bugs")
.replace("/lastname", "Flintstone")
.test("/firstname", "Fred")
.move("/old/location", "/new/location")
.copy("/original/location", "/new/location")
.asJson();
将发送如下带有正文的请求
[
{"op":"add","path":"/fruits/-","value":"Apple"},
{"op":"remove","path":"/bugs"},
{"op":"replace","path":"/lastname","value":"Flintstone"},
{"op":"test","path":"/firstname","value":"Fred"},
{"op":"move","path":"/new/location","from":"/old/location"},
{"op":"copy","path":"/new/location","from":"/original/location"}
]
Basic Forms(基本表单)
可以通过简单的field
调用传递基本的 http 名称值(name value) body 参数。 此类请求的Content-Type
默认为application/x-www-form-urlencoded
Unirest.post("http://localhost")
.field("fruit", "apple")
.field("droid", "R2D2")
.asEmpty();
// This will post a simple name-value pair body the same as a HTML form. This looks like
// `fruit=apple&droid=R2D2'
文件上传
您还可以在表单中发布二进制数据。 例如一个文件。
此类请求的 Content-Type
默认为 multipart/form-data
Unirest.post("http://localhost")
.field("upload", new File("/MyFile.zip"))
.asEmpty();
对于大文件,您可能需要使用 InputStream。 如果需要,请传递一个文件名。 我们在这里使用 FileInputStream 但它实际上可以是任何类型的 InputStream子类。
InputStream file = new FileInputStream(new File("/MyFile.zip"));
Unirest.post("http://localhost")
.field("upload", file, "MyFile.zip")
.asEmpty();
上传进度监控
如果您要上传大文件,您可能希望向用户提供一些时间进度条。 您可以通过提供 ProgresMonitor 来监控此进度。
Unirest.post("http://localhost")
.field("upload", new File("/MyFile.zip"))
.uploadMonitor((field, fileName, bytesWritten, totalBytes) -> {
updateProgressBarWithBytesLeft(totalBytes - bytesWritten);
})
.asEmpty();
异步请求
有时候,在大多数情况下,您希望应用程序是异步的而不是阻塞的,Unirest在Java中使用匿名回调或直接方法放置来支持这一点。所有请求类型也支持异步版本。
CompletableFuture<HttpResponse<JsonNode>> future = Unirest.post("http://localhost/post")
.header("accept", "application/json")
.field("param1", "value1")
.field("param2", "value2")
.asJsonAsync(response -> {
int code = response.getStatus();
JsonNode body = response.getBody();
});
分页请求
有时服务提供分页请求。 这是如何完成的并没有标准化,但 Unirest 证明了一种机制,可以跟踪页面直到所有页面都被消耗掉。 您必须提供两个函数来提取下一页。 第一个是获取您想要的格式的 HttpResponse
,另一个是从响应中提取 next
链接。结果是HttpResponse<T>
的PagedList
。 分页列表有一些处理结果的方便方法。 在这里,我们得到了一个分页的 Dogs 列表,其中 next
链接位于Header中。
PagedList<Doggos> result = Unirest.get("https://somewhere/dogs")
.asPaged(
r -> r.asObject(Doggos.class),
r -> r.getHeaders().getFirst("nextPage")
);
客户端证书
如果您需要使用自定义客户端证书来调用服务,您可以为 unirest 提供自定义密钥库。 您可以传递 KeyStore 对象或有效 PKCS#12 密钥库文件的路径。
Unirest.config()
.clientCertificateStore("/path/mykeystore.p12", "password1!");
Unirest.get("https://some.custom.secured.place.com")
.asString();
Proxies(代理)
有时您需要通过代理隧道。Unirest可以配置为这样做。注意,身份验证代理不能按每个请求进行配置,除非您想将其构建到URL本身中。
// Configure with authentication:
Unirest.config().proxy("proxy.com", 7777, "username", "password1!");
// or without
Unirest.config().proxy("proxy.com", 7777);
// or pass it in the request. This will override any proxy done in the config
// currently only unauthenticated proxies work
Unirest.get(MockServer.GET)
.proxy("proxy.com", 7777)
.asString();
Responses(响应)
Unirest 在您调用它的 as[type]
方法时发出实际请求。 这些方法还通知 Unirest 将响应映射到什么类型。 选项有Empty
、String
、File
、Object
、byte
和Json
。
响应返回一个HttpResponse<T>
,其中HttpResponse
对象拥有所有常见的响应数据,如状态和头信息。可以通过.getbody()
方法访问Body(如果存在)。
Empty Responses(空响应)
如果你不关心响应回来的消息体,asEmpty
是最简单的选择。 您仍然会得到状态和标题等响应信息。
HttpResponse response = Unirest.delete("http://localhost").asEmpty()
String Responses(字符串响应)
下一个最简单的响应类型是String。你想怎么处理都可以。
String body = Unirest.get("http://localhost")
.asString()
.getBody();
Object Mapped Responses(对象映射响应)
大多数情况下,在使用 RESTful 服务时,您可能希望将响应映射到一个对象中。
为此,您需要为 Unirest 配置提供“ObjectMapper”的实现(有关详细信息,请参阅 Object Mappers)。
如果响应是 JSON,那么你很幸运,Unirest 在 Google GSON 上提供了一个基本的JsonObjectMapper
在 asObject(Class)
之前,有必要提供 ObjectMapper
接口的自定义实现(如果您不想使用默认映射器)。 这应该只在第一次完成,因为 ObjectMapper 的实例将被全局共享。
Unirest 提供了一些插件来实现流行的对象映射器,如 Jackson 和 Gson。 详情请参阅 mvn central。
例如,
// Response to Object
Book book = Unirest.get("http://localhost/books/1")
.asObject(Book.class)
.getBody();
// Generic types can be resolved by using a GenericType subclass to avoid erasure
List<Book> books = Unirest.get("http://localhost/books/")
.asObject(new GenericType<List<Book>>(){})
.getBody();
Author author = Unirest.get("http://localhost/books/{id}/author")
.routeParam("id", bookObject.getId())
.asObject(Author.class)
.getBody();
对象或JSON解析出错时
你不能总是得到你想要的。 有时您从 Web 服务获得的结果不会映射到您期望的结果。 当asObject
或asJson
请求发生这种情况时,结果主体将为空,但响应对象将包含一个 ParsingException,允许您获取错误和原始主体以供检查。
UnirestParsingException ex = response.getParsingError().get();
ex.getOriginalBody(); // Has the original body as a string.
ex.getMessage(); // Will have the parsing exception.
ex.getCause(); // of course will have the original parsing exception itself.
映射Erro对象
有时使用 REST API 的服务会返回一个可以解析的错误对象。 您可以选择将其映射到 POJO 中,例如
HttpResponse<Book> book = Unirest.get("http://localhost/books/{id}")
.asObject(Book.class);
// This will be null if there wasn't an error
Error er = book.mapError(Error.class);
// You can also take advantage of this inside of the ifFailure method
Unirest.get("http://localhost/books/{id}")
.asObject(Book.class)
.ifFailure(Error.class, r -> {
Error e = r.getBody();
});
不使用对象映射器将返回的Body映射成一种类型
如果您不想提供完整的 ObjectMapper 实现,您可以使用一个简单的函数来映射响应
int body = Unirest.get("http://httpbin/count")
.asString()
.mapBody(Integer::valueOf);
File Responses(文件响应)
有时您只是想下载一个文件,或者捕获响应主体到一个文件中。Unirest可以做到这两点。告诉Unirest你想把文件放在哪里。
File result = Unirest.get("http://some.file.location/file.zip")
.asFile("/disk/location/file.zip")
.getBody();
下载进度监控
如果您要上传大文件,您可能希望向用户提供一些时间进度条。 您可以通过提供 ProgresMonitor 来监控此进度。
Unirest.get("http://localhost")
.downLoadMonitor((b, fileName, bytesWritten, totalBytes) -> {
updateProgressBarWithBytesLeft(totalBytes - bytesWritten);
})
.asFile("/disk/location/file.zip");
JSON responses(JSON 响应)
当您不需要完整的对象映射器时,Unirest 提供了一种轻量级的 JSON 响应类型。
String result = Unirest.get("http://some.json.com")
.asJson()
.getBody()
.getObject()
.getJSONObject("car")
.getJSONArray("wheels")
.get(0)
Large Responses(大响应)
一些响应方法(asString
、asJson
)将整个响应流读入内存。 为了读取原始流并处理大响应,您可以使用多种函数方法,例如:
Map r = Unirest.get(MockServer.GET)
.queryString("firstname", "Gary")
.asObject(i -> new Gson().fromJson(i.getContentReader(), HashMap.class))
.getBody();
或消费者:
Unirest.get(MockServer.GET)
.thenConsumeAsync(r -> {
// something like writing a file to disk
});
错误处理
HttpResponse对象有一些处理方法,可以通过链接来处理成功和失败:
ifSuccess(Consumer<HttpResponse<T>> response)
将被调用,如果响应是一个200系列的响应,并且任何主体处理(如Json
或Object
是成功的。ifFailure(Consumer<HttpResponse> response
将在状态为400+或主体处理失败时被调用。
将它们放在一起可能如下所示:
Unirest.get("http://somewhere")
.asJson()
.ifSuccess(response -> someSuccessMethod(response))
.ifFailure(response -> {
log.error("Oh No! Status" + response.getStatus());
response.getParsingError().ifPresent(e -> {
log.error("Parsing Exception: ", e);
log.error("Original body: " + e.getOriginalBody());
});
});
缓存
Unirest 提供了一个简单的即时内存响应缓存机制,其中包含一些条目过期选项。 这可以使用默认值、过期选项启用,或者消费者可以提供由他们选择的缓存支持的自定义缓存。 建议在高负载系统中,消费者使用专用缓存实现(如 EHCache 或 Guava)支持缓存。
Basic cache:
Unirest.config().cacheResponses(true);
//These 1st response will be cached in this case:
Unirest.get("https://somwhere").asString();
Unirest.get("https://somwhere").asString();
高级选项:
您可以使用构建器来自定义驱逐规则:
Unirest.config().cacheResponses(builder()
.depth(5) // Depth is the max number of entries cached
.maxAge(5, TimeUnit.MINUTES)); // Max age is how long the entry will be kept.
自定义缓存
您还可以通过实现缓存接口来提供自定义缓存
public static void main(String[] args){
Unirest.config().cacheResponses(Cache.builder().backingCache(new GuavaCache()));
}
// Example backing cache using Guava
public static class GuavaCache implements Cache {
com.google.common.cache.Cache<Key, HttpResponse> regular = CacheBuilder.newBuilder().build();
com.google.common.cache.Cache<Key, CompletableFuture> async = CacheBuilder.newBuilder().build();
@Override
public <T> HttpResponse get(Key key, Supplier<HttpResponse<T>> fetcher) {
try {
return regular.get(key, fetcher::get);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
@Override
public <T> CompletableFuture getAsync(Key key, Supplier<CompletableFuture<HttpResponse<T>>> fetcher) {
try {
return async.get(key, fetcher::get);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
}
Configuration(配置)
以前版本的unirest配置分散在几个不同的地方。有时在Unirest
上完成,有时在Option
上完成,有时在其他地方完成。所有配置现在都通过Unirest.config()
完成
Unirest.config()
.socketTimeout(500)
.connectTimeout(1000)
.concurrency(10, 5)
.proxy(new Proxy("https://proxy"))
.setDefaultHeader("Accept", "application/json")
.followRedirects(false)
.enableCookieManagement(false)
.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 相同)、 netscape 、ignoreCookies 、standard (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 设置不适用于自定义客户端。
Unirest.config()
.httpClient(ApacheClient.builder(myClient))
.asyncClient(ApacheAsyncClient.builder(myAsyncClient));
您还可以覆盖 Unirest 对 Apache 请求配置的实现
Unirest.config()
.httpClient(ApacheClient.builder(client)
.withRequestConfig((c,r) -> RequestConfig.custom().build()
);
Multiple Configurations
像往常一样,Unirest 维护一个主要的单个实例。 有时您可能需要针对不同系统的不同配置。 出于测试目的,您可能还需要一个实例而不是静态上下文。
// this returns the same instance used by Unirest.get("http://somewhere/")
UnirestInstance unirest = Unirest.primaryInstance();
// It can be configured and used just like the static context
unirest.config().connectTimeout(5000);
String result = unirest.get("http://foo").asString().getBody();
// You can also get a whole new instance
UnirestInstance unirest = Unirest.spawnInstance();
☢警告: 如果您获得了unirest的一个新实例,则您有责任在JVM关闭时关闭它。它没有被’ Unirest.shutDown(); ‘跟踪或关闭
Object Mappers(对象映射器)
Unirest 提供了一些基于流行的 JSON 库(Jackson 和 GSON)的不同对象映射器。 这些可以作为传统或shaded jars
包括在内:
<!-- https://mvnrepository.com/artifact/com.konghq/unirest-objectmapper-jackson -->
<dependency>
<groupId>com.konghq</groupId>
<artifactId>unirest-objectmapper-jackson</artifactId>
<version>3.11.09</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.konghq/unirest-object-mappers-gson -->
<dependency>
<groupId>com.konghq</groupId>
<artifactId>unirest-object-mappers-gson</artifactId>
<version>3.11.09</version>
</dependency>
如果您有其他需要,您可以通过实现ObjectMapper
接口来提供您自己的对象映射器。 它只有几个方法
Metrics(指标)
Unirest 具有用于收集运行时代码指标的钩子。 这是一个简单的轻量级框架,标记了两个事件:
- 发出实际请求之前的那一刻
- 发出实际请求后的那一刻
为您提供了方法和请求路径等上下文信息,以便您可以根据需要进行收集。 最简单的形式可能是这样的:
Unirest.config().instrumentWith(requestSummary -> {
long startNanos = System.nanoTime();
return (responseSummary,exception) -> logger.info("path: {} status: {} time: {}",
requestSummary.getRawPath(),
responseSummary.getStatus(),
System.nanoTime() - startNanos);
});
通过提供更多功能丰富的 UniMetric 实例,您可以轻松计算每条 路线(route) 的平均值、正常运行时间或其他有趣的事实。
Shutting Down(关闭)
Unirest启动一个后台事件循环,你的Java应用程序将无法退出,直到你手动关闭所有线程调用:
Unirest.shutdown();
一旦关闭,再次使用 Unirest 将重新初始化系统