Avatar ·

Как использовать java.net.URLConnection для отправки и обработки HTTP-запросов

Всем привет! Я только начинаю изучать Java и столкнулся с задачей: нужно отправить HTTP-запрос на сервер и получить ответ. Прочитал про класс URLConnection, но никак не могу разобраться с его правильным использованием. Написал вот такой код для GET-запроса: ```java import java.net.*; import java.io.*; public class HttpTest { public static void main(String[] args) throws Exception { URL url = new URL("http://example.com/api/data"); URLConnection conn = url.openConnection(); BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); String line; while ((line = reader.readLine()) != null) { System.out.println(line); } reader.close(); } } ``` Но код работает нестабильно: иногда вылетает IOException, иногда ответ приходит неполный. А ещё я хочу научиться отправлять POST-запросы с JSON-телом, но документация Oracle меня пугает своей сложностью. Подскажите, пожалуйста: 1. Как правильно настроить таймауты и заголовки? 2. Как отличить успешный ответ от ошибки (коды 4xx, 5xx)? 3. Как отправить POST с телом запроса? 4. Есть ли способ избежать утечки памяти при работе с потоками? Заранее спасибо! Буду благодарен за примеры рабочего кода с пояснениями. Многие советуют использовать Apache HttpClient, но хочется сначала разобраться со стандартным API. <<>>

Полное руководство по работе с URLConnection в Java

Вы на верном пути, но стандартный URLConnection требует аккуратного обращения. Давайте разберём все проблемы по порядку.

1. Базовая структура правильного GET-запроса

Главная ошибка новичков — отсутствие обработки кода ответа и неправильное управление потоками. Вот корректный шаблон:

import java.net.*;
import java.io.*;

public class HttpGetExample {
    public static void main(String[] args) {
        HttpURLConnection conn = null;
        BufferedReader reader = null;
        
        try {
            URL url = new URL("https://api.github.com/users/octocat");
            conn = (HttpURLConnection) url.openConnection();
            
            // Настройка параметров
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5000);      // 5 секунд на установку соединения
            conn.setReadTimeout(10000);        // 10 секунд на чтение данных
            conn.setRequestProperty("User-Agent", "Mozilla/5.0");
            conn.setRequestProperty("Accept", "application/json");
            
            // Проверка кода ответа
            int responseCode = conn.getResponseCode();
            System.out.println("Response Code: " + responseCode);
            
            if (responseCode >= 200 && responseCode < 300) {
                // Успешный ответ - читаем из InputStream
                reader = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            } else {
                // Ошибка - читаем из ErrorStream
                reader = new BufferedReader(
                    new InputStreamReader(conn.getErrorStream()));
            }
            
            // Чтение ответа
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            
            System.out.println("Response: " + response.toString());
            
        } catch (SocketTimeoutException e) {
            System.err.println("Таймаут соединения: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("Ошибка ввода-вывода: " + e.getMessage());
        } finally {
            // Всегда закрываем потоки
            try {
                if (reader != null) reader.close();
                if (conn != null) conn.disconnect();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

2. POST-запрос с JSON-телом

Для отправки данных используется OutputStream. Важно включить режим setDoOutput(true):

public class HttpPostExample {
    public static String sendPost(String urlStr, String jsonBody) throws IOException {
        HttpURLConnection conn = null;
        try {
            URL url = new URL(urlStr);
            conn = (HttpURLConnection) url.openConnection();
            
            // Настройка POST-запроса
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-Type", "application/json; utf-8");
            conn.setRequestProperty("Accept", "application/json");
            conn.setDoOutput(true);  // Разрешаем запись тела запроса
            
            // Запись JSON-тела
            try (OutputStream os = conn.getOutputStream()) {
                byte[] input = jsonBody.getBytes("utf-8");
                os.write(input, 0, input.length);
            }
            
            // Чтение ответа (аналогично GET)
            int responseCode = conn.getResponseCode();
            StringBuilder response = new StringBuilder();
            
            try (BufferedReader br = new BufferedReader(
                    new InputStreamReader(
                        responseCode >= 200 && responseCode < 300 
                        ? conn.getInputStream() 
                        : conn.getErrorStream()))) {
                String line;
                while ((line = br.readLine()) != null) {
                    response.append(line.trim());
                }
            }
            
            return response.toString();
            
        } finally {
            if (conn != null) conn.disconnect();
        }
    }
    
    public static void main(String[] args) throws IOException {
        String json = "{\"title\": \"foo\", \"body\": \"bar\", \"userId\": 1}";
        String result = sendPost("https://jsonplaceholder.typicode.com/posts", json);
        System.out.println(result);
    }
}

3. Важные моменты и лучшие практики

  • Таймауты — всегда устанавливайте setConnectTimeout и setReadTimeout, чтобы ваша программа не зависала навечно.
  • User-Agent — многие серверы блокируют запросы без этого заголовка. Всегда указывайте хотя бы "Java/1.8".
  • Кэширование — по умолчанию URLConnection кэширует ответы. Отключите через setUseCaches(false) для AJAX-подобных запросов.
  • Сжатие — добавьте заголовок Accept-Encoding: gzip и обрабатывайте сжатые ответы с помощью GZIPInputStream.
  • Следование редиректам — по умолчанию включено. Управляйте через setInstanceFollowRedirects(false).

4. Обработка ошибок

Всегда используйте getErrorStream() при кодах 4xx/5xx. Серверы часто возвращают полезную информацию об ошибке в теле ответа. Пример:

if (responseCode == 400) {
    // Читаем errorStream для получения деталей ошибки
    try (BufferedReader br = new BufferedReader(
            new InputStreamReader(conn.getErrorStream()))) {
        String errorBody = br.lines().collect(Collectors.joining());
        System.err.println("Bad Request: " + errorBody);
    }
}

Этот подход полностью решает ваши проблемы с неполными ответами и исключениями. Помните: всегда закрывайте потоки в finally или используйте try-with-resources (Java 7+).

<<>>

Альтернативный подход: использование java.net.http.HttpClient (Java 11+)

Если вы используете Java 11 или новее, настоятельно рекомендую перейти на новый HttpClient из модуля java.net.http. Он значительно удобнее и безопаснее старого URLConnection.

import java.net.URI;
import java.net.http.*;
import java.net.http.HttpResponse.BodyHandlers;

public class ModernHttpExample {
    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(5))
                .build();
        
        // GET-запрос
        HttpRequest getRequest = HttpRequest.newBuilder()
                .uri(URI.create("https://api.github.com/users/octocat"))
                .header("Accept", "application/json")
                .timeout(Duration.ofSeconds(10))
                .GET()
                .build();
        
        HttpResponse getResponse = client.send(getRequest, BodyHandlers.ofString());
        System.out.println("GET Status: " + getResponse.statusCode());
        System.out.println("GET Body: " + getResponse.body());
        
        // POST-запрос с JSON
        String json = "{\"title\": \"foo\", \"body\": \"bar\"}";
        HttpRequest postRequest = HttpRequest.newBuilder()
                .uri(URI.create("https://jsonplaceholder.typicode.com/posts"))
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(json))
                .build();
        
        HttpResponse postResponse = client.send(postRequest, BodyHandlers.ofString());
        System.out.println("POST Status: " + postResponse.statusCode());
        System.out.println("POST Body: " + postResponse.body());
    }
}

Преимущества HttpClient:

  • Автоматическое управление потоками — не нужно вручную закрывать.
  • Поддержка HTTP/2 и WebSocket.
  • Асинхронные запросы через sendAsync().
  • Чистый fluent API без лишних приведений типов.

Если проект позволяет использовать Java 11+, это лучший выбор. Старый URLConnection стоит применять только для обратной совместимости с Java 8.