Перейти к основному содержимому

Сделайте простой HTTP-запрос в Java

· 6 мин. чтения

1. Обзор

В этом кратком руководстве мы представляем способ выполнения HTTP-запросов в Java — с помощью встроенного класса Java HttpUrlConnection.

Обратите внимание, что начиная с JDK 11, Java предоставляет новый API для выполнения HTTP-запросов, который предназначен для замены HttpUrlConnection , HttpClient API .

2. HttpUrlConnection

Класс HttpUrlConnection позволяет нам выполнять базовые HTTP-запросы без использования каких-либо дополнительных библиотек. Все классы, которые нам нужны, являются частью пакета java.net .

Недостатки использования этого метода заключаются в том, что код может быть более громоздким, чем в других библиотеках HTTP, и в том, что он не предоставляет более продвинутых функций, таких как специальные методы для добавления заголовков или проверки подлинности.

3. Создание запроса

Мы можем создать экземпляр HttpUrlConnection , используя метод openConnection() класса URL . Обратите внимание, что этот метод только создает объект соединения, но еще не устанавливает соединение.

Класс HttpUrlConnection используется для всех типов запросов путем установки атрибута requestMethod в одно из значений: GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE.

Давайте создадим соединение с заданным URL-адресом, используя метод GET:

URL url = new URL("http://example.com");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");

4. Добавление параметров запроса

Если мы хотим добавить параметры к запросу, мы должны установить для свойства doOutput значение true , а затем записать строку вида param1=value¶m2=value в OutputStream экземпляра HttpUrlConnection :

Map<String, String> parameters = new HashMap<>();
parameters.put("param1", "val");

con.setDoOutput(true);
DataOutputStream out = new DataOutputStream(con.getOutputStream());
out.writeBytes(ParameterStringBuilder.getParamsString(parameters));
out.flush();
out.close();

Чтобы упростить преобразование параметра Map , мы написали служебный класс ParameterStringBuilder , содержащий статический метод getParamsString() , который преобразует Map в строку требуемого формата:

public class ParameterStringBuilder {
public static String getParamsString(Map<String, String> params)
throws UnsupportedEncodingException{
StringBuilder result = new StringBuilder();

for (Map.Entry<String, String> entry : params.entrySet()) {
result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
result.append("=");
result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
result.append("&");
}

String resultString = result.toString();
return resultString.length() > 0
? resultString.substring(0, resultString.length() - 1)
: resultString;
}
}

5. Настройка заголовков запроса

Добавление заголовков к запросу может быть достигнуто с помощью метода setRequestProperty() :

con.setRequestProperty("Content-Type", "application/json");

Чтобы прочитать значение заголовка из соединения, мы можем использовать метод getHeaderField() :

String contentType = con.getHeaderField("Content-Type");

6. Настройка тайм-аутов

Класс HttpUrlConnection позволяет устанавливать тайм-ауты подключения и чтения. Эти значения определяют интервал времени ожидания установления соединения с сервером или доступности данных для чтения.

Чтобы установить значения времени ожидания, мы можем использовать методы setConnectTimeout() и setReadTimeout() :

con.setConnectTimeout(5000);
con.setReadTimeout(5000);

В примере мы установили оба значения тайм-аута на пять секунд.

Пакет java.net содержит классы, упрощающие работу с файлами cookie, такие как CookieManager и HttpCookie .

Во- первых, чтобы прочитать куки из ответа , мы можем получить значение заголовка Set-Cookie и разобрать его на список объектов HttpCookie :

String cookiesHeader = con.getHeaderField("Set-Cookie");
List<HttpCookie> cookies = HttpCookie.parse(cookiesHeader);

Далее мы добавим файлы cookie в хранилище файлов cookie :

cookies.forEach(cookie -> cookieManager.getCookieStore().add(null, cookie));

Давайте проверим, присутствует ли файл cookie с именем пользователя , и если нет, мы добавим его в хранилище файлов cookie со значением «john»:

Optional<HttpCookie> usernameCookie = cookies.stream()
.findAny().filter(cookie -> cookie.getName().equals("username"));
if (usernameCookie == null) {
cookieManager.getCookieStore().add(null, new HttpCookie("username", "john"));
}

Наконец, чтобы добавить файлы cookie в запрос , нам нужно установить заголовок Cookie после закрытия и повторного открытия соединения:

con.disconnect();
con = (HttpURLConnection) url.openConnection();

con.setRequestProperty("Cookie",
StringUtils.join(cookieManager.getCookieStore().getCookies(), ";"));

8. Обработка редиректов

Мы можем включить или отключить автоматически следующие перенаправления для определенного соединения , используя метод setInstanceFollowRedirects() с параметром true или false :

con.setInstanceFollowRedirects(false);

Также можно включить или отключить автоматическое перенаправление для всех подключений :

HttpUrlConnection.setFollowRedirects(false);

По умолчанию поведение включено.

Когда запрос возвращает код состояния 301 или 302, указывающий на перенаправление, мы можем получить заголовок Location и создать новый запрос на новый URL:

if (status == HttpURLConnection.HTTP_MOVED_TEMP
|| status == HttpURLConnection.HTTP_MOVED_PERM) {
String location = con.getHeaderField("Location");
URL newUrl = new URL(location);
con = (HttpURLConnection) newUrl.openConnection();
}

9. Чтение ответа

Чтение ответа на запрос можно выполнить путем анализа InputStream экземпляра HttpUrlConnection .

Для выполнения запроса мы можем использовать методы getResponseCode() , connect() , getInputStream() или getOutputStream() :

int status = con.getResponseCode();

Наконец, давайте прочитаем ответ на запрос и поместим его в строку содержимого :

BufferedReader in = new BufferedReader(
new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer content = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();

Чтобы закрыть соединение , мы можем использовать метод disconnect() :

con.disconnect();

10. Чтение ответа на неудачные запросы

Если запрос завершится ошибкой, попытка прочитать InputStream экземпляра HttpUrlConnection не сработает. Вместо этого мы можем использовать поток, предоставленный HttpUrlConnection.getErrorStream() .

Мы можем решить, какой InputStream использовать, сравнив код состояния HTTP :

int status = con.getResponseCode();

Reader streamReader = null;

if (status > 299) {
streamReader = new InputStreamReader(con.getErrorStream());
} else {
streamReader = new InputStreamReader(con.getInputStream());
}

И, наконец, мы можем прочитать streamReader так же, как и в предыдущем разделе.

11. Создание полного ответа

Невозможно получить полное представление ответа с помощью экземпляра HttpUrlConnection .

Однако мы можем построить его, используя некоторые методы, которые предлагает экземпляр HttpUrlConnection :

public class FullResponseBuilder {
public static String getFullResponse(HttpURLConnection con) throws IOException {
StringBuilder fullResponseBuilder = new StringBuilder();

// read status and message

// read headers

// read response content

return fullResponseBuilder.toString();
}
}

Здесь мы читаем части ответов, включая код состояния, сообщение о состоянии и заголовки, и добавляем их в экземпляр StringBuilder .

Во-первых, давайте добавим информацию о статусе ответа :

fullResponseBuilder.append(con.getResponseCode())
.append(" ")
.append(con.getResponseMessage())
.append("\n");

Далее мы получим заголовки с помощью getHeaderFields() и добавим каждый из них в наш StringBuilder в формате HeaderName: HeaderValues :

con.getHeaderFields().entrySet().stream()
.filter(entry -> entry.getKey() != null)
.forEach(entry -> {
fullResponseBuilder.append(entry.getKey()).append(": ");
List headerValues = entry.getValue();
Iterator it = headerValues.iterator();
if (it.hasNext()) {
fullResponseBuilder.append(it.next());
while (it.hasNext()) {
fullResponseBuilder.append(", ").append(it.next());
}
}
fullResponseBuilder.append("\n");
});

Наконец, мы прочитаем содержимое ответа, как мы это делали ранее, и добавим его.

Обратите внимание, что метод getFullResponse проверяет, был ли запрос успешным или нет, чтобы решить, нужно ли использовать con.getInputStream() или con.getErrorStream() для получения содержимого запроса.

12. Заключение

В этой статье мы показали, как мы можем выполнять HTTP-запросы, используя класс HttpUrlConnection .

Полный исходный код примеров можно найти на GitHub .