Pytanie Jak używać java.net.URLConnection do uruchamiania i obsługi żądań HTTP


Zastosowanie java.net.URLConnection jest często zadawana tutaj, a Samouczek Oracle jest także zwięźle o tym.

Ten samouczek w zasadzie pokazuje tylko, jak wystrzelić żądanie GET i przeczytać odpowiedź. Nie wyjaśnia w żaden sposób, jak z niego korzystać, aby między innymi wykonać żądanie POST, ustawić nagłówki żądań, przeczytać nagłówki odpowiedzi, zaadresować pliki cookie, przesłać formularz HTML, przesłać plik itp.

Więc, jak mogę użyć java.net.URLConnection uruchamiać "zaawansowane" żądania HTTP?


1762


pochodzenie




Odpowiedzi:


Najpierw zrzeczenie się odpowiedzialności: opublikowane fragmenty kodu są podstawowymi przykładami. Będziesz musiał poradzić sobie z trywialnością IOExceptions i RuntimeExceptionSą jak NullPointerException, ArrayIndexOutOfBoundsException i współzawodniczy.


Przygotowanie

Najpierw musimy znać przynajmniej URL i zestaw znaków. Parametry są opcjonalne i zależą od wymagań funkcjonalnych.

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));

Parametry zapytania muszą być w name=value formatuj i łącz się przez &. Zwykle też Kodowanie URL parametry zapytania z określonym zestawem znaków za pomocą URLEncoder#encode().

The String#format() jest po prostu dla wygody. Wolę go, gdy potrzebuję operatora konkatenacji ciągów + więcej niż dwukrotnie.


Wypalanie a HTTP GET żądanie z (opcjonalnie) parametrami zapytania

To banalne zadanie. Jest to domyślna metoda żądania.

URLConnection connection = new URL(url + "?" + query).openConnection();
connection.setRequestProperty("Accept-Charset", charset);
InputStream response = connection.getInputStream();
// ...

Dowolny ciąg zapytania powinien być połączony z adresem URL za pomocą ?. The Accept-Charset nagłówek może podpowiedzieć serwerowi, na czym polega kodowanie parametrów. Jeśli nie wyślesz żadnego ciągu zapytania, możesz opuścić Accept-Charset nagłówek dalej. Jeśli nie musisz ustawiać żadnych nagłówków, możesz nawet użyć URL#openStream() metoda skrótu.

InputStream response = new URL(url).openStream();
// ...

Tak czy inaczej, jeśli druga strona jest HttpServlet, a następnie jego doGet() metoda zostanie wywołana, a parametry będą dostępne przez HttpServletRequest#getParameter().

Do celów testowych możesz wydrukować treść odpowiedzi na standardowe wyjście, jak poniżej:

try (Scanner scanner = new Scanner(response)) {
    String responseBody = scanner.useDelimiter("\\A").next();
    System.out.println(responseBody);
}

Wypalanie a HTTP POST zapytanie z parametrami zapytania

Ustawianie URLConnection#setDoOutput() do true niejawnie ustawia metodę żądania na POST. Standardowy formularz HTTP POST, jak w przypadku formularzy internetowych, jest typu application/x-www-form-urlencoded w którym łańcuch zapytania jest zapisywany w treści żądania.

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

Uwaga: gdy chcesz przesłać formularz HTML programowo, nie zapomnij wziąć name=value par dowolnych <input type="hidden"> elementy do ciągu zapytania i oczywiście również do name=value para <input type="submit"> element, który chcesz "nacisnąć" programowo (ponieważ jest zwykle używany po stronie serwera, aby odróżnić naciśnięcie przycisku, a jeśli tak, to który).

Możesz także obsadzić uzyskane URLConnection do HttpURLConnection i użyj jej HttpURLConnection#setRequestMethod() zamiast. Ale jeśli próbujesz użyć połączenia dla wyjścia, które nadal musisz ustawić URLConnection#setDoOutput() do true.

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

Tak czy inaczej, jeśli druga strona jest HttpServlet, a następnie jego doPost() metoda zostanie wywołana, a parametry będą dostępne przez HttpServletRequest#getParameter().


Właściwie uruchamianie żądania HTTP

Możesz jawnie wywołać żądanie HTTP przy pomocy URLConnection#connect(), ale żądanie zostanie automatycznie uruchomione na żądanie, gdy chcesz uzyskać jakiekolwiek informacje o odpowiedzi HTTP, na przykład o treści odpowiedzi URLConnection#getInputStream()i tak dalej. Powyższe przykłady dokładnie to robią, więc connect() połączenie jest w rzeczywistości zbędne.


Zbieranie informacji o odpowiedzi HTTP

  1. Status odpowiedzi HTTP:

    Potrzebujesz HttpURLConnection tutaj. Jeśli to konieczne, prześlij ją najpierw.

    int status = httpConnection.getResponseCode();
    
  2. Nagłówki odpowiedzi HTTP:

    for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
        System.out.println(header.getKey() + "=" + header.getValue());
    }
    
  3. Kodowanie odpowiedzi HTTP:

    Kiedy Content-Type zawiera charset parametr, wtedy treść odpowiedzi jest prawdopodobnie oparta na tekście i chcielibyśmy przetworzyć treść odpowiedzi z kodowaniem znaków określonego po stronie serwera.

    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.
    }
    

Utrzymanie sesji

Sesja po stronie serwera jest zazwyczaj wspierana przez plik cookie. Niektóre formularze internetowe wymagają zalogowania i / lub są śledzone przez sesję. Możesz użyć CookieHandler API do obsługi plików cookie. Musisz przygotować CookieManager z a CookiePolicy z ACCEPT_ALL przed wysłaniem wszystkich żądań 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();
// ...

Należy pamiętać, że wiadomo, że nie zawsze działa poprawnie we wszystkich okolicznościach. Jeśli ci się nie uda, najlepiej jest ręcznie zebrać i ustawić nagłówki ciasteczek. Zasadniczo musisz pobrać wszystkie Set-Cookie nagłówki z odpowiedzi logowania lub pierwszego GET zażądać, a następnie przekazać to kolejne wnioski.

// 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] jest tam, aby pozbyć się atrybutów plików cookie, które są nieistotne dla strony serwera, jak expires, pathitp. Można również użyć cookie.substring(0, cookie.indexOf(';')) zamiast split().


Tryb strumieniowania

The HttpURLConnection będzie domyślnie buforował Cały Żądaj treści przed jej wysłaniem, niezależnie od tego, czy samodzielnie ustawiłeś stałą długość treści connection.setRequestProperty("Content-Length", contentLength);. To może spowodować OutOfMemoryExceptions za każdym razem, gdy wysyłają duże żądania POST (np. przesyłanie plików). Aby tego uniknąć, chciałbyś ustawić HttpURLConnection#setFixedLengthStreamingMode().

httpConnection.setFixedLengthStreamingMode(contentLength);

Ale jeśli długość zawartości nie jest wcześniej znana, można użyć trybu strumieniowego z porcją, ustawiając HttpURLConnection#setChunkedStreamingMode() odpowiednio. Spowoduje to ustawienie HTTP Transfer-Encoding nagłówek do chunked co wymusi wysłanie treści żądania w porcjach. Poniższy przykład wyśle ​​ciało w kawałkach o wielkości 1KB.

httpConnection.setChunkedStreamingMode(1024);

Agent użytkownika

Może się tak zdarzyć żądanie zwraca nieoczekiwaną odpowiedź, podczas gdy działa dobrze z prawdziwą przeglądarką. Strona serwera prawdopodobnie blokuje żądania w oparciu o User-Agent nagłówek żądania. The URLConnection domyślnie ustawi to na Java/1.6.0_19 gdzie ostatnia część jest oczywiście wersją JRE. Możesz to zmienić w następujący sposób:

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.

Użyj ciągu User-Agent z a ostatnia przeglądarka.


Obsługa błędów

Jeśli kod odpowiedzi HTTP jest 4nn (Błąd klienta) lub 5nn (Błąd serwera), możesz chcieć przeczytać HttpURLConnection#getErrorStream() aby sprawdzić, czy serwer wysłał użyteczne informacje o błędzie.

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

Jeśli kod odpowiedzi HTTP to -1, oznacza to, że coś poszło nie tak podczas obsługi połączenia i odpowiedzi. The HttpURLConnection implementacja jest w starszych JRE nieco chaotyczna z utrzymaniem połączeń przy życiu. Możesz go wyłączyć, ustawiając http.keepAlive właściwość systemu do false. Możesz to zrobić programowo na początku swojej aplikacji przez:

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

Przesyłanie plików

Normalnie używałbyś multipart/form-data kodowanie dla mieszanej treści POST (dane binarne i dane znakowe). Kodowanie jest bardziej szczegółowo opisane w 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();
}

Jeśli druga strona jest HttpServlet, a następnie jego doPost() metoda zostanie wywołana, a części będą dostępne przez HttpServletRequest#getPart() (zauważ, więc nie  getParameter() i tak dalej!). The getPart() Metoda jest jednak stosunkowo nowa, została wprowadzona w Servlet 3.0 (Glassfish 3, Tomcat 7, itp.). Przed Servlet 3.0, twój najlepszy wybór jest używany Apache Commons FileUpload parsować multipart/form-data żądanie. Zobacz także ta odpowiedź na przykład zarówno podejścia FileUpload, jak i Servelt 3.0.


Radzenie sobie z niezaufanymi lub źle skonfigurowanymi stronami HTTPS

Czasami musisz połączyć adres URL HTTPS, być może dlatego, że piszesz skrobaczkę internetową. W takim przypadku możesz prawdopodobnie zmierzyć się z javax.net.ssl.SSLException: Not trusted server certificate w niektórych witrynach HTTPS, które nie aktualizują swoich certyfikatów SSL, lub java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] found lub javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name w niektórych źle skonfigurowanych witrynach HTTPS.

Kolejny jednorazowy bieg static inicjator w swojej klasie skrobaków internetowych powinien HttpsURLConnection bardziej wyrozumiałe dla tych witryn HTTPS, a zatem nie wyrzucaj już tych wyjątków.

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);
    }
}

Ostatnie słowa

The Apache HttpComponents HttpClient jest dużo wygodniej w tym wszystkim :)


Parsowanie i wyodrębnianie HTML

Jeśli chcesz tylko parsować i wyodrębniać dane z HTML, lepiej użyj parsera HTML takiego jak Jsoup


2526



Powinieneś najpierw umieścić link Apache, aby ludzie szukający rozwiązania znaleźli go szybciej;) - ZeissS
@ivanceras: jeśli nie możesz go zagotować w oparciu o informacje zawarte w tej odpowiedzi, naciśnij Ask Question przycisk po prawej stronie. - BalusC
@Brais: przeczytaj specyfikację. The -- część nie jest częścią samej granicy. To tylko ciąg separatora. Wycofałem Twoją nieprawidłową edycję. - BalusC
@BalusC bardzo dziękuje za tak doskonały samouczek. Dodaj również nagłówek "Zamykanie strumieni / połączeń". Jestem bardzo zdezorientowany, kiedy i które strumienie / połączenia są zamykane.
Smutne jest to, że na Androidzie jest nie zaleca się korzystanie z Apache HttpClient teraz i HttpURLConnection jest okrutny. android-developers.blogspot.in/2011/09/... - yati sagade


Podczas pracy z HTTP prawie zawsze lepiej jest się z nim zapoznać HttpURLConnection zamiast klasy bazowej URLConnection (od URLConnection jest klasą abstrakcyjną, kiedy pytasz URLConnection.openConnection() na adresie URL HTTP to i tak otrzymasz.

Wtedy możesz zamiast polegać na URLConnection#setDoOutput(true) niejawnie ustawić metodę żądania na STANOWISKO zamiast tego httpURLConnection.setRequestMethod("POST") które niektórzy mogą znaleźć bardziej naturalne (i które pozwala również określić inne metody żądania, takie jak POŁOŻYĆ, KASOWAĆ, ...).

Zapewnia również przydatne stałe HTTP, dzięki czemu możesz:

int responseCode = httpURLConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {

84



setDoOutPut true był moim problemem, który ustawił mój GET na POST. Dzięki - Patrick Kafka
Jeśli próbujesz zapisać dane do strumienia wyjściowego musi wciąż ustawione setDoOutput() do true w przeciwnym razie zostanie zgłoszony wyjątek (nawet jeśli ty setRequestMethod("POST")). Aby było jasne: ustawienie URLConnection#setDoOutput(true) do true niejawnie ustawia metodę żądania na POST, ale ustawienie httpURLConnection.setRequestMethod("POST") na POST nie domyślnie ustawiony setDoOutput() do true. - Tony Chan


Zainspirowany tym i innymi pytaniami na SO, stworzyłem minimalny open source basic-http-client który ucieleśnia większość technik tutaj znalezionych.

google-http-java-client jest także doskonałym źródłem open source.


49



Po prostu myślałem tak samo. Ale może być również miło mieć bibliotekę barebones / simple Java, która używa tylko kodu URLConnection, jak tutaj przedstawiono, który enkapsuluje kod do prostszych metod wykonywania HTTP GET, POST, itp. Biblioteka może być następnie skompilowana i spakowana jako JAR i importowane / używane w kodzie Java lub plik klasy źródłowej mogą być zawarte w projekcie Java, jeśli zewnętrzne pliki JAR nie są pożądane. Można to zrobić za pomocą innych bibliotek, takich jak Apache, itp., Ale jest to więcej bólu w porównaniu do prostej biblioteki klasy 1 przy użyciu metody URLConnection. - David
rapidvaluesolutions.com/tech_blog/... faworyzuje HttpURLConnection przez HttpClient - Ravindra babu


Istnieją dwie opcje, które można przejść za pomocą odsłon HTTP URL: GET / POST

Żądanie 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()));

Żądanie 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()));

22



Jest jeszcze kilka innych: PUT, DELETE, HEAD, ... - user207421
Jak wyświetlić rzeczywistą odpowiedź JSON? - Surenzxx


Sugeruję, żebyś rzucił okiem na kod kevinsawicki / http-request, jest to w zasadzie opakowanie na HttpUrlConnection zapewnia znacznie prostsze API w przypadku, gdy chcesz teraz wysyłać żądania lub możesz przyjrzeć się źródłom (nie jest zbyt duże), aby spojrzeć na sposób obsługi połączeń.

Przykład: Utwórz GET żądanie z typem treści application/json i niektóre parametry zapytania:

// 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);

20





Byłem bardzo zainspirowany tą odpowiedzią.

Często zajmuję się projektami, w których potrzebuję trochę HTTP i nie chcę wprowadzać wielu zależności od innych podmiotów (które dostarczają innych itd. Itd.)

Zacząłem pisać własne narzędzia w oparciu o niektóre z tych rozmów (a nie gdzie to zrobiono):

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 {

Wtedy jest tylko garść lub statyczne metody.

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;
    }
}

Następnie opublikuj ...

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;
}

Cóż, masz pomysł ....

Oto testy:

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);


}

Resztę można znaleźć tutaj:

https://github.com/RichardHightower/boon

Moim celem jest zapewnienie wspólnych rzeczy, które chciałbym zrobić w nieco prostszy sposób niż ...


19



To dziwne, że w doPost metoda jest charset param, który jest używany do ustawiania nagłówka żądania, ale wtedy dane są zapisywane za pomocą jakiegoś zakodowanego zestawu znaków IO.CHARSET. Błąd? - Vit Khudenko


Aktualizacja

Nowy klient HTTP dostarczany z Java 9, ale jako część   Nazwa modułu inkubatora jdk.incubator.httpclient. Moduły inkubatora są   sposób umieszczania nieostatecznych API w rękach programistów, podczas gdy   Postępy API w kierunku finalizacji lub usunięcia w przyszłości   wydanie.

W Javie 9 możesz wysłać GET Żądanie jak:

// GET
HttpResponse response = HttpRequest
    .create(new URI("http://www.stackoverflow.com"))
    .headers("Foo", "foovalue", "Bar", "barvalue")
    .GET()
    .response();

Następnie możesz sprawdzić zwrócone HttpResponse:

int statusCode = response.statusCode();
String responseBody = response.body(HttpResponse.asString());

Od tego nowego klienta HTTP jest java.httpclient  jdk.incubator.httpclient moduł, powinieneś zadeklarować tę zależność w swoim module-info.java plik:

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

15





Początkowo zostałem wprowadzony w błąd przez to artykuł co faworyzuje HttpClient.

Później byłem tego świadomy HttpURLConnection zamierza pozostać z tego artykuł

Zgodnie z blogiem Google:

Klient HTTP Apache ma mniej błędów na Eclair i Froyo. To najlepszy wybór dla tych wydań. Dla Gingerbread HttpURLConnection to najlepszy wybór. Jego prosty interfejs API i niewielkie rozmiary sprawiają, że doskonale nadaje się do systemu Android.

Przejrzyste buforowanie kompresji i odpowiedzi zmniejsza zużycie sieci, zwiększa szybkość i oszczędza baterię. Nowe aplikacje powinny używać HttpURLConnection; właśnie tam będziemy wydawać energię na przyszłość.

Po odczytaniu Ten artykuł i kilka innych pytań związanych z przepełnieniem stosu, jestem tego pewien HttpURLConnection zostanie na dłużej.

Niektóre pytania na temat SE sprzyjają HttpURLConnections:

W systemie Android zrób żądanie POST z danymi zakodowanego adresu URL bez użycia UrlEncodedFormEntity

HttpPost działa w projekcie Java, a nie w systemie Android


14





Możesz także użyć JdkRequest od jcabi-http (Jestem programistą), który robi to wszystko dla ciebie, dekorowanie HttpURLConnection, uruchamianie żądań HTTP i odpowiedzi na parsowanie, na przykład:

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

Sprawdź ten wpis na blogu, aby uzyskać więcej informacji: http://www.yegor256.com/2014/04/11/jcabi-http-intro.html


11



Jak obchodzić się z ciasteczkami? - Dejell


Jest również OkHttp, który jest domyślnie skutecznym klientem HTTP:

  • Obsługa HTTP / 2 umożliwia wszystkim żądaniom tego samego hosta współdzielenie gniazda.
  • Pule połączeń zmniejszają opóźnienie żądania (jeśli HTTP / 2 nie jest dostępny).
  • Przezroczysty GZIP zmniejsza rozmiary pobierania.
  • Buforowanie odpowiedzi pozwala całkowicie uniknąć sieci w przypadku powtarzających się żądań.

Najpierw utwórz instancję OkHttpClient:

OkHttpClient client = new OkHttpClient();

Następnie przygotuj swój GET żądanie:

Request request = new Request.Builder()
      .url(url)
      .build();

wreszcie użyj OkHttpClient wysłać przygotowany Request:

Response response = client.newCall(request).execute();

Aby uzyskać więcej informacji, zapoznaj się z Dokumentacja OkHttpa


11