Jak korzystać z java.net.URLConnection do uruchamiania i obsługi żądań HTTP?

1946

java.net.URLConnectionCzęsto pyta się o użycie , a samouczek Oracle jest zbyt zwięzły.

Ten samouczek w zasadzie pokazuje tylko, jak uruchomić żądanie GET i odczytać odpowiedź. Nigdzie nie wyjaśnia, jak go używać, między innymi do wykonywania żądania POST, ustawiania nagłówków żądań, czytania nagłówków odpowiedzi, radzenia sobie z plikami cookie, przesyłania formularza HTML, przesyłania pliku itp.

Jak mogę użyć java.net.URLConnectiondo uruchomienia i obsługi „zaawansowanych” żądań HTTP?

BalusC
źródło

Odpowiedzi:

2711

Najpierw zrzeczenie się odpowiedzialności: wszystkie opublikowane fragmenty kodu są podstawowymi przykładami. Będziesz musiał poradzić sobie z trywialnymi IOExceptioni RuntimeExceptionpodobnymi NullPointerException, ArrayIndexOutOfBoundsExceptioni sam się żonić.


Przygotowanie

Najpierw musimy znać przynajmniej adres 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ą mieć name=valueformat i być konkatenowane przez &. Normalnie kodowałbyś również adresy URL parametrów zapytania za pomocą określonego zestawu znaków URLEncoder#encode().

To String#format()tylko dla wygody. Wolę, gdybym potrzebował operatora konkatenacji String +więcej niż dwa razy.


Odpalanie żądania HTTP GET 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();
// ...

Każdy ciąg zapytania powinien być powiązany z adresem URL za pomocą ?. Accept-CharsetNagłówek może wskazywać serwer co kodujący parametry są w. Jeśli nie wysyła żadnych znaków zapytania, wtedy można opuścić Accept-Charsetnagłówek precz. Jeśli nie musisz ustawiać żadnych nagłówków, możesz nawet użyć URL#openStream()metody skrótu.

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

Tak czy inaczej, jeśli druga strona jest a HttpServlet, wówczas doGet()zostanie wywołana jej metoda i 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);
}

Odpalanie żądania HTTP POST z parametrami zapytania

Ustawianie URLConnection#setDoOutput()się trueniejawnie ustawia metody żądania aby móc pisać. Standardowy HTTP POST, podobnie jak formularze sieciowe, jest typu, w application/x-www-form-urlencodedktórym ciąg 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: ilekroć chcesz programowo przesłać formularz HTML, nie zapomnij wziąć name=valuepar dowolnych <input type="hidden">elementów do ciągu zapytania i oczywiście także name=valuepary <input type="submit">elementów, które chcesz programowo „nacisnąć” (ponieważ jest to zwykle używane po stronie serwera, aby rozróżnić, czy przycisk został naciśnięty, a jeśli tak, to który).

Można też rzucić otrzymany URLConnectionaby HttpURLConnectioni wykorzystanie jej HttpURLConnection#setRequestMethod()w zamian. Ale jeśli próbujesz użyć połączenia dla wyjścia trzeba jeszcze zestaw URLConnection#setDoOutput()do true.

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

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


Właściwie uruchamianie żądania HTTP

Możesz jawnie uruchomić żądanie HTTP URLConnection#connect(), ale żądanie zostanie automatycznie uruchomione na żądanie, jeśli chcesz uzyskać jakiekolwiek informacje o odpowiedzi HTTP, takie jak użycie treści odpowiedzi URLConnection#getInputStream()i tak dalej. Powyższe przykłady dokładnie to robią, więc connect()wywołanie jest w rzeczywistości zbędne.


Zbieranie informacji o odpowiedzi HTTP

  1. Status odpowiedzi HTTP :

    Potrzebujesz HttpURLConnectiontutaj. Najpierw rzuć to, jeśli to konieczne.

    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 :

    Gdy Content-Typezawiera charsetparametr, wówczas treść odpowiedzi jest prawdopodobnie oparta na tekście i chcielibyśmy następnie przetworzyć treść odpowiedzi za pomocą kodowania 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 zwykle wspierana przez plik cookie. Niektóre formularze internetowe wymagają zalogowania i / lub śledzenia przez sesję. Możesz używać CookieHandlerinterfejsu API do przechowywania plików cookie. Przed wysłaniem wszystkich żądań HTTP musisz przygotować znak „ CookieManagera CookiePolicyACCEPT_ALL.

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

Pamiętaj, że wiadomo, że nie zawsze działa to poprawnie we wszystkich okolicznościach. Jeśli to się nie powiedzie, najlepiej ręcznie zebrać i ustawić nagłówki plików cookie. Zasadniczo musisz pobrać wszystkie Set-Cookienagłówki z odpowiedzi logowania lub pierwszego GETżądania, a następnie przekazać je przez kolejne żądania.

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


Tryb przesyłania strumieniowego

HttpURLConnectionWola domyślnie buforowania całego ciała żądania przed wysłaniem go w rzeczywistości, niezależnie od tego, czy masz ustawioną stałą długość zawartości samodzielnie za pomocą connection.setRequestProperty("Content-Length", contentLength);. Może to powodować, OutOfMemoryExceptionilekroć jednocześnie wysyłasz duże żądania POST (np. Przesyłanie plików). Aby tego uniknąć, chcesz ustawić HttpURLConnection#setFixedLengthStreamingMode().

httpConnection.setFixedLengthStreamingMode(contentLength);

Ale jeśli długość zawartości nie jest tak naprawdę wcześniej znana, możesz skorzystać z trybu przesyłania strumieniowego, ustawiając HttpURLConnection#setChunkedStreamingMode()odpowiednio. Spowoduje to ustawienie Transfer-Encodingnagłówka HTTP, do chunkedktórego będzie wymuszane wysyłanie treści żądania we fragmentach. Poniższy przykład wyśle ​​ciało w kawałkach po 1 KB.

httpConnection.setChunkedStreamingMode(1024);

Agent użytkownika

Może się zdarzyć, że żądanie zwróci nieoczekiwaną odpowiedź, podczas gdy działa poprawnie z prawdziwą przeglądarką internetową . Strona serwera prawdopodobnie blokuje żądania na podstawie User-Agentnagłówka żądania. URLConnectionWola domyślnie ustawić go Java/1.6.0_19gdzie ostatnia część jest oczywiście wersja 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 najnowszej przeglądarki .


Obsługa błędów

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

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

Jeśli kod odpowiedzi HTTP to -1, coś poszło nie tak z obsługą połączeń i odpowiedzi. HttpURLConnectionRealizacja jest w starszych JRE nieco buggy z utrzymaniem połączenia żyje. Możesz to wyłączyć, ustawiając http.keepAlivewłaściwość systemową na false. Możesz to zrobić programowo na początku swojej aplikacji poprzez:

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

Przesyłanie plików

Zwykle używasz multipart/form-datakodowania dla mieszanej zawartości POST (dane binarne i 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 to a HttpServlet, wówczas doPost()zostanie wywołana jej metoda, a części będą dostępne przez HttpServletRequest#getPart()(uwaga, więc nie getParameter() i tak dalej!). getPart()Sposób jest jednak stosunkowo nowe, to wprowadza się serwletu 3,0 (GlassFish 3 Tomcatem 7, etc). Przed serwletem 3.0 najlepszym wyborem jest użycie Apache Commons FileUpload do parsowania multipart/form-datażądania. Zobacz także tę odpowiedź, aby zapoznać się z przykładami podejść do FileUpload i Servelt 3.0.


Radzenie sobie z niezaufanymi lub źle skonfigurowanymi stronami HTTPS

Czasami musisz podłączyć adres URL HTTPS, być może dlatego, że piszesz skrobak sieciowy. W takim przypadku możesz spotkać się z osobą javax.net.ssl.SSLException: Not trusted server certificatew niektórych witrynach HTTPS, która nie aktualizuje swoich certyfikatów SSL, java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] foundlub javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_namew niektórych źle skonfigurowanych witrynach HTTPS.

Poniższy jednorazowy staticinicjalizator w klasie skrobaka internetowego powinien uczynić HttpsURLConnectionbardziej łagodnym co do tych witryn HTTPS i tym samym nie rzucać 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

Apache HttpComponents HttpClient jest znacznie wygodniejszy w tym wszystkim :)


Analiza i wyodrębnianie HTML

Jeśli wszystko, czego potrzebujesz, to analizowanie i wyodrębnianie danych z HTML, lepiej użyj parsera HTML, takiego jak Jsoup

BalusC
źródło
119
Najpierw umieść link apache, aby ludzie szukający rozwiązania znaleźli go szybciej;)
ZeissS
40
@ivanceras: Jeśli nie możesz go sprowadzić na podstawie informacji zawartych w tej odpowiedzi, naciśnij Ask Questionprzycisk w prawym górnym rogu.
BalusC
3
@Brais: Przeczytaj specyfikację. Ta --część nie jest częścią samej granicy. To tylko ciąg separatora. Cofnąłem twoją nieprawidłową edycję.
BalusC
7
@BalusC bardzo dziękuje za tak doskonały samouczek. Dołącz także nagłówek „Zamykanie strumieni / połączeń”. Jestem naprawdę zdezorientowany, kiedy i które strumienie / połączenia mają zostać zamknięte.
10
Smutne jest to, że na Androidzie nie zaleca się HttpClientteraz używania Apache i HttpURLConnectionjest okrutny. android-developers.blogspot.in/2011/09/…
yati sagade
91

Podczas pracy z HTTP prawie zawsze bardziej użyteczne jest odwoływanie się do HttpURLConnectionklasy bazowej URLConnection(ponieważ URLConnectionjest to klasa abstrakcyjna, gdy poprosisz o URLConnection.openConnection()adres URL HTTP, to i tak otrzymasz).

Następnie możesz zamiast polegać na URLConnection#setDoOutput(true)niejawnym ustawieniu metody żądania na POST, zamiast tego zrobić, httpURLConnection.setRequestMethod("POST")co niektórzy mogą uznać za bardziej naturalne (i która pozwala również określić inne metody żądania, takie jak PUT , DELETE , ...).

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

int responseCode = httpURLConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {
Paal Thorstensen
źródło
1
setDoOutPut true był moim problemem, który ustawił mój GET na POST. Dzięki
Patrick Kafka,
22
Jeśli próbujesz napisać danych do OutputStream Państwo musi nadal ustawiony setDoOutput()do trueinaczej jest wyjątek (nawet jeśli setRequestMethod("POST")). Żeby było jasne: ustawienie URLConnection#setDoOutput(true)do trueniejawnie ustawia metody żądania do posta, ale ustawienie httpURLConnection.setRequestMethod("POST")na POST ma nie niejawnie zestaw setDoOutput()do true.
Tony Chan,
54

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

google-http-java-client jest również doskonałym zasobem typu open source.

David Chandler
źródło
Myślałem tylko tak samo. Ale może być również miło mieć otwartą / prostą bibliotekę Java, która używa tylko kodu URLConnection, jak tutaj przedstawiono, który zawiera kod do prostszych metod wykonywania HTTP GET, POST itp. Bibliotekę można następnie skompilować i spakować jako JAR i importowane / używane w kodzie Java lub pliku klasy źródłowej mogą zostać uwzględnione 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 bardziej kłopotliwe w porównaniu z prostą biblioteką klas plików 1 używającą URLConnection.
David
rapidvaluesolutions.com/tech_blog/… faworyzuje HttpURLConnection nad HttpClient
Ravindra babu
24

Proponuję rzucić okiem na kod w kevinsawicki / http-request , jego zasadniczo opakowanie na HttpUrlConnectionnim zapewnia znacznie prostszy interfejs API, na wypadek, gdybyś chciał teraz złożyć żądanie lub zajrzeć do źródeł ( nie jest zbyt duży), aby zobaczyć, jak obsługiwane są połączenia.

Przykład: Złóż GETzapytanie z typem treści application/jsoni niektórymi parametrami 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);
fernandohur
źródło
24

Są 2 opcje, do których możesz przejść za pomocą HTTP URL Hits: 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()));
Utkash Bhatt
źródło
3
Jak wyświetlić rzeczywistą odpowiedź JSON?
Sora,
21

Ta odpowiedź bardzo mnie zainspirowała.

Często pracuję nad projektami, w których muszę wykonać HTTP, i może nie chcę wprowadzać wielu zależności zewnętrznych (które wprowadzają inne itd. Itd.)

Zacząłem pisać własne programy narzędziowe na podstawie niektórych z tej rozmowy (nie żadnej, 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 {

Są tylko kilka metod statycznych.

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

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ę znajdziesz tutaj:

https://github.com/RichardHightower/boon

Moim celem jest zapewnienie wspólnych rzeczy, które chcielibyśmy zrobić w nieco łatwiejszy sposób niż ....

RickHigh
źródło
2
Dziwne, że w doPostmetodzie jest charsetparametr, który jest używany do ustawiania nagłówka żądania, ale potem dane są zapisywane za pomocą jakiegoś zakodowanego zestawu znaków IO.CHARSET. Błąd?
Vit Khudenko
21

Aktualizacja

Nowy klient HTTP jest dostarczany z Javą 9, ale jako część modułu inkubatora o nazwie jdk.incubator.httpclient. Moduły inkubatora umożliwiają przekazanie programistom niekończących interfejsów API, podczas gdy interfejsy API przechodzą do finalizacji lub usunięcia w przyszłej wersji.

W Javie 9 możesz wysłać GETzapytanie takie 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());

Ponieważ ten nowy klient HTTP jest już włączony java.httpclient jdk.incubator.httpclientmoduł, powinieneś zadeklarować tę zależność w swoim module-info.javapliku:

module com.foo.bar {
    requires jdk.incubator.httpclient;
}
rev Ali Dehghani
źródło
1
Dalsza aktualizacja: moduł nie ma statusu inkubacji. Teraz jest to java.net.http , a nie jdk.incubator.httpclient.
VGR
17

Początkowo zostałem wprowadzony w błąd przez ten artykuł, który sprzyja HttpClient.

Później uświadomiłem sobie, że HttpURLConnectionpozostanie z tego artykułu

Zgodnie z blogiem Google :

Klient HTTP Apache ma mniej błędów w Eclair i Froyo. To najlepszy wybór dla tych wydań. W przypadku Piernika najlepszym wyborem jest HttpURLConnection. Prosty interfejs API i niewielki rozmiar sprawiają, że doskonale pasuje do systemu Android.

Przejrzysta pamięć podręczna kompresji i odpowiedzi zmniejsza użycie sieci, poprawia prędkość i oszczędza baterię. Nowe aplikacje powinny korzystać z HttpURLConnection; to tam będziemy spędzać naszą energię na naprzód.

Po przeczytaniu tego artykułu i kilku innych pytań dotyczących przepływów, jestem przekonany, że HttpURLConnectionpozostanie na dłużej.

Niektóre pytania dotyczące SE HttpURLConnections:

Na Androidzie, złóż wniosek POST z danymi formularza zakodowanego w adresie URL bez użycia UrlEncodedFormEntity

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

Ravindra Babu
źródło
15

Istnieje również OkHttp , który jest domyślnie wydajnym klientem HTTP:

  • Obsługa HTTP / 2 pozwala wszystkim żądaniom do tego samego hosta na współużytkowanie gniazda.
  • Pula połączeń zmniejsza opóźnienie żądań (jeśli HTTP / 2 nie jest dostępne).
  • Przezroczysty GZIP zmniejsza rozmiary pobierania.
  • Buforowanie odpowiedzi całkowicie eliminuje sieć przy powtarzaniu żądań.

Najpierw utwórz instancję OkHttpClient:

OkHttpClient client = new OkHttpClient();

Następnie przygotuj GETzapytanie:

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

na koniec użyj, OkHttpClientaby wysłać przygotowane Request:

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

Aby uzyskać więcej informacji, zapoznaj się z dokumentacją OkHttp

Ali Dehghani
źródło
14

Można również skorzystać JdkRequestz jcabi-http (jestem programistą), który robi to wszystko za Ciebie, dekorowanie HttpURLConnection, wypalanie żądania HTTP i analizowania odpowiedzi, na przykład:

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

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

yegor256
źródło
1
Jak obchodzisz się z plikami cookie?
Dejell
12

jeśli używasz http get, usuń ten wiersz

urlConnection.setDoOutput(true);
Nison Cheruvathur
źródło