Введение: зачем нужен HTTP клиент в Java?
Современная разработка на Java редко обходится без взаимодействия с внешними сервисами. Будь то отправка данных на сервер, получение информации от REST API или интеграция с облачными платформами — везде требуется HTTP клиент. В Java существует несколько способов выполнить HTTP-запрос, и важно выбрать правильный подход.
Долгое время стандартным инструментом была библиотека java.net.HttpURLConnection, но с выходом Java 11 появился новый, более удобный модуль java.net.http. В этой статье мы разберем все этапы работы с HTTP клиентом на Java: от простых GET-запросов до отправки JSON и обработки ошибок.
Статья ориентирована на разработчиков, которые уже знакомы с основами Java, но хотят систематизировать знания о сетевых запросах. Мы будем использовать встроенный HttpClient из Java 11+, так как это современный и рекомендуемый подход.
1. Основы HTTP клиента: создание и настройка
Прежде чем отправлять запросы, нужно создать экземпляр HttpClient. Это центральный объект, через который выполняются все HTTP-операции. Рассмотрим базовую конфигурацию.
Создание клиента
Самый простой способ — использовать статический метод newHttpClient():
import java.net.http.HttpClient;
HttpClient client = HttpClient.newHttpClient();
Однако для реальных проектов часто требуется настройка таймаутов, версии протокола или прокси. Вот пример расширенной конфигурации:
import java.net.http.HttpClient;
import java.time.Duration;
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2) // используем HTTP/2
.connectTimeout(Duration.ofSeconds(10)) // таймаут подключения
.followRedirects(HttpClient.Redirect.NORMAL) // автоматическое следование редиректам
.build();
Важные параметры настройки
- version — выбор между HTTP/1.1 и HTTP/2 (по умолчанию HTTP/2, если сервер поддерживает)
- connectTimeout — максимальное время ожидания соединения
- followRedirects — политика обработки редиректов (NEVER, ALWAYS, NORMAL)
- proxy — настройка прокси-сервера (если требуется)
Обратите внимание: один экземпляр HttpClient можно использовать для множества запросов. Он является потокобезопасным, поэтому его можно создать один раз и переиспользовать во всем приложении.
2. Создание и отправка HTTP запросов
Теперь перейдем к главному — формированию запросов. В Java 11+ для этого используется класс HttpRequest. Рассмотрим основные типы запросов.
GET-запрос: получение данных
GET — самый простой тип запроса. Он используется для получения данных от сервера. Пример:
import java.net.URI;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.GET() // явно указываем метод (можно опустить, GET по умолчанию)
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Статус: " + response.statusCode());
System.out.println("Тело ответа: " + response.body());
Метод send() выполняет синхронный запрос. Если нужно асинхронное выполнение, используйте sendAsync(), который возвращает CompletableFuture.
POST-запрос: отправка данных
POST используется для отправки данных на сервер. Чаще всего это JSON или форма. Рассмотрим оба варианта.
POST с JSON:
import java.net.http.HttpRequest.BodyPublishers;
String json = "{\"name\":\"Иван\",\"email\":\"ivan@example.com\"}";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.header("Content-Type", "application/json")
.POST(BodyPublishers.ofString(json))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
POST с формой (application/x-www-form-urlencoded):
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.stream.Collectors;
Map<String, String> formData = Map.of("username", "john", "password", "secret123");
String formBody = formData.entrySet().stream()
.map(e -> URLEncoder.encode(e.getKey(), StandardCharsets.UTF_8) + "=" +
URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/login"))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(BodyPublishers.ofString(formBody))
.build();
PUT и DELETE запросы
Для обновления данных используется PUT, для удаления — DELETE. Синтаксис аналогичен POST:
// PUT запрос
HttpRequest putRequest = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users/1"))
.header("Content-Type", "application/json")
.PUT(BodyPublishers.ofString("{\"name\":\"Обновленное имя\"}"))
.build();
// DELETE запрос
HttpRequest deleteRequest = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users/1"))
.DELETE()
.build();
3. Обработка ответа и работа с заголовками
После отправки запроса важно правильно обработать ответ. Помимо тела ответа, часто нужны заголовки, статус-код и обработка ошибок.
Чтение заголовков ответа
Заголовки могут содержать важную информацию: тип контента, кэширование, токены и т.д.
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// Получаем все заголовки
HttpHeaders headers = response.headers();
System.out.println("Content-Type: " + headers.firstValue("Content-Type").orElse("нет"));
System.out.println("Все заголовки: " + headers.map());
Обработка статус-кодов и ошибок
HTTP-статусы делятся на категории: 2xx — успех, 4xx — ошибка клиента, 5xx — ошибка сервера. Важно проверять статус перед обработкой данных:
int