Come utilizzare java.net.URLConnection per attivare e gestire le richieste HTTP?


1948

java.net.URLConnectionQui viene chiesto di usarlo abbastanza spesso e il tutorial Oracle è troppo conciso al riguardo.

Quel tutorial in pratica mostra solo come eseguire una richiesta GET e leggere la risposta. Non spiega da nessuna parte come usarlo per eseguire una richiesta POST, impostare intestazioni di richiesta, leggere intestazioni di risposta, gestire i cookie, inviare un modulo HTML, caricare un file, ecc.

Quindi, come posso utilizzare java.net.URLConnectionper attivare e gestire richieste HTTP "avanzate"?

Risposte:


2711

Prima una dichiarazione di non responsabilità: gli snippet di codice pubblicati sono tutti esempi di base. Avrai bisogno di gestire banali IOExceptions e RuntimeExceptions come NullPointerException, ArrayIndexOutOfBoundsExceptione consorti te.


Preparazione

Dobbiamo prima conoscere almeno l'URL e il set di caratteri. I parametri sono opzionali e dipendono dai requisiti funzionali.

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

I parametri della query devono essere in name=valueformato e devono essere concatenati da &. Normalmente dovresti anche codificare URL i parametri della query con il set di caratteri specificato usando URLEncoder#encode().

Il String#format()è solo per convenienza. Lo preferisco quando avrei bisogno dell'operatore di concatenazione di stringhe +più di due volte.


Attivazione di una richiesta GET HTTP con (facoltativamente) parametri di query

È un compito banale. È il metodo di richiesta predefinito.

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

Qualsiasi stringa di query deve essere concatenata all'URL utilizzando ?. L' Accept-Charsetintestazione può suggerire al server la codifica dei parametri. Se non si invia alcuna stringa di query, è possibile lasciare l' Accept-Charsetintestazione. Se non è necessario impostare alcuna intestazione, è anche possibile utilizzare il URL#openStream()metodo di scelta rapida.

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

In entrambi i casi, se l'altro lato è a HttpServlet, doGet()verrà chiamato il suo metodo e i parametri saranno disponibili per HttpServletRequest#getParameter().

A scopo di test, è possibile stampare il corpo della risposta su stdout come di seguito:

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

Attivazione di una richiesta POST HTTP con parametri di query

L'impostazione di URLConnection#setDoOutput()su trueimposta implicitamente il metodo di richiesta su POST. Il POST HTTP standard come fanno i moduli Web è di tipo in application/x-www-form-urlencodedcui la stringa di query è scritta nel corpo della richiesta.

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

Nota: ogni volta che si desidera inviare un modulo HTML a livello di codice, non dimenticare di inserire le name=valuecoppie di tutti gli <input type="hidden">elementi nella stringa di query e, naturalmente, anche la name=valuecoppia <input type="submit">dell'elemento che si desidera "premere" a livello di codice (perché che di solito è stato utilizzato sul lato server per distinguere se un pulsante è stato premuto e, in caso affermativo, quale).

È anche possibile lanciare l'ottenuto URLConnectionper HttpURLConnectione usare il suo HttpURLConnection#setRequestMethod()posto. Ma se stai provando a utilizzare la connessione per l'output devi comunque impostare URLConnection#setDoOutput()su true.

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

In entrambi i casi, se l'altro lato è a HttpServlet, doPost()verrà chiamato il suo metodo e i parametri saranno disponibili per HttpServletRequest#getParameter().


Attivazione effettiva della richiesta HTTP

È possibile URLConnection#connect()attivare esplicitamente la richiesta HTTP con , ma la richiesta verrà automaticamente attivata su richiesta quando si desidera ottenere informazioni sulla risposta HTTP, ad esempio il corpo della risposta URLConnection#getInputStream()e così via. Gli esempi sopra fanno esattamente questo, quindi la connect()chiamata è in effetti superflua.


Raccolta di informazioni sulla risposta HTTP

  1. Stato della risposta HTTP :

    Hai bisogno di un HttpURLConnectionqui. Lancia prima se necessario.

    int status = httpConnection.getResponseCode();
  2. Intestazioni di risposta HTTP :

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

    Quando Content-Typecontiene un charsetparametro, è probabile che il corpo della risposta sia basato sul testo e vorremmo quindi elaborare il corpo della risposta con la codifica dei caratteri specificata sul lato server.

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

Mantenere la sessione

La sessione lato server è generalmente supportata da un cookie. Alcuni moduli web richiedono che tu abbia effettuato l'accesso e / o che sia tracciato da una sessione. Puoi utilizzare l' CookieHandlerAPI per conservare i cookie. È necessario preparare un CookieManagercon un CookiePolicydi ACCEPT_ALLprima di inviare tutte le richieste 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();
// ...

Si noti che questo non funziona sempre correttamente in tutte le circostanze. Se non riesce, è meglio raccogliere e impostare manualmente le intestazioni dei cookie. Fondamentalmente è necessario afferrare tutte le Set-Cookieintestazioni dalla risposta dell'accesso o della prima GETrichiesta e quindi passare attraverso le richieste successive.

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

L' split(";", 2)[0]è lì per sbarazzarsi di attributi di cookie, che sono irrilevanti per il lato server come expires, pathecc In alternativa, si potrebbe anche usare cookie.substring(0, cookie.indexOf(';'))al posto di split().


Modalità streaming

Per HttpURLConnectionimpostazione predefinita, bufferizza l' intero corpo della richiesta prima di inviarlo effettivamente, indipendentemente dal fatto che tu abbia impostato tu stesso una lunghezza del contenuto fissa connection.setRequestProperty("Content-Length", contentLength);. Ciò può causare OutOfMemoryExceptions ogni volta che si inviano contemporaneamente richieste POST di grandi dimensioni (ad esempio, il caricamento di file). Per evitare ciò, si desidera impostare il HttpURLConnection#setFixedLengthStreamingMode().

httpConnection.setFixedLengthStreamingMode(contentLength);

Ma se la lunghezza del contenuto non è davvero nota in anticipo, è possibile utilizzare la modalità di streaming suddivisa impostando di HttpURLConnection#setChunkedStreamingMode()conseguenza. Ciò imposterà l' Transfer-Encodingintestazione HTTP su chunkedcui forzerà l'invio del corpo della richiesta in blocchi. L'esempio seguente invierà il corpo in blocchi di 1 KB.

httpConnection.setChunkedStreamingMode(1024);

User-Agent

Può succedere che una richiesta restituisca una risposta inaspettata, mentre funziona bene con un browser web reale . Il lato server sta probabilmente bloccando le richieste in base all'intestazione della User-Agentrichiesta. Per URLConnectionimpostazione predefinita, la volontà lo imposterà Java/1.6.0_19dove l'ultima parte è ovviamente la versione JRE. Puoi sovrascriverlo come segue:

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.

Utilizzare la stringa User-Agent da un browser recente .


Gestione degli errori

Se il codice di risposta HTTP è 4nn(Errore client) o 5nn(Errore server), potresti voler leggere HttpURLConnection#getErrorStream()per vedere se il server ha inviato utili informazioni di errore.

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

Se il codice di risposta HTTP è -1, qualcosa non ha funzionato con la connessione e la gestione della risposta. L' HttpURLConnectionimplementazione nei vecchi JRE è in qualche modo difettosa nel mantenere vive le connessioni. È possibile disattivarlo impostando la http.keepAliveproprietà di sistema su false. Puoi farlo a livello di codice all'inizio della tua applicazione:

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

Caricamento file

Normalmente useresti la multipart/form-datacodifica per contenuti POST misti (dati binari e di caratteri). La codifica è descritta più dettagliatamente in 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();
}

Se l'altro lato è a HttpServlet, doPost()verrà chiamato il suo metodo e le parti saranno disponibili da HttpServletRequest#getPart()(nota, quindi no getParameter() e così via!). Il getPart()metodo è tuttavia relativamente nuovo, è stato introdotto in Servlet 3.0 (Glassfish 3, Tomcat 7, ecc.). Prima di Servlet 3.0, la scelta migliore è utilizzare FileUpload di Apache Commons per analizzare una multipart/form-datarichiesta. Vedi anche questa risposta per esempi di approccio FileUpload e Servelt 3.0.


Gestione di siti HTTPS non attendibili o non configurati correttamente

A volte è necessario collegare un URL HTTPS, forse perché si sta scrivendo un raschietto web. In tal caso, è possibile che si verifichino problemi javax.net.ssl.SSLException: Not trusted server certificatecon alcuni siti HTTPS che non mantengono aggiornati i loro certificati SSL java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] foundo javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_namecon alcuni siti HTTPS non configurati correttamente.

Il seguente staticinizializzatore di una sola volta nella classe del web scraper dovrebbe rendere HttpsURLConnectionpiù indulgente su tali siti HTTPS e quindi non generare più tali eccezioni.

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

Ultime parole

L' Apache HttpComponents HttpClient è molto più conveniente in tutto questo :)


Analisi ed estrazione di HTML

Se tutto ciò che vuoi è analizzare ed estrarre dati dall'HTML, allora meglio usare un parser HTML come Jsoup


119
Dovresti posizionare prima il link apache, in modo che le persone in cerca di una soluzione lo trovino più velocemente;)
ZeissS,

40
@ivanceras: se non riesci a ridurlo in base alle informazioni contenute in questa risposta, ti preghiamo di premere il Ask Questionpulsante in alto a destra.
BalusC,

3
@Brais: leggi le specifiche. La --parte non fa parte del confine stesso. È solo una stringa di separazione. Ho ripristinato la tua modifica non valida.
BalusC

7
@BalusC grazie mille per un tutorial così perfetto. Includi anche un'intestazione come "Chiusura di flussi / connessioni". Sono davvero confuso su quando e quali flussi / connessioni chiudere.

10
La parte triste è che su Android è non consiglia di utilizzare l'Apache HttpClientora ed HttpURLConnectionè crudele. android-developers.blogspot.in/2011/09/…
yati sagade

91

Quando si lavora con HTTP è quasi sempre più utile fare riferimento HttpURLConnectionpiuttosto che alla classe base URLConnection(poiché URLConnectionè una classe astratta quando si chiede URLConnection.openConnection()su un URL HTTP è quello che si ottiene comunque).

Quindi puoi invece fare affidamento sull'impostazione URLConnection#setDoOutput(true)implicita del metodo di richiesta su POST invece di ciò httpURLConnection.setRequestMethod("POST")che alcuni potrebbero trovare più naturali (e che ti consente anche di specificare altri metodi di richiesta come PUT , DELETE , ...).

Fornisce inoltre utili costanti HTTP in modo da poter fare:

int responseCode = httpURLConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {

1
setDoOutPut true è stato il mio problema che ha impostato GET su POST. Grazie
Patrick Kafka il

22
Se si sta tentando di scrivere i dati al OutputStream si deve ancora impostato setDoOutput()al truecontrario viene generata un'eccezione (anche se setRequestMethod("POST")). Per essere chiari: l'impostazione URLConnection#setDoOutput(true)per impostare trueimplicitamente il metodo di richiesta su POST, ma l'impostazione httpURLConnection.setRequestMethod("POST")su POST non è implicitamente impostata setDoOutput()su true.
Tony Chan,

54

Ispirato da questa e altre domande su SO, ho creato un client di base-http minimo open source che incarna la maggior parte delle tecniche trovate qui.

google-http-java-client è anche un'ottima risorsa open source.


Stavo pensando lo stesso. Ma potrebbe anche essere bello avere una libreria barebone / semplice Java che utilizza solo il codice URLConnection come presentato qui che incapsula il codice in metodi più semplici per eseguire HTTP GET, POST, ecc. La libreria può quindi essere compilata e impacchettata come JAR e importato / utilizzato nel codice Java o il file di classe di origine può essere incluso nel progetto Java se non si desiderano JAR esterni. Questo potrebbe essere fatto con altre librerie come Apache, ecc., Ma è più una seccatura rispetto a una semplice libreria di classi di 1 file che utilizza URLConnection.
David

rapidvaluesolutions.com/tech_blog/… favorisce HttpURLConnection su HttpClient
Ravindra babu

24

Ti suggerisco di dare un'occhiata al codice su kevinsawicki / http-request , in pratica è un wrapper in cima a HttpUrlConnectionesso fornisce un'API molto più semplice nel caso in cui tu voglia solo fare le richieste in questo momento o puoi dare un'occhiata alle fonti ( non è troppo grande) per dare un'occhiata a come vengono gestite le connessioni.

Esempio: effettuare una GETrichiesta con tipo di contenuto application/jsone alcuni parametri di query:

// GET http://google.com?q=baseball%20gloves&size=100
String response = HttpRequest.get("http://google.com", true, "q", "baseball gloves", "size", 100)
        .accept("application/json")
        .body();
System.out.println("Response was: " + response);

24

Ci sono 2 opzioni che puoi scegliere con Hit URL HTTP: GET / POST

RICHIEDI Richiesta: -

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

Richiesta POST: -

HttpURLConnection.setFollowRedirects(true); // defaults to true

String url = "https://name_of_the_url"
URL request_url = new URL(url);
HttpURLConnection http_conn = (HttpURLConnection)request_url.openConnection();
http_conn.setConnectTimeout(100000);
http_conn.setReadTimeout(100000);
http_conn.setInstanceFollowRedirects(true);
http_conn.setDoOutput(true);
PrintWriter out = new PrintWriter(http_conn.getOutputStream());
if (urlparameter != null) {
   out.println(urlparameter);
}
out.close();
out = null;
System.out.println(String.valueOf(http_conn.getResponseCode()));

3
Come è possibile visualizzare la risposta JSON effettiva?
Sora,

21

Sono stato anche molto ispirato da questa risposta.

Sono spesso a progetti in cui ho bisogno di fare un po 'di HTTP, e potrei non voler portare molte dipendenze di terze parti (che portano altri e così via e così via, ecc.)

Ho iniziato a scrivere i miei programmi di utilità basati su alcune di queste conversazioni (non tutte le altre):

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 {

Quindi ci sono solo un mucchio o metodi statici.

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

Quindi pubblica ...

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

Bene hai avuto l'idea ....

Ecco i test:

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


}

Puoi trovare il resto qui:

https://github.com/RichardHightower/boon

Il mio obiettivo è quello di fornire le cose comuni che uno vorrebbe fare in un modo un po 'più semplice di ...


2
È strano che nel doPostmetodo sia presente un charsetparametro, che viene utilizzato per impostare l'intestazione della richiesta, ma i dati vengono scritti con un set di caratteri hardcoded IO.CHARSET. Un insetto?
Vit Khudenko,

21

Aggiornare

Il nuovo client HTTP è stato fornito con Java 9 ma come parte di un modulo Incubator denominato jdk.incubator.httpclient. I moduli dell'incubatore sono un mezzo per mettere le API non finali nelle mani degli sviluppatori mentre le API avanzano verso la finalizzazione o la rimozione in una versione futura.

In Java 9, è possibile inviare una GETrichiesta come:

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

Quindi puoi esaminare il reso HttpResponse:

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

Dal momento che questo nuovo client HTTP è in java.httpclient jdk.incubator.httpclientmodulo, dovresti dichiarare questa dipendenza nel tuo module-info.javafile:

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

1
Ulteriore aggiornamento: il modulo è fuori dallo stato di incubazione. Ora è java.net.http , non jdk.incubator.httpclient.
VGR

17

Inizialmente sono stato ingannato da questo articolo che favorisce HttpClient.

Più tardi mi sono reso conto che HttpURLConnectionrimarrà da questo articolo

Secondo il blog di Google :

Il client HTTP Apache ha meno bug su Eclair e Froyo. È la scelta migliore per queste versioni. Per Gingerbread, HttpURLConnection è la scelta migliore. L'API semplice e le dimensioni ridotte lo rendono perfetto per Android.

La compressione trasparente e la cache di risposta riducono l'uso della rete, migliorano la velocità e risparmiano la batteria. Le nuove applicazioni dovrebbero usare HttpURLConnection; è dove spenderemo la nostra energia per il futuro.

Dopo aver letto questo articolo e alcune altre domande sullo stack over, sono convinto che HttpURLConnectionrimarrà per periodi più lunghi.

Alcune delle domande SE che favoriscono HttpURLConnections:

Su Android, invia una richiesta POST con i dati del modulo con codifica URL senza utilizzare UrlEncodedFormEntity

HttpPost funziona nel progetto Java, non in Android


15

C'è anche OkHttp , che è un client HTTP efficiente per impostazione predefinita:

  • Il supporto HTTP / 2 consente a tutte le richieste allo stesso host di condividere un socket.
  • Il pool di connessioni riduce la latenza delle richieste (se HTTP / 2 non è disponibile).
  • GZIP trasparente riduce le dimensioni del download.
  • La memorizzazione nella cache di risposta evita completamente la rete per richieste ripetute.

Innanzitutto creare un'istanza di OkHttpClient:

OkHttpClient client = new OkHttpClient();

Quindi, prepara la tua GETrichiesta:

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

infine, usa OkHttpClientper inviare preparati Request:

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

Per maggiori dettagli, puoi consultare la documentazione di OkHttp



Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.