概要

HttpURLConnection は Java 1.1 から存在する HTTP 通信の標準 API です。Java 11 以降では HttpClient が推奨されますが、Java 8 環境の保守案件や、外部ライブラリを追加できない制約のあるプロジェクトでは今でも現役で使われています。ただし、ストリームの読み書きやエラーレスポンスの扱い、disconnect のタイミングなど、初見で引っかかるポイントが多い API でもあります。この記事では、GET/POST の基本パターンから、エラーストリームの正しい読み取り方、タイムアウト設定、レスポンスヘッダーの取得まで、実務で困らないレベルの知識を整理します。Java 21 では sealed interface で成功/失敗を型安全に表現するパターンも紹介します。

使いどころ

Java 8 環境の保守案件で外部 API にリクエストを送信する

外部ライブラリの追加が制限された環境で HTTP 通信を実装する

テスト環境で簡易的な HTTP リクエストを送信してエンドポイントの疎通確認を行う

コード例

HttpURLConnection で GET/POST を実装する
import java.io.BufferedReader;

public class HttpURLConnectionSample {

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

    public static String get(String urlStr) throws IOException {
        var conn = (HttpURLConnection) new URL(urlStr).openConnection();
        try {
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(CONNECT_TIMEOUT_MS);
            conn.setReadTimeout(READ_TIMEOUT_MS);
            conn.setRequestProperty("Accept", "application/json");

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

    public static String post(String urlStr, String jsonBody) throws IOException {
        var conn = (HttpURLConnection) new URL(urlStr).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");

            var bodyBytes = jsonBody.getBytes(StandardCharsets.UTF_8);
            conn.setRequestProperty("Content-Length", String.valueOf(bodyBytes.length));
            try (OutputStream os = conn.getOutputStream()) {
                os.write(bodyBytes);
            }

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

    private static String readAll(BufferedReader br) throws IOException {
        var sb = new StringBuilder();
        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }
        return sb.toString();
    }

    public static void main(String[] args) throws IOException {
        String response = get("https://httpbin.org/get");
        System.out.println(response);
    }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

var(JEP 286)による型推論で HttpURLConnection の型宣言を省略でき、記述が簡潔になる。エラーストリームの処理パターンも整理しやすい。

Java 17
// Java 17: var で簡潔に
var conn = (HttpURLConnection)
    new URL(urlStr).openConnection();
try {
    conn.setRequestMethod("GET");
    var status = conn.getResponseCode();
    if (status >= 400) {
        try (var br = new BufferedReader(
                new InputStreamReader(
                    conn.getErrorStream()))) {
            throw new IOException(readAll(br));
        }
    }
} finally {
    conn.disconnect();
}

Library Comparison

標準 HttpURLConnectionJava 8 環境や外部ライブラリ追加不可の制約がある場合。ストリーム操作が冗長。リダイレクト追跡やクッキー管理は手動で行う必要がある。
標準 HttpClient(Java 11+)Java 11 以降の環境。ビルダーパターンで組み立てやすく、非同期もサポート。Java 8 では使えない。
OkHttpJava 8 環境でも HttpURLConnection より扱いやすいクライアントが必要な場合。外部依存が追加される。Kotlin ランタイムも含まれる。

注意点

getErrorStream() は 4xx/5xx のときだけ非 null を返す。getInputStream() をそのまま呼ぶと IOException が発生する

disconnect() を finally ブロックで必ず呼ぶこと。呼ばないとソケットが解放されずリソースリークになる

setDoOutput(true) を呼ばないと POST のリクエストボディを送信できない。GET の場合は false(デフォルト)のままにする

URL クラスのコンストラクタは Java 20 で非推奨になった。将来的には URI.create().toURL() への移行が必要

FAQ

HttpURLConnection は非推奨ですか。

API 自体は非推奨ではありません。ただし Java 11 以降の新規プロジェクトでは HttpClient が推奨です。Java 8 環境や保守案件では引き続き使用できます。

リダイレクト(3xx)を自動追跡するにはどうしますか。

HttpURLConnection.setFollowRedirects(true) でデフォルトで有効です。ただし HTTP から HTTPS へのリダイレクトは自動追跡されないため、手動でリダイレクト先 URL を取得して再接続する必要があります。

エラーレスポンスのボディを読むにはどうしますか。

ステータスコードが 400 以上の場合、getInputStream() ではなく getErrorStream() からレスポンスボディを読みます。null チェックも忘れずに行ってください。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます