簡述
如果您需要使用Java語言來請求HTTP資源肤视,那么你可能會(huì)遇到多種解決方案,你最終可能會(huì)以一種合理的方式來達(dá)成這個(gè)目的 —– 引用第三方包已卷。
好消息弧哎,好消息雁比,黃鶴帶著小姨子回來了,皮革廠有救了:Java9除了有模塊化特性之外撤嫩,還附帶了一個(gè)全新的HTTP客戶端API偎捎。不僅支持HTTP2.0,還提供了一套有親和力的API序攘。SO茴她,讓我們來剝掉小姨子神秘的蕾絲面紗。
HTTP2.0是啥子?xùn)|東程奠?
HTTP2.0帶來了令人手舞足蹈的新特性:(這里不來自原文)
- 二進(jìn)制分幀
- 請求/響應(yīng)管線化
- 異步連接
- 多路復(fù)用
- 服務(wù)器推送流(Server Push技術(shù))
- 基于TCP的長連接
- 首部壓縮
一句話描述:http/2最大的特點(diǎn)是使用多路復(fù)用丈牢,對(duì)同一個(gè)域的服務(wù)器只建立一次TCP連接,加載多個(gè)資源瞄沙,使用二進(jìn)制幀傳輸己沛,同時(shí)會(huì)對(duì)http頭部進(jìn)行壓縮。
就這樣距境,我們瞧上了豐滿的HTTP2.0申尼。
Incubator 模塊
這里需要注意的是,在Java9中HTTP客戶端的構(gòu)建需要依賴于一個(gè)Incubator模塊垫桂,and more:
- 在 JDK 9, 這個(gè)模塊叫做jdk.incubator.httpclient
- incubator模塊在JDK10將被java.httpclient所取代
- JDK10在這方面將迎來重大突破(現(xiàn)在誰知道师幕?)
Java 9的HTTP客戶端API
基本上,通過HTTP進(jìn)行通信時(shí)诬滩,會(huì)涉及到三個(gè)類:HttpClient將用于發(fā)送HttpRequest和接收HttpResponse霹粥。這API還是比較容易理解的灭将,right? Let’s see:
基本示例:GET 請求,返回字符串
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://labs.consol.de/"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandler.asString());
System.out.println(response.statusCode());
System.out.println(response.body());
很優(yōu)雅后控,對(duì)不對(duì)宗侦? 沒有InputStream和Reader被涉及, - 轉(zhuǎn)而是通過BodyHandler來直接從響應(yīng)中獲取字符串忆蚀。不多贅述,將在下文介紹BodyHandlers姑裂。
雖然HttpClient馋袜,HttpRequest并且HttpResponse是HTTP2.0通信中的主要參與者,但我們還是要與Builder配合使用舶斧。Builder提供了一套簡明易懂的API欣鳖。
HttpRequest.Builder
我們可以通過調(diào)用HttpRequest.newBuilder()來獲取一個(gè)HttpRequest.Builder實(shí)例,就像第一個(gè)示例中那樣茴厉。 我們將使用它來配置與特定請求相關(guān)的所有內(nèi)容泽台。下面是源代碼:
// HttpRequest.Builder
public abstract static class Builder {
// note: some methods left out for the sake of brevity
public abstract Builder uri(URI uri);
public abstract Builder version(HttpClient.Version version);
public abstract Builder header(String name, String value);
public abstract Builder timeout(Duration duration);
public abstract Builder GET();
public abstract Builder POST(BodyProcessor body);
public abstract Builder PUT(BodyProcessor body);
public abstract Builder DELETE(BodyProcessor body);
public abstract HttpRequest build();
}
可讀性很好(自解釋型), right? 采用鏈?zhǔn)椒椒ㄕ{(diào)用來完成請求內(nèi)容的配置矾缓,然后調(diào)用build()獲取HttpRequest實(shí)例怀酷。
HttpClient.Builder
與HttpRequest類似,我們調(diào)用HttpClient.newBuilder()來獲取HttpClient.Builder實(shí)例嗜闻。它提供了一個(gè)API來配置一些關(guān)于我們的連接的更通用的東西蜕依。 下面是源代碼:
// HttpClient.Builder
public abstract static class Builder {
public abstract Builder cookieManager(CookieManager cookieManager);
public abstract Builder sslContext(SSLContext sslContext);
public abstract Builder sslParameters(SSLParameters sslParameters);
public abstract Builder executor(Executor executor);
public abstract Builder followRedirects(Redirect policy);
public abstract Builder version(HttpClient.Version version);
public abstract Builder priority(int priority);
public abstract Builder proxy(ProxySelector selector);
public abstract Builder authenticator(Authenticator a);
public abstract HttpClient build();
}
真是通體雪白!琉雳!良好的自解釋性幫助IDE能夠更好地達(dá)成你的目的样眠。
更多Java 9 HTTP客戶端應(yīng)用示例
上面見識(shí)到了這套前景光明的API,下面讓我們來看看更多的關(guān)于它的應(yīng)用示例吧翠肘。
1. 保存GET請求到文件
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://labs.consol.de/"))
.GET()
.build();
Path tempFile = Files.createTempFile("consol-labs-home", ".html");
HttpResponse<Path> response = client.send(request, HttpResponse.BodyHandler.asFile(tempFile));
System.out.println(response.statusCode());
System.out.println(response.body());
2. 通過POST上傳文件
用POST上傳本地文件也變得十分簡單檐束,使用HttpRequest.BodyProcessor:
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("http://localhost:8080/upload/"))
.POST(HttpRequest.BodyProcessor.fromFile(Paths.get("/tmp/file-to-upload.txt")))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandler.discard(null));
System.out.println(response.statusCode());
3. 異步HTTP請求
異步HTTP請求也變得簡單,由HttpClient#sendAsync()代替HttpClient#send束倍。如果服務(wù)端支持被丧,你甚至可以取消執(zhí)行中的請求:
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://labs.consol.de/"))
.GET()
.build();
CompletableFuture<HttpResponse<String>> response = client.sendAsync(request, HttpResponse.BodyHandler.asString());
Thread.sleep(5000);
if(response.isDone()) {
System.out.println(response.get().statusCode());
System.out.println(response.get().body());
} else {
response.cancel(true);
System.out.println("Request took more than 5 seconds... cancelling.");
}
4. 使用系統(tǒng)代理設(shè)置
HttpClient client = HttpClient.newBuilder()
.proxy(ProxySelector.getDefault())
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://labs.consol.de"))
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandler.asString());
System.out.println(response.statusCode());
System.out.println(response.body());
5.基本認(rèn)證 Basic Authentication
HttpClient client = HttpClient.newBuilder()
.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("username", "password".toCharArray());
}
})
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://labs.consol.de"))
.GET()
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandler.asString());
System.out.println(response.statusCode());
System.out.println(response.body());
結(jié)論
上面的例子表明,使用Java 9的標(biāo)準(zhǔn)API來發(fā)送HTTP請求會(huì)更簡便肌幽。 此外晚碾,我們將能以優(yōu)雅的方式處理響應(yīng)。當(dāng)然喂急,某些三方包也有類似喜人的功能格嘁,但總得選擇一種體面的(decent)開箱即用的解決方案(大家閨秀即視感,2333)廊移。
英文原文