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);
В примере мы установили оба значения тайм-аута на пять секунд.
7. Обработка файлов cookie
Пакет 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 .