Wie verwende ich java.net.URLConnection, um HTTP-Anforderungen auszulösen und zu verarbeiten?

1948

Die Verwendung von java.net.URLConnectionwird hier ziemlich oft gefragt, und das Oracle-Tutorial ist zu kurz.

Dieses Tutorial zeigt im Grunde nur, wie eine GET-Anfrage ausgelöst und die Antwort gelesen wird. Es wird nirgendwo erklärt, wie man es verwendet, um unter anderem eine POST-Anfrage auszuführen, Anforderungsheader festzulegen, Antwortheader zu lesen, mit Cookies umzugehen, ein HTML-Formular zu senden, eine Datei hochzuladen usw.

Wie kann ich also java.net.URLConnection"erweiterte" HTTP-Anforderungen auslösen und verarbeiten?

BalusC
quelle

Antworten:

2711

Zunächst ein Haftungsausschluss: Die veröffentlichten Code-Schnipsel sind grundlegende Beispiele. Sie werden trivial behandeln müssen IOExceptions und RuntimeExceptions wie NullPointerException, ArrayIndexOutOfBoundsExceptionund Konsorten selbst.


Vorbereiten

Wir müssen zuerst mindestens die URL und den Zeichensatz kennen. Die Parameter sind optional und hängen von den funktionalen Anforderungen ab.

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

Die Abfrageparameter müssen name=valueformatiert und von verkettet sein &. Normalerweise würden Sie die Abfrageparameter auch mit dem angegebenen Zeichensatz per URL codierenURLEncoder#encode() .

Das String#format()ist nur zur Vereinfachung. Ich bevorzuge es, wenn ich den String-Verkettungsoperator +mehr als zweimal benötigen würde .


Auslösen einer HTTP-GET- Anforderung mit (optional) Abfrageparametern

Es ist eine triviale Aufgabe. Dies ist die Standardanforderungsmethode.

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

Jede Abfragezeichenfolge sollte mit der URL verkettet werden ?. Der Accept-CharsetHeader weist den Server möglicherweise darauf hin, in welcher Codierung sich die Parameter befinden. Wenn Sie keine Abfragezeichenfolge senden, können Sie den Accept-CharsetHeader weglassen. Wenn Sie keine Überschriften festlegen müssen, können Sie sogar die URL#openStream()Verknüpfungsmethode verwenden.

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

In beiden Fällen wird die Methode aufgerufen , wenn die andere Seite a ist HttpServlet, doGet()und die Parameter stehen zur Verfügung HttpServletRequest#getParameter().

Zu Testzwecken können Sie den Antworttext wie folgt auf stdout drucken:

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

Auslösen einer HTTP-POST- Anforderung mit Abfrageparametern

Durch Setzen von URLConnection#setDoOutput()to wird truedie Anforderungsmethode implizit auf POST gesetzt. Der Standard-HTTP-POST wie bei Webformularen ist vom Typ, application/x-www-form-urlencodedbei dem die Abfragezeichenfolge in den Anforderungshauptteil geschrieben wird.

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

Hinweis: Wenn Sie möchten , dass ein HTML - Formular programmatisch unterbreiten, nicht zu vergessen die nehmen name=valuePaare von irgendwelchen <input type="hidden">Elementen in die Query - String und natürlich auch das name=valuePaar aus dem <input type="submit">Element , das Sie zu „drücken“ programmatisch möchten (weil Dies wird normalerweise auf der Serverseite verwendet, um zu unterscheiden, ob und welche Taste gedrückt wurde.

Sie können auch die erhaltenen Guss URLConnectionzu HttpURLConnectionverwenden , das HttpURLConnection#setRequestMethod()statt. Aber wenn Sie versuchen , die Verbindung für die Ausgabe zu verwenden , müssen Sie noch Satz URLConnection#setDoOutput()zu true.

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

In beiden Fällen wird die Methode aufgerufen , wenn die andere Seite a ist HttpServlet, doPost()und die Parameter stehen zur Verfügung HttpServletRequest#getParameter().


Die HTTP-Anfrage wird tatsächlich ausgelöst

Sie können die HTTP-Anforderung explizit mit URLConnection#connect()auslösen, die Anforderung wird jedoch automatisch bei Bedarf ausgelöst, wenn Sie Informationen zur HTTP-Antwort abrufen möchten, z. B. den verwendeten Antworttext URLConnection#getInputStream()usw. Die obigen Beispiele machen genau das, so dass der connect()Aufruf tatsächlich überflüssig ist.


Sammeln von HTTP-Antwortinformationen

  1. HTTP-Antwortstatus :

    Du brauchst ein HttpURLConnectionhier. Wirf es zuerst, wenn nötig.

    int status = httpConnection.getResponseCode();
  2. HTTP-Antwortheader :

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

    Wenn das Content-Typeeinen charsetParameter enthält , ist der Antworttext wahrscheinlich textbasiert und wir möchten den Antworttext dann mit der serverseitig angegebenen Zeichencodierung verarbeiten.

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

Sitzung pflegen

Die serverseitige Sitzung wird normalerweise durch ein Cookie unterstützt. Einige Webformulare erfordern, dass Sie angemeldet sind und / oder von einer Sitzung verfolgt werden. Sie können die CookieHandlerAPI verwenden, um Cookies zu verwalten. Sie müssen ein CookieManagermit einem CookiePolicyvon vorbereiten, ACCEPT_ALLbevor Sie alle HTTP-Anforderungen senden.

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

Beachten Sie, dass dies bekanntermaßen nicht immer unter allen Umständen ordnungsgemäß funktioniert. Wenn dies für Sie fehlschlägt, sollten Sie die Cookie-Header am besten manuell erfassen und festlegen. Grundsätzlich müssen Sie alle Set-CookieHeader aus der Antwort des Logins oder der ersten GETAnforderung abrufen und diese dann durch die nachfolgenden Anforderungen weiterleiten.

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

Das split(";", 2)[0]wird bekommen Cookie Attribute befreien , die wie für die Server - Seite nicht relevant sind expires, pathusw. Alternativ können Sie auch nutzen könnten cookie.substring(0, cookie.indexOf(';'))statt split().


Streaming-Modus

Das HttpURLConnectionwird standardmäßig puffer den gesamten Körper Anforderung bevor es tatsächlich zu senden, unabhängig davon , ob Sie eine feste Inhaltslänge festgelegt haben , sie mit connection.setRequestProperty("Content-Length", contentLength);. Dies kann zu OutOfMemoryExceptions führen, wenn Sie gleichzeitig große POST-Anforderungen senden (z. B. Dateien hochladen). Um dies zu vermeiden, möchten Sie die einstellen HttpURLConnection#setFixedLengthStreamingMode().

httpConnection.setFixedLengthStreamingMode(contentLength);

Wenn die Länge des Inhalts jedoch vorher nicht wirklich bekannt ist, können Sie den Chunked-Streaming-Modus verwenden, indem Sie den HttpURLConnection#setChunkedStreamingMode()entsprechenden Wert einstellen . Dadurch wird der HTTP- Transfer-EncodingHeader festgelegt, an den chunkeddas Senden des Anforderungshauptteils in Blöcken erzwungen wird. Im folgenden Beispiel wird der Body in Blöcken von 1 KB gesendet.

httpConnection.setChunkedStreamingMode(1024);

User-Agent

Es kann vorkommen, dass eine Anfrage eine unerwartete Antwort zurückgibt, während dies mit einem echten Webbrowser problemlos funktioniert . Die Serverseite blockiert wahrscheinlich Anforderungen basierend auf dem User-AgentAnforderungsheader. Das URLConnectionTestament legt standardmäßig fest, Java/1.6.0_19wo der letzte Teil offensichtlich die JRE-Version ist. Sie können dies wie folgt überschreiben:

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.

Verwenden Sie die User-Agent-Zeichenfolge eines aktuellen Browsers .


Fehlerbehandlung

Wenn der HTTP-Antwortcode 4nn(Clientfehler) oder 5nn(Serverfehler) lautet , möchten Sie möglicherweise den lesen, HttpURLConnection#getErrorStream()um festzustellen, ob der Server nützliche Fehlerinformationen gesendet hat.

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

Wenn der HTTP-Antwortcode -1 ist, ist bei der Verbindung und der Antwortbehandlung ein Fehler aufgetreten. Die HttpURLConnectionImplementierung in älteren JREs ist etwas fehlerhaft, da die Verbindungen am Leben bleiben. Sie können es deaktivieren, indem Sie die http.keepAliveSystemeigenschaft auf setzen false. Sie können dies zu Beginn Ihrer Bewerbung programmgesteuert tun, indem Sie:

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

Dateien hochladen

Normalerweise verwenden Sie die multipart/form-dataCodierung für gemischte POST-Inhalte (Binär- und Zeichendaten). Die Codierung wird ausführlicher in RFC2388 beschrieben .

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

Wenn die andere Seite a ist HttpServlet, wird ihre doPost()Methode aufgerufen und die Teile sind verfügbar von HttpServletRequest#getPart()(beachten Sie, also nicht getParameter() und so weiter!). Die getPart()Methode ist jedoch relativ neu und wurde in Servlet 3.0 (Glassfish 3, Tomcat 7 usw.) eingeführt. Vor Servlet 3.0 verwenden Sie am besten Apache Commons FileUpload , um eine multipart/form-dataAnforderung zu analysieren . In dieser Antwort finden Sie auch Beispiele für die Ansätze FileUpload und Servelt 3.0.


Umgang mit nicht vertrauenswürdigen oder falsch konfigurierten HTTPS-Sites

Manchmal müssen Sie eine HTTPS-URL verbinden, möglicherweise weil Sie einen Web-Scraper schreiben. In diesem Fall kann es javax.net.ssl.SSLException: Not trusted server certificateauf einigen HTTPS-Sites zu Problemen kommen, die ihre SSL-Zertifikate nicht auf dem neuesten Stand halten, java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] foundoder javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_nameauf einigen oder auf einigen falsch konfigurierten HTTPS-Sites.

Der folgende einmalige staticInitialisierer in Ihrer Web-Scraper-Klasse sollte HttpsURLConnectiondie HTTPS-Sites milder gestalten und daher diese Ausnahmen nicht mehr auslösen.

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

Letzte Worte

Der Apache HttpComponents HttpClient ist in dieser Hinsicht viel praktischer :)


HTML analysieren und extrahieren

Wenn Sie nur Daten aus HTML analysieren und extrahieren möchten, verwenden Sie besser einen HTML-Parser wie Jsoup

BalusC
quelle
119
Sie sollten den Apache-Link zuerst platzieren, damit Leute, die nach einer Lösung suchen, ihn schneller finden;)
ZeissS
40
@ivanceras: Wenn Sie es aufgrund der Informationen in dieser Antwort nicht auf den Punkt bringen können, drücken Sie bitte die Ask QuestionTaste oben rechts.
BalusC
3
@Brais: Bitte lesen Sie die Spezifikation. Der --Teil ist nicht Teil der Grenze selbst. Es ist nur eine Trennzeichenfolge. Ich habe Ihre ungültige Bearbeitung zurückgesetzt.
BalusC
7
@BalusC vielen Dank für dieses perfekte Tutorial. Bitte geben Sie auch eine Überschrift wie "Streams / Verbindungen schließen" an. Ich bin wirklich verwirrt darüber, wann und welche Streams / Verbindungen geschlossen werden sollen.
10
Der traurige Teil ist, dass es auf Android nicht empfohlen wird, den Apache HttpClientjetzt zu verwenden und HttpURLConnectiongrausam ist. android-developers.blogspot.in/2011/09/…
Yati Sagade
91

Wenn Sie mit HTTP arbeiten, ist es fast immer nützlicher, auf HttpURLConnectiondie Basisklasse zu verweisen URLConnection(da URLConnectiones sich um eine abstrakte Klasse handelt, wenn Sie URLConnection.openConnection()nach einer HTTP-URL fragen , erhalten Sie diese ohnehin zurück).

Dann können Sie sich nicht darauf verlassen URLConnection#setDoOutput(true), die Anforderungsmethode implizit auf POST zu setzen, sondern dies tun, httpURLConnection.setRequestMethod("POST")was einige möglicherweise natürlicher finden (und Sie können auch andere Anforderungsmethoden wie PUT , DELETE , ... angeben ).

Es bietet auch nützliche HTTP-Konstanten, damit Sie Folgendes tun können:

int responseCode = httpURLConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {
Paal Thorstensen
quelle
1
setDoOutPut true war mein Problem, durch das mein GET auf einen POST gesetzt wurde. Vielen Dank
Patrick Kafka
22
Wenn Sie versuchen , Daten an den Output schreiben Sie müssen noch gesetzt setDoOutput()zu truesonst eine Ausnahme (auch wenn Sie geworfen setRequestMethod("POST")). Um klar zu sein: Einstellung URLConnection#setDoOutput(true)zu trueimplizit setzt die Anforderungsmethode POST, aber Einstellung httpURLConnection.setRequestMethod("POST")zu POST ist nicht implizit gesetzt setDoOutput()zu true.
Tony Chan
54

Inspiriert von dieser und anderen Fragen zu SO habe ich einen minimalen Open-Source- Basic-http-Client erstellt , der die meisten der hier beschriebenen Techniken verkörpert.

google-http-java-client ist auch eine großartige Open-Source-Ressource.

David Chandler
quelle
Ich habe nur das Gleiche gedacht. Es könnte aber auch schön sein, eine Barebones / einfache Java-Bibliothek zu haben, die nur den hier vorgestellten URLConnection-Code verwendet, der den Code in einfachere Methoden zum Ausführen eines HTTP-GET, POST usw. kapselt. Die Bibliothek kann dann als JAR und kompiliert und gepackt werden Im Java-Code importierte / verwendete Dateien oder die Quellklassendatei können in das Java-Projekt aufgenommen werden, wenn keine externen JARs gewünscht werden. Dies könnte mit anderen Bibliotheken wie Apache usw. geschehen, ist jedoch im Vergleich zu einer einfachen 1-Dateiklassenbibliothek mit URLConnection schwieriger.
David
rapidvaluesolutions.com/tech_blog/… bevorzugt HttpURLConnection gegenüber HttpClient
Ravindra babu
24

Ich schlage vor, Sie werfen einen Blick auf den Code auf kevinsawicki / http-request. Es handelt sich im Grunde genommen um einen Wrapper, der HttpUrlConnectioneine viel einfachere API bietet, falls Sie die Anfragen jetzt nur stellen möchten, oder Sie können sich die Quellen ansehen ( Es ist nicht zu groß, um einen Blick darauf zu werfen, wie Verbindungen behandelt werden.

Beispiel: Stellen Sie eine GETAnfrage mit Inhaltstyp application/jsonund einigen Abfrageparametern:

// 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
quelle
24

Es gibt zwei Optionen, die Sie für HTTP-URL-Treffer verwenden können: GET / POST

GET Anfrage: -

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-Anfrage: -

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
quelle
3
Wie können Sie die tatsächliche JSON-Antwort anzeigen?
Sora
21

Diese Antwort hat mich auch sehr inspiriert.

Ich bin oft in Projekten, in denen ich etwas HTTP ausführen muss, und ich möchte möglicherweise nicht viele Abhängigkeiten von Drittanbietern einbringen (die andere einbringen und so weiter und so fort usw.).

Ich fing an, meine eigenen Dienstprogramme zu schreiben, basierend auf einigen dieser Konversationen (keine, wo getan):

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 {

Dann gibt es nur ein paar oder statische Methoden.

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

Dann poste ...

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

Nun, Sie haben die Idee ...

Hier sind die Tests:

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


}

Den Rest finden Sie hier:

https://github.com/RichardHightower/boon

Mein Ziel ist es, die üblichen Dinge, die man tun möchte, etwas einfacher zu machen als ...

RickHigh
quelle
2
Es ist seltsam, dass es in der doPostMethode einen charsetParameter gibt, der zum Festlegen des Anforderungsheaders verwendet wird, aber dann werden die Daten mit einem fest codierten Zeichensatz geschrieben IO.CHARSET. Ein Käfer?
Vit Khudenko
21

Aktualisieren

Der neue HTTP-Client wurde mit Java 9 ausgeliefert, ist jedoch Teil eines Inkubator-Moduls mit dem Namen jdk.incubator.httpclient. Inkubatormodule sind ein Mittel, um nicht endgültige APIs in die Hände von Entwicklern zu legen, während die APIs in einer zukünftigen Version entweder in Richtung Finalisierung oder Entfernung voranschreiten.

In Java 9 können Sie eine GETAnfrage senden wie:

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

Dann können Sie die zurückgegebenen überprüfen HttpResponse:

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

Da ist dieser neue HTTP Client in java.httpclient jdk.incubator.httpclientModul sollten Sie diese Abhängigkeit in Ihrer module-info.javaDatei deklarieren :

module com.foo.bar {
    requires jdk.incubator.httpclient;
}
Ali Dehghani
quelle
1
Weiteres Update: Das Modul hat keinen Inkubationsstatus mehr. Es ist jetzt java.net.http , nicht jdk.incubator.httpclient.
VGR
17

Anfangs wurde ich durch diesen Artikel irregeführt, der bevorzugt HttpClient.

Später wurde mir klar, dass HttpURLConnectiondies von diesem Artikel abweichen wird

Laut Google Blog :

Der Apache HTTP-Client weist weniger Fehler in Eclair und Froyo auf. Es ist die beste Wahl für diese Versionen. Für Lebkuchen ist HttpURLConnection die beste Wahl. Dank seiner einfachen API und seiner geringen Größe eignet es sich hervorragend für Android.

Transparente Komprimierung und Antwort-Caching reduzieren die Netzwerknutzung, verbessern die Geschwindigkeit und sparen Batterie. Neue Anwendungen sollten HttpURLConnection verwenden. Hier werden wir in Zukunft unsere Energie ausgeben.

Nachdem ich diesen Artikel und einige andere Fragen zum Stapelüberlauf gelesen habe , bin ich überzeugt, dass dies HttpURLConnectionlänger dauern wird.

Einige der SE-Fragen befürworten HttpURLConnections:

Stellen Sie unter Android eine POST-Anfrage mit URL-codierten Formulardaten, ohne UrlEncodedFormEntity zu verwenden

HttpPost funktioniert in Java-Projekten, nicht in Android

Ravindra Babu
quelle
15

Es gibt auch OkHttp , einen HTTP-Client, der standardmäßig effizient ist:

  • Durch die HTTP / 2-Unterstützung können alle Anforderungen an denselben Host einen Socket gemeinsam nutzen.
  • Das Verbindungspooling reduziert die Anforderungslatenz (wenn HTTP / 2 nicht verfügbar ist).
  • Transparentes GZIP verkleinert die Downloadgröße.
  • Durch das Zwischenspeichern von Antworten wird das Netzwerk für wiederholte Anforderungen vollständig vermieden.

Erstellen Sie zunächst eine Instanz von OkHttpClient:

OkHttpClient client = new OkHttpClient();

Dann bereiten Sie Ihre GETAnfrage vor:

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

Schließlich verwenden OkHttpClient, um vorbereitet zu senden Request:

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

Weitere Informationen finden Sie in der Dokumentation zu OkHttp

Ali Dehghani
quelle
14

Sie können auch verwenden , JdkRequestvon jcabi-http (Ich bin ein Entwickler), die für Sie alle funktionierts, Dekorieren HttpURLConnection, Brennen HTTP - Anfragen und -Antworten Parsen, zum Beispiel:

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

Weitere Informationen finden Sie in diesem Blogbeitrag: http://www.yegor256.com/2014/04/11/jcabi-http-intro.html

yegor256
quelle
1
Wie gehen Sie mit Cookies um?
Dejell
12

Wenn Sie http get verwenden, entfernen Sie bitte diese Zeile

urlConnection.setDoOutput(true);
Nison Cheruvathur
quelle