Як використовувати java.net.URLCзв'язок для запуску та обробки HTTP-запитів?


1946

Використання java.net.URLConnectionзапитують про досить часто тут, і підручник Oracle є занадто коротким про це.

Цей підручник в основному лише показує, як запустити GET-запит та прочитати відповідь. Він ніде не пояснює, як використовувати його, щоб серед інших виконати запит POST, встановити заголовки запитів, прочитати заголовки відповідей, розправити файли cookie, надіслати HTML-форму, завантажити файл тощо.

Отже, як я можу використовувати java.net.URLConnectionдля запуску та обробки "просунутих" HTTP-запитів?

Відповіді:


2711

Спершу заздалегідь відмови від відповідальності: опубліковані фрагменти коду - всі основні приклади. Вам потрібно буде впоратися з тривіальними IOExceptionі RuntimeExceptionподібними 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));

Параметри запиту повинні бути у name=valueформаті та об'єднані &. Зазвичай ви також кодуєте URL-адреси параметрів запиту за допомогою вказаної таблиці URLEncoder#encode().

Це 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();
// ...

У будь-якому випадку, якщо інша сторона є a 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()для 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в HttpURLConnectionі використовувати його HttpURLConnection#setRequestMethod()замість. Але якщо ви намагаєтеся використовувати з'єднання для виведення ви все ще потрібно встановити URLConnection#setDoOutput()в true.

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

У будь-якому випадку, якщо інша сторона є a 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містить 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. Деякі веб-форми вимагають, щоб ви входили в систему та / або відслідковувались сеансом. Ви можете використовувати CookieHandlerAPI для підтримки файлів 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]);
}
// ...

split(";", 2)[0]Знаходиться там , щоб позбутися від атрибутів печива , які НЕ мають відношення до стороні сервера , як expires, pathі т.д. Крім того , можна також використовувати cookie.substring(0, cookie.indexOf(';'))замість split().


Режим потокового передачі

HttpURLConnectionБуде за замовчуванням буфера всього тіла запиту перед тим як відправити його, незалежно від того, чи були ви встановити фіксовану довжину змісту самостійно , використовуючи connection.setRequestProperty("Content-Length", contentLength);. Це може спричинити OutOfMemoryExceptions, коли ви одночасно надсилаєте великі POST-запити (наприклад, завантаження файлів). Щоб цього уникнути, ви хочете встановити HttpURLConnection#setFixedLengthStreamingMode().

httpConnection.setFixedLengthStreamingMode(contentLength);

Але якщо довжина вмісту заздалегідь не відома, то ви можете скористатися режимом потокового потоку, встановивши HttpURLConnection#setChunkedStreamingMode()відповідним чином. Це встановить Transfer-Encodingзаголовок HTTP, до chunkedякого змусить орган запиту надсилати шматки. Наведений нижче приклад відправить тіло шматками в 1 КБ.

httpConnection.setChunkedStreamingMode(1024);

Користувач-агент

Може статися, що запит повертає несподівану відповідь, при цьому він справно працює з реальним веб-браузером . Сторона сервера, ймовірно, блокує запити на основі User-Agentзаголовка запиту. За URLConnectionзамовчуванням за замовчуванням встановлено його Java/1.6.0_19там, де остання частина очевидно є версією JRE. Ви можете змінити це так:

connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"); // Do as if you're using Chrome 41 on Windows 7.

Використовуйте рядок User-Agent з недавнього браузера .


Помилка обробки

Якщо кодом відповіді HTTP є 4nn(Помилка клієнта) або 5nn(Помилка сервера), ви можете прочитати, HttpURLConnection#getErrorStream()щоб побачити, чи надіслав сервер яку-небудь корисну інформацію про помилку.

InputStream error = ((HttpURLConnection) connection).getErrorStream();

Якщо код відповіді HTTP дорівнює -1, щось не вдалося попрацювати з підключенням та відповіддю. HttpURLConnectionРеалізація в старих JREs кілька баггі зі збереженням з'єднання в живих. Ви можете вимкнути його, встановивши для http.keepAliveсистемного властивості значення false. Ви можете це зробити програмно на початку програми:

System.setProperty("http.keepAlive", "false");

Завантаження файлів

Зазвичай ви використовуєте multipart/form-dataкодування для змішаного вмісту POST (двійкові та символьні дані). Кодування більш детально описано в RFC2388 .

String param = "value";
File textFile = new File("/path/to/file.txt");
File binaryFile = new File("/path/to/file.bin");
String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.
String CRLF = "\r\n"; // Line separator required by multipart/form-data.
URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

try (
    OutputStream output = connection.getOutputStream();
    PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, charset), true);
) {
    // Send normal param.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"param\"").append(CRLF);
    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
    writer.append(CRLF).append(param).append(CRLF).flush();

    // Send text file.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"textFile\"; filename=\"" + textFile.getName() + "\"").append(CRLF);
    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF); // Text file itself must be saved in this charset!
    writer.append(CRLF).flush();
    Files.copy(textFile.toPath(), output);
    output.flush(); // Important before continuing with writer!
    writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.

    // Send binary file.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"binaryFile\"; filename=\"" + binaryFile.getName() + "\"").append(CRLF);
    writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(binaryFile.getName())).append(CRLF);
    writer.append("Content-Transfer-Encoding: binary").append(CRLF);
    writer.append(CRLF).flush();
    Files.copy(binaryFile.toPath(), output);
    output.flush(); // Important before continuing with writer!
    writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.

    // End of multipart/form-data.
    writer.append("--" + boundary + "--").append(CRLF).flush();
}

Якщо інша сторона є a HttpServlet, тоді її doPost()метод буде викликаний, і частини будуть доступні HttpServletRequest#getPart()(зверніть увагу, таким чином, не getParameter() і так далі!). Однак getPart()метод є відносно новим, він впроваджений у Servlet 3.0 (Glassfish 3, Tomcat 7 тощо). До Servlet 3.0 ваш найкращий вибір - це використовувати Apache Commons FileUpload для розбору multipart/form-dataзапиту. Також дивіться цю відповідь для прикладів як FileUpload, так і підходів Servelt 3.0.


Справа з ненадійними або неправильно налаштованими HTTPS-сайтами

Іноді потрібно підключити URL-адресу HTTPS, можливо, тому, що ви пишете веб-скребок. У цьому випадку ви, можливо, зіткнетеся з a javax.net.ssl.SSLException: Not trusted server certificateна деяких HTTPS-сайтах, які не оновлюють свої SSL-сертифікати, або на java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] foundабо javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_nameнеправильно налаштованих HTTPS-сайтах.

Наступний одноразовий staticініціалізатор у вашому класі веб-скребків повинен зробити HttpsURLConnectionбільш м'яким щодо цих HTTPS-сайтів і, таким чином, більше не кидати ці винятки.

static {
    TrustManager[] trustAllCertificates = new TrustManager[] {
        new X509TrustManager() {
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null; // Not relevant.
            }
            @Override
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
                // Do nothing. Just allow them all.
            }
            @Override
            public void checkServerTrusted(X509Certificate[] certs, String authType) {
                // Do nothing. Just allow them all.
            }
        }
    };

    HostnameVerifier trustAllHostnames = new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true; // Just allow them all.
        }
    };

    try {
        System.setProperty("jsse.enableSNIExtension", "false");
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCertificates, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(trustAllHostnames);
    }
    catch (GeneralSecurityException e) {
        throw new ExceptionInInitializerError(e);
    }
}

Останні слова

Apache HttpComponents HttpClient є набагато зручнішим в цьому все :)


Розбір та вилучення HTML

Якщо все, що вам потрібно, - це розбір та вилучення даних з HTML, тоді краще скористайтеся HTML-аналізатором, таким як Jsoup


119
Спершу слід розмістити посилання на апачі, щоб люди, які шукають рішення, знаходили його швидше;)
ZeissS

40
@ivanceras: Якщо ви не можете закинути його на основі інформації у цій відповіді, натисніть Ask Questionкнопку в правій верхній частині.
BalusC

3
@Brais: Прочитайте специфікацію. --Частина не є частиною самого кордону. Це просто розділовий рядок. Я повернув вашу недійсну редакцію.
BalusC

7
@BalusC дякую за такий ідеальний підручник. Також додайте заголовок на кшталт "Закриття потоків / з'єднань". Я дуже розгублений, коли і які потоки / з'єднання потрібно закрити.

10
Сумно полягає в тому, що на Android не рекомендується зараз використовувати Apache HttpClientі HttpURLConnectionє жорстоким. android-developers.blogspot.in/2011/09/…
yati sagade

91

Під час роботи з HTTP майже завжди корисніше посилатися на HttpURLConnectionбазовий клас, а не на базовий клас URLConnection(оскільки URLConnectionце абстрактний клас, коли ви звертаєтесь за URLConnection.openConnection()URL-адресою HTTP - це те, що ви все одно повернете).

Тоді ви можете замість того, щоб покладатися на URLConnection#setDoOutput(true)неявне встановлення методу запиту на POST, замість цього зробити httpURLConnection.setRequestMethod("POST")якісь більш природними (а також дозволяє вказати інші методи запиту, такі як PUT , DELETE , ...).

Він також пропонує корисні константи HTTP, щоб ви могли:

int responseCode = httpURLConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {

1
setDoOutPut true було моєю проблемою, яка встановила мій GET на POST. Спасибі
Патрік Кафка

22
Якщо ви намагаєтеся записати дані у вихідний потік, вам все одно потрібно встановити, що setDoOutput()в trueіншому випадку буде викинуто виняток (навіть якщо ви setRequestMethod("POST")). Для того, щоб бути ясно: установка URLConnection#setDoOutput(true)для trueнеявно встановлює метод запиту до POST, але установка httpURLConnection.setRequestMethod("POST")в POST ніяк НЕ неявно встановлюється setDoOutput()в true.
Тоні Чан

54

Натхненний цим та іншими питаннями щодо SO, я створив мінімальний відкритий код basic-http-client, який втілює більшість методів, знайдених тут.

google-http-java-client також є чудовим ресурсом з відкритим кодом.


Я просто думав те саме. Але також може бути непогано мати баребони / просту бібліотеку Java, яка використовує лише код URLConnection, представлений тут, який інкапсулює код у більш прості методи, щоб зробити HTTP GET, POST тощо. Бібліотеку можна потім компілювати та упакувати як JAR та імпортований / використаний в коді Java або файл класу-джерела можна включити в проект Java, якщо зовнішні JAR-адреси не бажані. Це можна зробити з іншими бібліотеками, такими як Apache тощо, але це біль, ніж порівняно з простою бібліотекою класів 1 файлів за допомогою URLConnection.
Давид

rapidvaluesolutions.com/tech_blog/… надає перевагу HttpURLCпідключення за HttpClient
Равіндра бабу,

24

Я пропоную вам поглянути на код на kevinsawicki / http-запит , в основному його обгортка 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);

24

Є два варіанти, за допомогою HTTP-хітів URL-адрес: GET / 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);
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()));

3
Як ви можете переглянути фактичну відповідь JSON?
Сора

21

Мене також дуже надихнула ця відповідь.

Я часто буваю в проектах, де мені потрібно робити якісь 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

Моя мета - надати загальні речі, які б хотілося зробити трохи простіше, ніж тоді ...


2
Як не дивно, що в doPostметоді є charsetпарам, який використовується для встановлення заголовка запиту, але потім дані записуються за допомогою жорсткого коду IO.CHARSET. Жук?
Віт Худенко

21

Оновлення

Новий клієнт 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файлі:

module com.foo.bar {
    requires jdk.incubator.httpclient;
}

1
Подальше оновлення: модуль вийшов з інкубаційного стану. Зараз java.net.http , а не jdk.incubator.httpclient.
VGR

17

Спочатку мене ввели в оману ця стаття, яка сприяє HttpClient.

Пізніше я зрозумів, що HttpURLConnectionзбираюся зупинитися на цій статті

Відповідно до блогу Google :

HTTP-клієнт Apache має менше помилок на Eclair та Froyo. Це найкращий вибір для цих випусків. Для пряників HttpURLConnection - найкращий вибір. Його простий API та невеликий розмір робить його чудовим для Android.

Прозоре кешування компресії та відповіді зменшує використання мережі, покращує швидкість та економить акумулятор. Нові програми повинні використовувати HttpURLConnection; саме там ми будемо витрачати свою енергію вперед.

Прочитавши цю статтю та деякі інші стеки над питаннями потоку, я переконаний, що HttpURLConnectionтриватиме довше.

Деякі з питань ДП на користь HttpURLConnections:

На Android зробіть запит POST з даними кодованої URL-адреси без використання UrlEncodedFormEntity

HttpPost працює в проекті Java, а не в Android


15

Також є 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


14

Ви можете також використовувати JdkRequestвід jcabi-клієнта (я розробник), який робить всю цю роботу за вас, прикрашаючи HttpURLConnection, стріляючи HTTP запитів і аналізу відповідей, наприклад:

String html = new JdkRequest("http://www.google.com").fetch().body();

Перегляньте це повідомлення в блозі для отримання додаткової інформації: http://www.yegor256.com/2014/04/11/jcabi-http-intro.html


1
Як ви обробляєте файли cookie?
Dejell

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.