Как использовать java. net. URLConnection для запуска и обработки HTTP-запросов


использование java.net.URLConnection спрашивают о довольно часто здесь, и то Oracle tutorial и слишком кратко об этом.

этот учебник в основном показывает только, как запустить запрос GET и прочитать ответ. Он нигде не объясняет, как использовать его для выполнения запроса POST, установки заголовков запросов, чтения заголовков ответов, работы с файлами cookie, отправки HTML-формы, загрузки файла и т. д.

Итак, как я могу использовать java.net.URLConnection огонь и обрабатывать" продвинутые " HTTP-запросы?

11 1780

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&param2=%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() to true неявно задает метод запроса для 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 to HttpURLConnection и использовать его HttpURLConnection#setRequestMethod() вместо. Но если вы пытаетесь использовать соединение для вывода, вам все равно нужно установить URLConnection#setDoOutput() to true.

HttpURLConnection httpConnection = (HttpURLConnection) new URL(url).openConnection();
httpConnection.setRequestMethod("POST");
// ...

в любом случае, если другая сторона HttpServlet, то doPost() метод будет вызван и параметры будут доступны мимо HttpServletRequest#getParameter().


фактически запуск HTTP-запроса

вы можете запустить HTTP-запрос явно с URLConnection#connect(), но запрос будет автоматически запущен по требованию, когда вы хотите получить любую информацию о HTTP-ответе, например, тело ответа с помощью URLConnection#getInputStream() и так далее. Приведенные выше примеры делают именно это, поэтому connect() звонок на самом деле излишний.


сбор информации HTTP-ответа

  1. состояние ответа HTTP:

    вам понадобится HttpURLConnection здесь. Бросьте его сначала, если это необходимо.

    int status = httpConnection.getResponseCode();
    
  2. заголовки ответов HTTP:

    for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
        System.out.println(header.getKey() + "=" + header.getValue());
    }
    
  3. кодировка ответа HTTP:

    когда Content-Type содержит a charset параметр, тогда тело ответа, вероятно, основано на тексте, и мы хотели бы обработать тело ответа с указанной кодировкой символов на стороне сервера.

    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);. Это может привести к OutOfMemoryExceptions всякий раз, когда вы одновременно отправляете большие запросы 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.httpclientjdk.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:

на Android сделайте запрос POST с данными формы в кодировке URL без использования UrlEncodedFormEntity

HttpPost работает в проекте Java, а не в Android

вы также можете использовать 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 по

Если вы используете http get, пожалуйста, удалите эту строку

urlConnection.setDoOutput(true);