到達点は、通信の成功を複数段階で確認することです。前提は第08・10回です。
接続できたことと成功したことは違う
HTTPのリクエストにはメソッド、宛先、ヘッダ、必要に応じた本文があります。レスポンスにはステータス、ヘッダ、本文があります。接続できても404や500は返ります。さらに200でも、本文が想定した形式とは限りません。
次のMain.javaは外部サイトに依存しない実習です。ローカルの小さなサーバーを立て、自分自身へ通信します。サーバーは実験用で、本番運用の構成ではありません。
import com.sun.net.httpserver.HttpServer;
import java.net.*;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
public class Main {
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
server.createContext("/health", exchange -> {
byte[] body = "ok".getBytes(StandardCharsets.UTF_8);
exchange.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8");
exchange.sendResponseHeaders(200, body.length);
try (var out = exchange.getResponseBody()) { out.write(body); }
});
server.start();
try (HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(2)).build()) {
URI uri = URI.create("http://127.0.0.1:" + server.getAddress().getPort() + "/health");
HttpRequest request = HttpRequest.newBuilder(uri)
.timeout(Duration.ofSeconds(3)).GET().build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) throw new IllegalStateException("HTTP failure");
System.out.println(response.body());
} finally {
server.stop(0);
}
}
}
出力はokです。接続待ちとリクエスト待ちに期限を設けています。外部通信ではHTTPSを使い、証明書検証を無効化してエラーを隠さないようにします。
再試行にも仕様がある
読み取りの一時的失敗は再試行できる場合があります。一方、注文作成などは「サーバーで成功したが応答だけ届かなかった」可能性があります。無条件に再送すると重複するため、冪等性キーなどで同じ処理を識別します。回数、待ち時間、全体の期限を決め、無限に再試行しません。
JSONは文字列から決められたデータ構造へ変換し、必須項目と型を検査します。本文のサイズにも上限を置きます。アクセストークンをURLやログへ出さず、設定から読み込んで必要なヘッダへ渡します。
練習と解答
練習:クライアントのパスを /missing に変えてください。
解答:接続自体は成功しますが404となり、コードはHTTP failureを投げます。「通信できた」と「要求が成功した」を分けて扱えている証拠です。
公式資料
HttpClient APIを参照できます。
Java全20回の目次 | 前の回 | 次の回