Comment utiliser java.net.URLConnection pour déclencher et gérer les requêtes HTTP?


1949

L'utilisation de java.net.URLConnectionest souvent demandée ici, et le didacticiel Oracle est trop concis à ce sujet.

Ce didacticiel montre essentiellement comment déclencher une demande GET et lire la réponse. Il n'explique nulle part comment l'utiliser pour, entre autres, effectuer une demande POST, définir des en-têtes de demande, lire des en-têtes de réponse, gérer les cookies, soumettre un formulaire HTML, télécharger un fichier, etc.

Alors, comment puis-je utiliser java.net.URLConnectionpour déclencher et gérer les requêtes HTTP "avancées"?

Réponses:


2711

D'abord un avertissement préalable: les extraits de code publiés sont tous des exemples de base. Vous devrez gérer les éléments triviaux IOExceptionet RuntimeExceptionsimilaires NullPointerException, ArrayIndexOutOfBoundsExceptionet vous épouser.


En train de préparer

Nous devons d'abord connaître au moins l'URL et le jeu de caractères. Les paramètres sont facultatifs et dépendent des exigences fonctionnelles.

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

Les paramètres de requête doivent être au name=valueformat et être concaténés par &. Normalement, vous devez également encoder par URL les paramètres de requête avec le jeu de caractères spécifié à l'aide de URLEncoder#encode().

C'est String#format()juste pour plus de commodité. Je le préfère lorsque j'aurais besoin de l'opérateur de concaténation de chaînes +plus de deux fois.


Lancer une requête HTTP GET avec (facultativement) des paramètres de requête

C'est une tâche insignifiante. C'est la méthode de demande par défaut.

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

Toute chaîne de requête doit être concaténée avec l'URL à l'aide de ?. L'en- Accept-Charsettête peut indiquer au serveur le codage des paramètres. Si vous n'envoyez aucune chaîne de requête, vous pouvez laisser l'en- Accept-Charsettête à l'écart. Si vous n'avez pas besoin de définir d'en-têtes, vous pouvez même utiliser la URL#openStream()méthode de raccourci.

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

Dans les deux cas, si l'autre côté est un HttpServlet, sa doGet()méthode sera appelée et les paramètres seront disponibles par HttpServletRequest#getParameter().

À des fins de test, vous pouvez imprimer le corps de la réponse sur stdout comme ci-dessous:

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

Lancer une requête HTTP POST avec des paramètres de requête

Réglage de la URLConnection#setDoOutput()à truedéfinit implicitement la méthode de requête POST à. Le HTTP POST standard comme le font les formulaires Web est de type application/x-www-form-urlencodeddans lequel la chaîne de requête est écrite dans le corps de la demande.

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

Remarque: chaque fois que vous souhaitez soumettre un formulaire HTML par programme, n'oubliez pas de prendre les name=valuepaires d' <input type="hidden">éléments dans la chaîne de requête et bien sûr aussi la name=valuepaire de l' <input type="submit">élément sur lequel vous souhaitez "appuyer" par programme (car qui a généralement été utilisé côté serveur pour distinguer si un bouton a été enfoncé et si oui, lequel).

Vous pouvez également jeter l'obtenu URLConnectionà HttpURLConnectionet utiliser sa HttpURLConnection#setRequestMethod()place. Mais si vous essayez d'utiliser la connexion pour la sortie, vous devez toujours la régler URLConnection#setDoOutput()sur true.

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

Dans les deux cas, si l'autre côté est un HttpServlet, sa doPost()méthode sera appelée et les paramètres seront disponibles par HttpServletRequest#getParameter().


Déclencher réellement la requête HTTP

Vous pouvez déclencher explicitement la demande HTTP avec URLConnection#connect(), mais la demande sera automatiquement déclenchée à la demande lorsque vous souhaitez obtenir des informations sur la réponse HTTP, telles que le corps de la réponse à l'aide de URLConnection#getInputStream()etc. Les exemples ci-dessus font exactement cela, donc l' connect()appel est en fait superflu.


Collecte des informations de réponse HTTP

  1. Statut de réponse HTTP :

    Vous en avez besoin HttpURLConnectionici. Jetez-le d'abord si nécessaire.

    int status = httpConnection.getResponseCode();
  2. En-têtes de réponse HTTP :

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

    Lorsque le Content-Typecontient un charsetparamètre, le corps de la réponse est probablement basé sur du texte et nous aimerions alors traiter le corps de la réponse avec le codage de caractères spécifié côté serveur.

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

Maintenir la session

La session côté serveur est généralement soutenue par un cookie. Certains formulaires Web nécessitent que vous soyez connecté et / ou que vous soyez suivi par une session. Vous pouvez utiliser l' CookieHandlerAPI pour gérer les cookies. Vous devez préparer un CookieManageravec CookiePolicydes ACCEPT_ALLavant d' envoyer toutes les requêtes 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();
// ...

Notez que cela est connu pour ne pas toujours fonctionner correctement en toutes circonstances. Si cela échoue pour vous, le mieux est de rassembler et de définir manuellement les en-têtes des cookies. Vous devez essentiellement récupérer tous les en- Set-Cookietêtes de la réponse de la connexion ou de la première GETdemande, puis les transmettre aux demandes suivantes.

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

Le split(";", 2)[0]est là pour se débarrasser des attributs de cookie qui ne sont pas pertinents pour le côté serveur comme expires, path, etc. Sinon, vous pouvez également utiliser au cookie.substring(0, cookie.indexOf(';'))lieu de split().


Mode streaming

Le HttpURLConnectiontestament par défaut met en mémoire tampon tout le corps de la requête avant de l'envoyer, que vous ayez défini vous-même une longueur de contenu fixe connection.setRequestProperty("Content-Length", contentLength);. Cela peut entraîner des OutOfMemoryExceptions lorsque vous envoyez simultanément des demandes POST volumineuses (par exemple, le téléchargement de fichiers). Pour éviter cela, vous souhaitez définir le HttpURLConnection#setFixedLengthStreamingMode().

httpConnection.setFixedLengthStreamingMode(contentLength);

Mais si la longueur du contenu n'est vraiment pas connue à l'avance, vous pouvez utiliser le mode de diffusion en morceaux en définissant le en HttpURLConnection#setChunkedStreamingMode()conséquence. Cela définira l'en- Transfer-Encodingtête HTTP sur chunkedlequel forcera l'envoi du corps de la demande par blocs. L'exemple ci-dessous enverra le corps en morceaux de 1 Ko.

httpConnection.setChunkedStreamingMode(1024);

Agent utilisateur

Il peut arriver qu'une demande renvoie une réponse inattendue, alors qu'elle fonctionne correctement avec un véritable navigateur Web . Le côté serveur bloque probablement les demandes sur la base de l'en- User-Agenttête de demande. La URLConnectionvolonté par défaut le placera là Java/1.6.0_19où la dernière partie est évidemment la version JRE. Vous pouvez remplacer cela comme suit:

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.

Utilisez la chaîne User-Agent d'un navigateur récent .


La gestion des erreurs

Si le code de réponse HTTP est 4nn(Erreur client) ou 5nn(Erreur serveur), vous pouvez lire le HttpURLConnection#getErrorStream()pour voir si le serveur a envoyé des informations d'erreur utiles.

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

Si le code de réponse HTTP est -1, quelque chose s'est mal passé avec la connexion et la gestion des réponses. L' HttpURLConnectionimplémentation est dans les anciens JRE quelque peu boguée avec le maintien des connexions en vie. Vous pouvez le désactiver en définissant la http.keepAlivepropriété système sur false. Vous pouvez le faire par programme au début de votre application en:

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

Téléchargement de fichiers

Vous utiliseriez normalement l' multipart/form-dataencodage pour le contenu POST mixte (données binaires et caractères). Le codage est décrit plus en détail dans la 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();
}

Si l'autre côté est un HttpServlet, alors sa doPost()méthode sera appelée et les pièces seront disponibles par HttpServletRequest#getPart()(notez donc pas getParameter() et ainsi de suite!). La getPart()méthode est cependant relativement nouvelle, elle est introduite dans Servlet 3.0 (Glassfish 3, Tomcat 7, etc.). Avant Servlet 3.0, votre meilleur choix est d'utiliser Apache Commons FileUpload pour analyser une multipart/form-datademande. Consultez également cette réponse pour des exemples des approches FileUpload et Servelt 3.0.


Gestion des sites HTTPS non fiables ou mal configurés

Parfois, vous devez connecter une URL HTTPS, peut-être parce que vous écrivez un grattoir Web. Dans ce cas, vous pouvez probablement rencontrer un javax.net.ssl.SSLException: Not trusted server certificatesur certains sites HTTPS qui ne tiennent pas à jour leurs certificats SSL, ou un java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] foundou javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_namesur certains sites HTTPS mal configurés.

L' staticinitialiseur à exécution unique suivant dans votre classe Web Scraper devrait rendre HttpsURLConnectionplus indulgents ces sites HTTPS et donc ne plus lever ces exceptions.

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

Derniers mots

L' Apache HttpComponents HttpClient est beaucoup plus pratique dans tout cela :)


Analyse et extraction de HTML

Si tout ce que vous voulez c'est analyser et extraire des données de HTML, alors mieux vaut utiliser un analyseur HTML comme Jsoup


119
Vous devez d'abord placer le lien apache, afin que les personnes à la recherche d'une solution la trouvent plus rapidement;)
ZeissS

40
@ivanceras: Si vous ne pouvez pas le résumer sur la base des informations de cette réponse, veuillez appuyer sur le Ask Questionbouton en haut à droite.
BalusC

3
@Brais: Veuillez lire les spécifications. La --partie ne fait pas partie de la frontière elle-même. C'est juste une chaîne de séparation. J'ai annulé votre modification non valide.
BalusC

7
@BalusC merci beaucoup pour un tutoriel aussi parfait. Veuillez également inclure un titre comme "Fermeture des flux / connexions". Je suis vraiment confus quant au moment et aux flux / connexions à fermer.

10
Le plus triste, c'est que sur Android, il n'est pas recommandé d'utiliser Apache HttpClientmaintenant et HttpURLConnectionest cruel. android-developers.blogspot.in/2011/09/…
yati sagade

91

Lorsque vous travaillez avec HTTP, il est presque toujours plus utile de se référer à HttpURLConnectionla classe de base URLConnection(car il URLConnections'agit d'une classe abstraite lorsque vous demandez URLConnection.openConnection()une URL HTTP, c'est ce que vous obtiendrez de toute façon).

Ensuite, vous pouvez au lieu de vous fier URLConnection#setDoOutput(true)à définir implicitement la méthode de demande sur POST à ​​la place, ce httpURLConnection.setRequestMethod("POST")que certains pourraient trouver plus naturel (et qui vous permet également de spécifier d'autres méthodes de demande telles que PUT , DELETE , ...).

Il fournit également des constantes HTTP utiles pour que vous puissiez faire:

int responseCode = httpURLConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {

1
setDoOutPut true était mon problème qui a défini mon GET sur un POST. Merci
Patrick Kafka

22
Si vous essayez d'écrire des données dans le flux de sortie, vous devez toujours définir setDoOutput()sur truesinon une exception est levée (même si vous setRequestMethod("POST")). Pour être clair: la définition URLConnection#setDoOutput(true)de truedéfinit implicitement la méthode de demande sur POST, mais la définition httpURLConnection.setRequestMethod("POST")de POST n'est pas implicitement définie setDoOutput()sur true.
Tony Chan

54

Inspiré par cela et d'autres questions sur SO, j'ai créé un client http-basic- open source minimal qui incarne la plupart des techniques trouvées ici.

google-http-java-client est également une excellente ressource open source.


Je pensais juste la même chose. Mais il pourrait également être agréable d'avoir une bibliothèque Java simple / barebones qui utilise uniquement le code URLConnection tel que présenté ici qui encapsule le code dans des méthodes plus simples pour faire un HTTP GET, POST, etc. La bibliothèque peut ensuite être compilée et empaquetée en JAR et importé / utilisé dans du code Java ou le fichier de classe source peut être inclus dans le projet Java si des fichiers JAR externes ne sont pas souhaités. Cela pourrait être fait avec d'autres bibliothèques comme Apache, etc. mais est plus pénible qu'une simple bibliothèque de classes de 1 fichier utilisant URLConnection.
David

rapidvaluesolutions.com/tech_blog/… favorise HttpURLConnection sur HttpClient
Ravindra babu

24

Je vous suggère de jeter un oeil au code sur kevinsawicki / http-request , c'est essentiellement un wrapper au-dessus HttpUrlConnectionqui fournit une API beaucoup plus simple au cas où vous voudriez simplement faire les demandes maintenant ou vous pouvez jeter un oeil aux sources ( ce n'est pas trop gros) pour voir comment les connexions sont gérées.

Exemple: effectuez une GETdemande avec le type de contenu application/jsonet certains paramètres de requête:

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

Il y a 2 options que vous pouvez utiliser avec les URL URL HTTP: GET / POST

OBTENIR la demande: -

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

Demande 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
Comment pouvez-vous afficher la réponse JSON réelle?
Sora

21

J'ai également été très inspiré par cette réponse.

Je suis souvent sur des projets où j'ai besoin de faire du HTTP, et je ne veux peut-être pas apporter beaucoup de dépendances tierces (qui en amènent d'autres et ainsi de suite et ainsi de suite, etc.)

J'ai commencé à écrire mes propres utilitaires sur la base d'une partie de cette conversation (pas n'importe où):

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 {

Ensuite, il y a juste un tas ou des méthodes statiques.

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

Ensuite, postez ...

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

Eh bien, vous avez l'idée ....

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


}

Vous pouvez trouver le reste ici:

https://github.com/RichardHightower/boon

Mon objectif est de fournir les choses courantes que l'on voudrait faire de manière un peu plus facile que ...


2
Il est étrange que dans le doPost méthode il y ait un charsetparamètre, qui est utilisé pour définir l'en-tête de la demande, mais ensuite les données sont écrites avec un jeu de caractères codé en dur IO.CHARSET. Un bug?
Vit Khudenko

21

Mise à jour

Le nouveau client HTTP est livré avec Java 9 mais dans le cadre d'un module d'incubateur nommé jdk.incubator.httpclient. Les modules d'incubateur sont un moyen de mettre les API non finales entre les mains des développeurs tandis que les API progressent vers la finalisation ou la suppression dans une future version.

Dans Java 9, vous pouvez envoyer une GETdemande comme:

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

Ensuite, vous pouvez examiner le retour HttpResponse:

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

Puisque ce nouveau client HTTP est en java.httpclient jdk.incubator.httpclient module, vous devez déclarer cette dépendance dans votre module-info.java fichier:

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

1
Poursuite de la mise à jour: le module est hors statut d'incubation. C'est maintenant java.net.http , pas jdk.incubator.httpclient.
VGR

17

Au début, j'ai été induit en erreur par cette article qui favorise HttpClient.

Plus tard, j'ai réalisé que HttpURLConnection allait rester de cet article

Selon le blog Google :

Le client Apache HTTP a moins de bugs sur Eclair et Froyo. C'est le meilleur choix pour ces versions. Pour Gingerbread, HttpURLConnection est le meilleur choix. Son API simple et sa petite taille le rendent idéal pour Android.

La compression transparente et la mise en cache des réponses réduisent l'utilisation du réseau, améliorent la vitesse et économisent la batterie. Les nouvelles applications devraient utiliser HttpURLConnection; c'est là que nous dépenserons notre énergie à l'avenir.

Après avoir lu cet article et quelques autres questions sur la pile de flux, je suis convaincu queHttpURLConnection va durer plus longtemps.

Certaines des questions SE favorisant HttpURLConnections:

Sur Android, effectuez une demande POST avec des données de formulaire encodées URL sans utiliser UrlEncodedFormEntity

HttpPost fonctionne dans le projet Java, pas dans Android


15

Il existe également OkHttp , qui est un client HTTP efficace par défaut:

  • La prise en charge HTTP / 2 permet à toutes les demandes adressées au même hôte de partager un socket.
  • Le regroupement de connexions réduit la latence des demandes (si HTTP / 2 n'est pas disponible).
  • GZIP transparent réduit les tailles de téléchargement.
  • La mise en cache des réponses évite complètement le réseau pour les demandes répétées.

Créez d'abord une instance de OkHttpClient:

OkHttpClient client = new OkHttpClient();

Ensuite, préparez votre GETdemande:

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

enfin, utilisez OkHttpClientpour envoyer préparé Request:

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

Pour plus de détails, vous pouvez consulter la documentation d'OkHttp



En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.