HTTPクライアントAPIの概要
Javaの標準APIでHTTP接続を行う場合、Java 8以前のバージョンでは、HttpURLConnectionクラスを利用する方法が一般的だった。しかし、このクラスはJava SE 1.1の頃(1996年)に追加された非常に古いもので、機能面でも使い勝手の面でも、現在のアプリケーション開発で使うには多くの問題を抱えている。
特に問題であるのは、HTTP/2プロトコルに対応していないことで、現在では多くのWebサイトで利用されている非同期通信などの機能を使うことができない。
そこで、Java 9およびJava 10で、HTTP/2に対応した新しいAPIがJEP 110: HTTP/2 Client (Incubator)として試験的に導入された。そして、そのフィードバックを元に仕様の修正が行われ、JEP 321: HTTP Client (Standard)としてJava 11で正式に追加されることになった。新しいHTTPクライアントAPIの特徴としては、次の点が挙げられる。
- HTTP/2をサポート
- 同期/非同期の双方をサポート
- WebSocketをサポート
- リクエスト/レスポンスをリアクティブストリームとして処理
- ビルダーとメソッドチェーンによる簡易な設定
HTTPクライアントAPIの関連クラス・インタフェースはjava.net.httpモジュールのjava.net.httpパッケージで提供されている。中心になるのは次の3つのクラスとインタフェースである。
- HttpClientクラス - リクエストの送信やレスポンスの取得を行う
- HttpRequestクラス - URIを含むリクエストの詳細情報をカプセル化する
- HttpResponseインタフェース - サーバから受け取ったレスポンスをカプセル化する
上記に加えて、WebSocketを利用するためのWebSocketインタフェースや、リクエスト/レスポンスをリアクティブストリームとして扱うためのHttpRequest.BodyPublisherインタフェースやHttpResponse.BodySubscriberインタフェースなどが用意されている。
基本的な使い方
それでは、HTTPクライアントAPIの基本的な使い方を見ていこう。次のコードは、指定のURIに対してHTTPリクエストを送ってレスポンスを受け取る例である。
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class HttpGetSample {
public static void main(String[] args) {
// デフォルト設定でHttpClientインスタンスを作成
HttpClient client = HttpClient.newHttpClient();
// ビルダーを使用してHttpRequestインスタンスを作成
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://news.mynavi.jp/freeword?q=java"))
.build();
try {
// リクエストを送信
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
// レスポンスボディを出力
System.out.println(response.body());
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
HttpClientクラスのインスタンスを作成する最も簡単な方法は、newHttpClient()メソッドを使用することだ。
HttpClient client = HttpClient.newHttpClient();
このメソッドは、デフォルト値に設定されたHttpClientインスタンスを作成して返す。この場合のデフォルト値とは、リクエストメソッドは"GET"、プロトコルは"HTTP/2"、リダイレクトは無効、そしてプロキシとSSLコンテキストはシステムのデフォルト設定となる。
もし設定をカスタマイズしたい場合は、次のようにnewBuilder()メソッドを使ってHttpClientインスタンスを作成する。
// ビルダーを使用してHttpClientインスタンスを作成
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2) // 明示的にHTTP/2を指定
.followRedirects(HttpClient.Redirect.NORMAL) // リダイレクトを有効化(HTTPS→HTTPを除く)
.build();
このメソッドは、HttpClient.Builderインタフェースのインスタンスを返す。HttpClient.BuilderにはHTTP通信のためのさまざまな設定を行うメソッドが用意されている。それぞれのメソッドは、戻り値として自身のHttpClient.Builderインスタンスを返すので、各種設定をメソッドチェーンでセットしていくことができるようになっている。上の例では、明示的にHTTP/2プロトコルを使用することと、リダイレクトを有効化(ただし、HTTPSからHTTPへはリダイレクトしない)する設定を追加している。最後にbuild()メソッドを呼び出すことで、リクエストのためのHttpClientインスタンスが作成できる。
実際のリクエスト内容は、HttpRequestクラスを使ってカプセル化する。
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://news.mynavi.jp/freeword?q=java"))
.build();
このクラスのインスタンスもnewBuilder()メソッドで作成できる。newBuilder()メソッドの戻り値はHttpRequest.Builderインスタンスで、このインタフェースもHttpClientと同様にメソッドチェーンによって様々な設定が行えるようになっている。上のコードの場合、uri()メソッドでリクエスト先のURIを指定した上で、build()メソッドを呼び出してHttpRequestインスタンスを作成している。
これを次のようにすると、タイムアウト時間として60秒を、リクエストヘッダの"Content-Type"の値に"text/plain"を指定できる。
// タイムアウトとContent-Typeヘッダを指定してHttpRequestインスタンスを作成
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://news.mynavi.jp/freeword?q=java"))
.timeout(Duration.ofSeconds(60)) // タイムアウトを指定
.header("Content-Type", "text/plain") // Content-Typeを指定
.build();
実際のリクエストの送信は、同期通信であれば次のようにHttpClientインスタンスに対してsend()メソッドを呼び出すことで行う。
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
send()メソッドの第一引数にはHttpRequestインスタンスを、第二引数にはレスポンスボディを処理するためのハンドラであるHttpResponse.BodyHandler<T>のインスタンスを渡す。文字列やファイルなどといった一般的な形式のハンドラについては、HttpResponse.BodyHandlersクラスのstaticメソッドを使えば簡単に作成できる。ofString()は、レスポンスボディを文字列として受け取る場合のハンドラを作成してくれるメソッドだ。
実際のレスポンスは、HttpResponse<T>インスタンスにカプセル化されて返ってくる。このインタフェースでは、body()メソッドで実際のレスポンスボディを取得することができる。今回の例では、ハンドラがHttpResponse.BodyHandler<String>になっているので、HttpResponse<T>の型引数もStringであり、body()が返す値もStringインスタンスとなる。
以上が、HTTPクライアントAPIを使ってHTTP通信を行うもっとも一般的な手順である。ただし、冒頭で紹介したようにHTTPクライアントAPIは非同期通信をはじめとするさまざまな通信方式にも対応している。次回は、いろいろなバリエーションのHTTP通信のやり方を紹介しよう。