Как использовать java. net. URLConnection для запуска и обработки HTTP-запросов
использование java.net.URLConnection
спрашивают о довольно часто здесь, и то Oracle tutorial и слишком кратко об этом.
этот учебник в основном показывает только, как запустить запрос GET и прочитать ответ. Он нигде не объясняет, как использовать его для выполнения запроса POST, установки заголовков запросов, чтения заголовков ответов, работы с файлами cookie, отправки HTML-формы, загрузки файла и т. д.
Итак, как я могу использовать java.net.URLConnection
огонь и обрабатывать" продвинутые " HTTP-запросы?
11 ответов:
первый отказ от ответственности заранее: опубликованные фрагменты кода все основные примеры. Вам нужно будет обрабатывать тривиальные
IOException
s иRuntimeException
s какNullPointerException
,ArrayIndexOutOfBoundsException
и общается сам с собой.
подготовка
сначала нам нужно знать хотя бы URL и кодировку. Эти параметры являются необязательными и зависят от функциональных требований.
String url = "http://example.com"; String charset = "UTF-8"; // Or in Java 7 and later, use the constant: java.nio.charset.StandardCharsets.UTF_8.name() String param1 = "value1"; String param2 = "value2"; // ... String query = String.format("param1=%s¶m2=%s", URLEncoder.encode(param1, charset), URLEncoder.encode(param2, charset));
параметры запроса должны быть в и быть сцеплены с
&
. Вы бы обычно также URL-encode параметры запроса с указанным набором символов с помощьюURLEncoder#encode()
.The
String#format()
- это просто для удобства. Я предпочитаю, когда мне понадобится оператор конкатенации+
более чем в два раза.
увольнение HTTP GET запрос (при необходимости) параметры запроса
это тривиальная задача. Это метод запроса по умолчанию.
URLConnection connection = new URL(url + "?" + query).openConnection(); connection.setRequestProperty("Accept-Charset", charset); InputStream response = connection.getInputStream(); // ...
любая строка запроса должна быть соединена с URL с помощью
?
. ЭлементAccept-Charset
заголовок может подсказать серверу, в какой кодировке находятся параметры. Если вы не отправляете строку запроса, то вы можете оставитьAccept-Charset
заголовок подальше. Если вам не нужно устанавливать какие-либо заголовки, то вы можете даже использоватьURL#openStream()
метод быстрого доступа.InputStream response = new URL(url).openStream(); // ...
в любом случае, если другая сторона
HttpServlet
, тоdoGet()
метод будет вызван и параметры будут доступны поHttpServletRequest#getParameter()
.для целей тестирования вы можете распечатать тело ответа на stdout, как показано ниже:
try (Scanner scanner = new Scanner(response)) { String responseBody = scanner.useDelimiter("\A").next(); System.out.println(responseBody); }
увольнение HTTP POST запрос с параметрами запроса
задание
URLConnection#setDoOutput()
totrue
неявно задает метод запроса для POST. Стандарт HTTP POST, как и веб-формы, имеет типapplication/x-www-form-urlencoded
где строка запроса записывается в тело запроса.URLConnection connection = new URL(url).openConnection(); connection.setDoOutput(true); // Triggers POST. connection.setRequestProperty("Accept-Charset", charset); connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + charset); try (OutputStream output = connection.getOutputStream()) { output.write(query.getBytes(charset)); } InputStream response = connection.getInputStream(); // ...
Примечание: всякий раз, когда вы хотите отправить HTML-форму программно, не забудьте взять
name=value
пар любого<input type="hidden">
элементы в строке запроса и, конечно же,name=value
пара<input type="submit">
элемент, который вы хотели бы "нажать" программно (потому что это обычно используется на стороне сервера, чтобы отличить, была ли нажата кнопка, и если да, который.)вы также можете применять полученные
URLConnection
toHttpURLConnection
и использовать егоHttpURLConnection#setRequestMethod()
вместо. Но если вы пытаетесь использовать соединение для вывода, вам все равно нужно установитьURLConnection#setDoOutput()
totrue
.HttpURLConnection httpConnection = (HttpURLConnection) new URL(url).openConnection(); httpConnection.setRequestMethod("POST"); // ...
в любом случае, если другая сторона
HttpServlet
, тоdoPost()
метод будет вызван и параметры будут доступны мимоHttpServletRequest#getParameter()
.
фактически запуск HTTP-запроса
вы можете запустить HTTP-запрос явно с
URLConnection#connect()
, но запрос будет автоматически запущен по требованию, когда вы хотите получить любую информацию о HTTP-ответе, например, тело ответа с помощьюURLConnection#getInputStream()
и так далее. Приведенные выше примеры делают именно это, поэтомуconnect()
звонок на самом деле излишний.
сбор информации HTTP-ответа
вам понадобится
HttpURLConnection
здесь. Бросьте его сначала, если это необходимо.int status = httpConnection.getResponseCode();
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) { System.out.println(header.getKey() + "=" + header.getValue()); }
когда
Content-Type
содержит acharset
параметр, тогда тело ответа, вероятно, основано на тексте, и мы хотели бы обработать тело ответа с указанной кодировкой символов на стороне сервера.String contentType = connection.getHeaderField("Content-Type"); String charset = null; for (String param : contentType.replace(" ", "").split(";")) { if (param.startsWith("charset=")) { charset = param.split("=", 2)[1]; break; } } if (charset != null) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(response, charset))) { for (String line; (line = reader.readLine()) != null;) { // ... System.out.println(line) ? } } } else { // It's likely binary content, use InputStream/OutputStream. }
сохранение сессии
сеанс на стороне сервера обычно поддерживается файлом cookie. Некоторые веб-формы требуют, чтобы вы вошли в систему и/или отслеживает сессии. Вы можете использовать
CookieHandler
API для поддержки файлов cookie. Вам необходимо подготовитьCookieManager
сCookiePolicy
наACCEPT_ALL
перед отправкой всех HTTP-запросов.// First set the default cookie manager. CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL)); // All the following subsequent URLConnections will use the same cookie manager. URLConnection connection = new URL(url).openConnection(); // ... connection = new URL(url).openConnection(); // ... connection = new URL(url).openConnection(); // ...
обратите внимание, что это, как известно, не всегда работает должным образом во всех обстоятельствах. Если это не удается для вас, то лучше всего вручную собрать и установить заголовки файлов cookie. Вам в основном нужно захватить все
Set-Cookie
заголовки из ответа логина или первогоGET
запрос, а затем передать это через последующие запросы.// Gather all cookies on the first request. URLConnection connection = new URL(url).openConnection(); List<String> cookies = connection.getHeaderFields().get("Set-Cookie"); // ... // Then use the same cookies on all subsequent requests. connection = new URL(url).openConnection(); for (String cookie : cookies) { connection.addRequestProperty("Cookie", cookie.split(";", 2)[0]); } // ...
The
split(";", 2)[0]
есть ли, чтобы избавиться от атрибутов cookie, которые не имеют отношения к стороне сервера, какexpires
,path
и т. д. Кроме того, вы также можете использоватьcookie.substring(0, cookie.indexOf(';'))
вместоsplit()
.
режим потоковой передачи
The
HttpURLConnection
будет по умолчанию буфер весь тело запроса перед фактической отправкой, независимо от того, установили ли вы фиксированную длину контента самостоятельно с помощьюconnection.setRequestProperty("Content-Length", contentLength);
. Это может привести кOutOfMemoryException
s всякий раз, когда вы одновременно отправляете большие запросы POST (например, загрузка файлов). Чтобы избежать этого, вы хотели бы установитьHttpURLConnection#setFixedLengthStreamingMode()
.httpConnection.setFixedLengthStreamingMode(contentLength);
но если длина контента действительно не известна заранее, то вы можете использовать режим потоковой передачи фрагментов, установив
HttpURLConnection#setChunkedStreamingMode()
соответственно. Это позволит настроить http -Transfer-Encoding
при работе с HTTP почти всегда полезнее ссылаться на
HttpURLConnection
, а не базовый классURLConnection
(посколькуURLConnection
- это абстрактный класс, когда вы проситеURLConnection.openConnection()
на HTTP URL это то, что вы получите обратно в любом случае).тогда вы можете вместо того, чтобы полагаться на
URLConnection#setDoOutput(true)
чтобы неявно установить метод запроса в POST вместоhttpURLConnection.setRequestMethod("POST")
что некоторые могут найти более естественным (и что также позволяет указать другие методы запроса, такие как поставить,удалить, ...).он также предоставляет полезные константы HTTP, так что вы можете сделать:
int responseCode = httpURLConnection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) {
вдохновленный этим и другими вопросами о SO, я создал минимальный открытый исходный код basic-http-client это воплощает большинство методов, найденных здесь.
google-http-java-client также является отличным ресурсом с открытым исходным кодом.
есть 2 варианта вы можете пойти с HTTP URL хитов: GET / POST
запрос GET :-
HttpURLConnection.setFollowRedirects(true); // defaults to true String url = "https://name_of_the_url"; URL request_url = new URL(url); HttpURLConnection http_conn = (HttpURLConnection)request_url.openConnection(); http_conn.setConnectTimeout(100000); http_conn.setReadTimeout(100000); http_conn.setInstanceFollowRedirects(true); System.out.println(String.valueOf(http_conn.getResponseCode()));
POST запрос : -
HttpURLConnection.setFollowRedirects(true); // defaults to true String url = "https://name_of_the_url" URL request_url = new URL(url); HttpURLConnection http_conn = (HttpURLConnection)request_url.openConnection(); http_conn.setConnectTimeout(100000); http_conn.setReadTimeout(100000); http_conn.setInstanceFollowRedirects(true); http_conn.setDoOutput(true); PrintWriter out = new PrintWriter(http_conn.getOutputStream()); if (urlparameter != null) { out.println(urlparameter); } out.close(); out = null; System.out.println(String.valueOf(http_conn.getResponseCode()));
Я предлагаю вам взглянуть на код kevinsawicki / http-request, его в основном обертка на вершине
HttpUrlConnection
это обеспечивает гораздо более простой API в случае, если вы просто хотите сделать запросы прямо сейчас, или вы можете взглянуть на источники (не слишком большой), чтобы взглянуть на то, как соединения обрабатываются.пример: сделать
GET
запрос типа контентаapplication/json
и некоторые параметры запроса:// GET http://google.com?q=baseball%20gloves&size=100 String response = HttpRequest.get("http://google.com", true, "q", "baseball gloves", "size", 100) .accept("application/json") .body(); System.out.println("Response was: " + response);
Я был очень вдохновлен этим ответом.
Я часто нахожусь в проектах, где мне нужно сделать некоторые HTTP, и я, возможно, не захочу вводить много сторонних зависимостей (которые приносят другие и так далее и так далее и т. д.)
Я начал писать свои собственные программы, основанные на некоторые из этого разговора (не сделано):
package org.boon.utils; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.util.Map; import static org.boon.utils.IO.read; public class HTTP {
тогда есть только куча или статические методы.
public static String get( final String url) { Exceptions.tryIt(() -> { URLConnection connection; connection = doGet(url, null, null, null); return extractResponseString(connection); }); return null; } public static String getWithHeaders( final String url, final Map<String, ? extends Object> headers) { URLConnection connection; try { connection = doGet(url, headers, null, null); return extractResponseString(connection); } catch (Exception ex) { Exceptions.handle(ex); return null; } } public static String getWithContentType( final String url, final Map<String, ? extends Object> headers, String contentType) { URLConnection connection; try { connection = doGet(url, headers, contentType, null); return extractResponseString(connection); } catch (Exception ex) { Exceptions.handle(ex); return null; } } public static String getWithCharSet( final String url, final Map<String, ? extends Object> headers, String contentType, String charSet) { URLConnection connection; try { connection = doGet(url, headers, contentType, charSet); return extractResponseString(connection); } catch (Exception ex) { Exceptions.handle(ex); return null; } }
затем пост...
public static String postBody( final String url, final String body) { URLConnection connection; try { connection = doPost(url, null, "text/plain", null, body); return extractResponseString(connection); } catch (Exception ex) { Exceptions.handle(ex); return null; } } public static String postBodyWithHeaders( final String url, final Map<String, ? extends Object> headers, final String body) { URLConnection connection; try { connection = doPost(url, headers, "text/plain", null, body); return extractResponseString(connection); } catch (Exception ex) { Exceptions.handle(ex); return null; } } public static String postBodyWithContentType( final String url, final Map<String, ? extends Object> headers, final String contentType, final String body) { URLConnection connection; try { connection = doPost(url, headers, contentType, null, body); return extractResponseString(connection); } catch (Exception ex) { Exceptions.handle(ex); return null; } } public static String postBodyWithCharset( final String url, final Map<String, ? extends Object> headers, final String contentType, final String charSet, final String body) { URLConnection connection; try { connection = doPost(url, headers, contentType, charSet, body); return extractResponseString(connection); } catch (Exception ex) { Exceptions.handle(ex); return null; } } private static URLConnection doPost(String url, Map<String, ? extends Object> headers, String contentType, String charset, String body ) throws IOException { URLConnection connection;/* Handle output. */ connection = new URL(url).openConnection(); connection.setDoOutput(true); manageContentTypeHeaders(contentType, charset, connection); manageHeaders(headers, connection); IO.write(connection.getOutputStream(), body, IO.CHARSET); return connection; } private static void manageHeaders(Map<String, ? extends Object> headers, URLConnection connection) { if (headers != null) { for (Map.Entry<String, ? extends Object> entry : headers.entrySet()) { connection.setRequestProperty(entry.getKey(), entry.getValue().toString()); } } } private static void manageContentTypeHeaders(String contentType, String charset, URLConnection connection) { connection.setRequestProperty("Accept-Charset", charset == null ? IO.CHARSET : charset); if (contentType!=null && !contentType.isEmpty()) { connection.setRequestProperty("Content-Type", contentType); } } private static URLConnection doGet(String url, Map<String, ? extends Object> headers, String contentType, String charset) throws IOException { URLConnection connection;/* Handle output. */ connection = new URL(url).openConnection(); manageContentTypeHeaders(contentType, charset, connection); manageHeaders(headers, connection); return connection; } private static String extractResponseString(URLConnection connection) throws IOException { /* Handle input. */ HttpURLConnection http = (HttpURLConnection)connection; int status = http.getResponseCode(); String charset = getCharset(connection.getHeaderField("Content-Type")); if (status==200) { return readResponseBody(http, charset); } else { return readErrorResponseBody(http, status, charset); } } private static String readErrorResponseBody(HttpURLConnection http, int status, String charset) { InputStream errorStream = http.getErrorStream(); if ( errorStream!=null ) { String error = charset== null ? read( errorStream ) : read( errorStream, charset ); throw new RuntimeException("STATUS CODE =" + status + "\n\n" + error); } else { throw new RuntimeException("STATUS CODE =" + status); } } private static String readResponseBody(HttpURLConnection http, String charset) throws IOException { if (charset != null) { return read(http.getInputStream(), charset); } else { return read(http.getInputStream()); } } private static String getCharset(String contentType) { if (contentType==null) { return null; } String charset = null; for (String param : contentType.replace(" ", "").split(";")) { if (param.startsWith("charset=")) { charset = param.split("=", 2)[1]; break; } } charset = charset == null ? IO.CHARSET : charset; return charset; }
Ну вы получить идею....
здесь представлены тесты:
static class MyHandler implements HttpHandler { public void handle(HttpExchange t) throws IOException { InputStream requestBody = t.getRequestBody(); String body = IO.read(requestBody); Headers requestHeaders = t.getRequestHeaders(); body = body + "\n" + copy(requestHeaders).toString(); t.sendResponseHeaders(200, body.length()); OutputStream os = t.getResponseBody(); os.write(body.getBytes()); os.close(); } } @Test public void testHappy() throws Exception { HttpServer server = HttpServer.create(new InetSocketAddress(9212), 0); server.createContext("/test", new MyHandler()); server.setExecutor(null); // creates a default executor server.start(); Thread.sleep(10); Map<String,String> headers = map("foo", "bar", "fun", "sun"); String response = HTTP.postBodyWithContentType("http://localhost:9212/test", headers, "text/plain", "hi mom"); System.out.println(response); assertTrue(response.contains("hi mom")); assertTrue(response.contains("Fun=[sun], Foo=[bar]")); response = HTTP.postBodyWithCharset("http://localhost:9212/test", headers, "text/plain", "UTF-8", "hi mom"); System.out.println(response); assertTrue(response.contains("hi mom")); assertTrue(response.contains("Fun=[sun], Foo=[bar]")); response = HTTP.postBodyWithHeaders("http://localhost:9212/test", headers, "hi mom"); System.out.println(response); assertTrue(response.contains("hi mom")); assertTrue(response.contains("Fun=[sun], Foo=[bar]")); response = HTTP.get("http://localhost:9212/test"); System.out.println(response); response = HTTP.getWithHeaders("http://localhost:9212/test", headers); System.out.println(response); assertTrue(response.contains("Fun=[sun], Foo=[bar]")); response = HTTP.getWithContentType("http://localhost:9212/test", headers, "text/plain"); System.out.println(response); assertTrue(response.contains("Fun=[sun], Foo=[bar]")); response = HTTP.getWithCharSet("http://localhost:9212/test", headers, "text/plain", "UTF-8"); System.out.println(response); assertTrue(response.contains("Fun=[sun], Foo=[bar]")); Thread.sleep(10); server.stop(0); } @Test public void testPostBody() throws Exception { HttpServer server = HttpServer.create(new InetSocketAddress(9220), 0); server.createContext("/test", new MyHandler()); server.setExecutor(null); // creates a default executor server.start(); Thread.sleep(10); Map<String,String> headers = map("foo", "bar", "fun", "sun"); String response = HTTP.postBody("http://localhost:9220/test", "hi mom"); assertTrue(response.contains("hi mom")); Thread.sleep(10); server.stop(0); } @Test(expected = RuntimeException.class) public void testSad() throws Exception { HttpServer server = HttpServer.create(new InetSocketAddress(9213), 0); server.createContext("/test", new MyHandler()); server.setExecutor(null); // creates a default executor server.start(); Thread.sleep(10); Map<String,String> headers = map("foo", "bar", "fun", "sun"); String response = HTTP.postBodyWithContentType("http://localhost:9213/foo", headers, "text/plain", "hi mom"); System.out.println(response); assertTrue(response.contains("hi mom")); assertTrue(response.contains("Fun=[sun], Foo=[bar]")); Thread.sleep(10); server.stop(0); }
вы можете найти здесь:
https://github.com/RichardHightower/boon
моя цель-предоставить общие вещи, которые можно было бы сделать немного проще....
обновление
новый HTTP-клиент поставляется с Java 9, но как часть Инкубаторный модуль с именем
jdk.incubator.httpclient
. Модули инкубатора являются средство передачи нефинансовых API в руки разработчиков, в то время как Прогресс API в направлении либо завершения, либо удаления в будущем освобождать.в Java 9, вы можете отправить
GET
запрос типа:// GET HttpResponse response = HttpRequest .create(new URI("http://www.stackoverflow.com")) .headers("Foo", "foovalue", "Bar", "barvalue") .GET() .response();
затем вы можете изучить вернулся
HttpResponse
:int statusCode = response.statusCode(); String responseBody = response.body(HttpResponse.asString());
так как этот новый HTTP-клиент находится в
java.httpclient
jdk.incubator.httpclient
модуль, вы должны объявить эту зависимость в свойmodule-info.java
file:module com.foo.bar { requires jdk.incubator.httpclient; }
Первоначально я был введен в заблуждение этим статьи выступает
HttpClient
.позже я понял, что
HttpURLConnection
собирается остаться от этого статьисогласно блогу Google:
HTTP-клиент Apache имеет меньше ошибок на Eclair и Froyo. Это лучший выбор для этих релизов. Для пряников HttpURLConnection является лучшим выбором. Его простой API и небольшой размер делает его отлично подходит для Андроид.
прозрачное сжатие и кэширование ответов уменьшают использование сети, улучшают скорость и экономят батарею. Новые приложения должны использовать HttpURLConnection; именно там мы будем тратить нашу энергию в будущем.
после прочтения в этой статье и некоторые другие вопросы стека над потоком, я убежден, что
HttpURLConnection
собирается остаться на более длительные сроки.некоторые из вопросов SE в пользу
HttpURLConnections
:
вы также можете использовать
JdkRequest
С jcabi-http (Я разработчик), который делает всю эту работу за вас, украшая HttpURLConnection, запуская HTTP-запросы и разбирая ответы, например:String html = new JdkRequest("http://www.google.com").fetch().body();
проверьте это сообщение в блоге для получения дополнительной информации:http://www.yegor256.com/2014/04/11/jcabi-http-intro.html
есть еще OkHttp, который является HTTP-клиентом, который эффективен по умолчанию:
- поддержка HTTP/2 позволяет всем запросам к одному хосту совместно использовать сокет.
- пул соединений уменьшает задержку запроса (если HTTP/2 недоступен).
- прозрачный GZIP сжимает размеры загрузки.
- кэширование ответов позволяет полностью избежать сети для повторных запросов.
сначала создайте экземпляр
OkHttpClient
:OkHttpClient client = new OkHttpClient();
тогда приготовь свой
GET
запрос:Request request = new Request.Builder() .url(url) .build();
наконец, использовать
OkHttpClient
отправить подготовленныйRequest
:Response response = client.newCall(request).execute();
для более подробной информации, вы можете обратиться к OkHttp по