«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\u0026param2=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 в String требуемого формата:
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");
To прочитать значение заголовка из соединения, мы можем использовать метод 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);
Далее мы добавим куки в куки store:
cookies.forEach(cookie -> cookieManager.getCookieStore().add(null, cookie));
Давайте проверим, присутствует ли cookie с именем username, и если нет, мы добавим его в хранилище 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.