java-recipes

ホーム ネットワーク › N-02: HttpURLConnection

N-02: HttpURLConnection によるHTTP通信(Java 8〜)

HttpURLConnection は Java 1.1 から存在する古参の HTTP クライアント API です。Java 8 環境で HTTP 通信が必要な場合や、 レガシーコードを読む際に必要となります。Java 11 以降は HttpClient(N-01) の利用を推奨します。

HttpURLConnection vs HttpClient

項目HttpURLConnectionHttpClient(Java 11+)
最低バージョンJava 1.1〜Java 11〜
非同期処理なし(同期のみ)sendAsync() + CompletableFuture
コードの簡潔さ冗長(setDoOutput など)ビルダーパターンで直感的
HTTP/2 対応なしあり
接続の切断disconnect() が必要自動管理

サンプルコード

Sample.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;

public class HttpURLConnectionSample {

    private static final int CONNECT_TIMEOUT_MS = 5000;
    private static final int READ_TIMEOUT_MS    = 10000;

    /**
     * GET リクエストを送信してレスポンスボディを返す。
     */
    public static String get(String urlStr) throws IOException {
        URL url = new URL(urlStr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        try {
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(CONNECT_TIMEOUT_MS);
            conn.setReadTimeout(READ_TIMEOUT_MS);
            conn.setRequestProperty("Accept", "application/json");

            int status = conn.getResponseCode();
            System.out.println("ステータスコード: " + status);

            if (status >= 400) {
                // エラー時は getErrorStream() からレスポンスを読む
                try (BufferedReader br = new BufferedReader(
                        new InputStreamReader(conn.getErrorStream(), StandardCharsets.UTF_8))) {
                    throw new IOException("HTTP エラー " + status + ": " + readAll(br));
                }
            }
            try (BufferedReader br = new BufferedReader(
                    new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
                return readAll(br);
            }
        } finally {
            conn.disconnect(); // 接続を明示的に切断する
        }
    }

    /**
     * POST リクエストを送信してレスポンスボディを返す。
     */
    public static String post(String urlStr, String jsonBody) throws IOException {
        URL url = new URL(urlStr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        try {
            conn.setRequestMethod("POST");
            conn.setConnectTimeout(CONNECT_TIMEOUT_MS);
            conn.setReadTimeout(READ_TIMEOUT_MS);
            conn.setDoOutput(true); // リクエストボディの送信を有効化
            conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            conn.setRequestProperty("Accept", "application/json");

            // リクエストボディを書き込む
            byte[] bodyBytes = jsonBody.getBytes(StandardCharsets.UTF_8);
            conn.setRequestProperty("Content-Length", String.valueOf(bodyBytes.length));
            try (OutputStream os = conn.getOutputStream()) {
                os.write(bodyBytes);
            }

            int status = conn.getResponseCode();
            System.out.println("ステータスコード: " + status);

            if (status >= 400) {
                try (BufferedReader br = new BufferedReader(
                        new InputStreamReader(conn.getErrorStream(), StandardCharsets.UTF_8))) {
                    throw new IOException("HTTP エラー " + status + ": " + readAll(br));
                }
            }
            try (BufferedReader br = new BufferedReader(
                    new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
                return readAll(br);
            }
        } finally {
            conn.disconnect();
        }
    }

    // レスポンスの全行を結合するヘルパー
    private static String readAll(BufferedReader br) throws IOException {
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }
        return sb.toString();
    }

    public static void main(String[] args) throws Exception {
        // 実際のエンドポイントに差し替えて実行してください
        // String response = get("https://httpbin.org/get");
        // System.out.println(response);

        // POST の例
        // String body = "{\"name\":\"山田太郎\",\"age\":30}";
        // String response = post("https://httpbin.org/post", body);
        // System.out.println(response);

        System.out.println("実際の URL に差し替えて実行してください。");
    }
}

よくあるミス・注意点

⚠️ エラーレスポンスは getErrorStream() から読む

HTTP 400 以上のステータスコードが返った場合、getInputStream() を呼ぶとIOException がスローされます。 エラーレスポンスのボディは必ず getErrorStream() から読んでください。

// NG: 4xx/5xx のとき IOException が発生する
BufferedReader br = new BufferedReader(
    new InputStreamReader(conn.getInputStream(), ...)); // ← エラー!

// OK: ステータスコードで分岐する
InputStream stream = status >= 400
    ? conn.getErrorStream()
    : conn.getInputStream();

📌 disconnect() は finally ブロックで必ず呼ぶ

HttpURLConnectionCloseable を実装していないため try-with-resources が使えません。 例外が発生しても接続が切断されるよう、必ず finally disconnect() を呼びましょう。

📌 POST でボディを送るには setDoOutput(true) が必要

デフォルトではリクエストボディを送れません。conn.setDoOutput(true) を設定してからgetOutputStream() でボディを書き込みます。 この設定を忘れると IllegalStateException が発生します。

テストする観点

  • 正常な URL への GET リクエストで 200 のレスポンスが返るか
  • 存在しないリソースへのリクエストで 404 の例外が発生するか
  • タイムアウト設定が機能して、応答が遅いサーバーに対して SocketTimeoutException がスローされるか
  • 不正な URL 文字列で MalformedURLException が発生するか
  • POST リクエストで Content-Type ヘッダーが正しく設定されているか

GitHub でソースコードを見る →