¿Cómo usar java.net.URLConnection para disparar y manejar solicitudes HTTP?

1949

El uso de java.net.URLConnectionle pregunta por bastante a menudo aquí, y el tutorial de Oracle es demasiado concisa al respecto.

Ese tutorial básicamente solo muestra cómo disparar una solicitud GET y leer la respuesta. No explica en ninguna parte cómo usarlo, entre otros, para realizar una solicitud POST, establecer encabezados de solicitud, leer encabezados de respuesta, manejar cookies, enviar un formulario HTML, cargar un archivo, etc.

Entonces, ¿cómo puedo usar java.net.URLConnectionpara disparar y manejar solicitudes HTTP "avanzadas"?

BalusC
fuente

Respuestas:

2711

Primero un descargo de responsabilidad de antemano: los fragmentos de código publicados son todos ejemplos básicos. Tendrás que manejar cosas triviales IOExceptiony me RuntimeExceptiongusta NullPointerException, ArrayIndexOutOfBoundsExceptiony consorte tú mismo.


Preparando

Primero necesitamos saber al menos la URL y el juego de caracteres. Los parámetros son opcionales y dependen de los requisitos funcionales.

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

Los parámetros de consulta deben estar en name=valueformato y estar concatenados por &. Normalmente también codificaría en URL los parámetros de consulta con el juego de caracteres especificado utilizando URLEncoder#encode().

El String#format()es solo por conveniencia. Lo prefiero cuando necesito el operador de concatenación de cadenas +más de dos veces.


Disparando una solicitud HTTP GET con (opcionalmente) parámetros de consulta

Es una tarea trivial. Es el método de solicitud predeterminado.

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

Cualquier cadena de consulta se debe concatenar a la URL usando ?. El Accept-Charsetencabezado puede indicar al servidor en qué codificación se encuentran los parámetros. Si no envía ninguna cadena de consulta, puede dejar el Accept-Charsetencabezado ausente. Si no necesita configurar ningún encabezado, incluso puede usar el URL#openStream()método de acceso directo.

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

De cualquier manera, si el otro lado es a HttpServlet, entonces doGet()se llamará a su método y los parámetros estarán disponibles por HttpServletRequest#getParameter().

Para fines de prueba, puede imprimir el cuerpo de respuesta en stdout de la siguiente manera:

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

Disparando una solicitud HTTP POST con parámetros de consulta

Establecer el URLConnection#setDoOutput()para trueimplícitamente establece el método de solicitud a POST. El HTTP POST estándar como lo hacen los formularios web es del tipo application/x-www-form-urlencodeden el que la cadena de consulta se escribe en el cuerpo de la solicitud.

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: cada vez que desee enviar un formulario HTML mediante programación, no olvide tomar los name=valuepares de cualquier <input type="hidden">elemento en la cadena de consulta y, por supuesto, también el name=valuepar del <input type="submit">elemento que desea "presionar" mediante programación (porque eso generalmente se ha utilizado en el lado del servidor para distinguir si se presionó un botón y, de ser así, cuál).

También puede emitir el obtenido URLConnectiona HttpURLConnectiony utilizar su HttpURLConnection#setRequestMethod()lugar. Pero si usted está tratando de utilizar la conexión para la salida todavía necesita conjunto URLConnection#setDoOutput()a true.

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

De cualquier manera, si el otro lado es a HttpServlet, entonces doPost()se llamará a su método y los parámetros estarán disponibles por HttpServletRequest#getParameter().


En realidad disparando la solicitud HTTP

Puede disparar la solicitud HTTP explícitamente URLConnection#connect(), pero la solicitud se disparará automáticamente a pedido cuando desee obtener información sobre la respuesta HTTP, como el cuerpo de la respuesta que usa, URLConnection#getInputStream()etc. Los ejemplos anteriores hacen exactamente eso, por lo que la connect()llamada es de hecho superflua.


Recopilación de información de respuesta HTTP

  1. Estado de respuesta HTTP :

    Necesitas un HttpURLConnectionaquí. Echa primero si es necesario.

    int status = httpConnection.getResponseCode();
  2. Encabezados de respuesta HTTP :

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

    Cuando Content-Typecontiene un charsetparámetro, es probable que el cuerpo de la respuesta esté basado en texto y nos gustaría procesar el cuerpo de la respuesta con la codificación de caracteres especificada en el lado del servidor.

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

Manteniendo la sesión

La sesión del lado del servidor generalmente está respaldada por una cookie. Algunos formularios web requieren que haya iniciado sesión y / o que una sesión los rastree. Puede usar la CookieHandlerAPI para mantener las cookies. Es necesario preparar una CookieManagercon una CookiePolicyde ACCEPT_ALLantes de enviar todas las peticiones 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();
// ...

Tenga en cuenta que se sabe que esto no siempre funciona correctamente en todas las circunstancias. Si falla para usted, lo mejor es reunir y configurar manualmente los encabezados de las cookies. Básicamente, debe tomar todos los Set-Cookieencabezados de la respuesta del inicio de sesión o la primera GETsolicitud y luego pasar esto a través de las solicitudes posteriores.

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

El split(";", 2)[0]está allí para deshacerse de los atributos de galletas que son irrelevantes para el lado del servidor como expires, path, etc. Alternativamente, se podría también utilizar cookie.substring(0, cookie.indexOf(';'))en lugar de split().


Modo de transmisión

Por HttpURLConnectiondefecto, almacenará en el búfer todo el cuerpo de la solicitud antes de enviarlo, independientemente de si ha establecido una longitud de contenido fija usted mismo connection.setRequestProperty("Content-Length", contentLength);. Esto puede causar OutOfMemoryExceptioncorreos electrónicos cada vez que envía solicitudes POST grandes (por ejemplo, cargando archivos). Para evitar esto, desea configurar el HttpURLConnection#setFixedLengthStreamingMode().

httpConnection.setFixedLengthStreamingMode(contentLength);

Pero si la longitud del contenido realmente no se conoce de antemano, entonces puede hacer uso del modo de transmisión fragmentada configurando en HttpURLConnection#setChunkedStreamingMode()consecuencia. Esto establecerá el Transfer-Encodingencabezado HTTP al chunkedque forzará el envío del cuerpo de la solicitud en fragmentos. El siguiente ejemplo enviará el cuerpo en trozos de 1 KB.

httpConnection.setChunkedStreamingMode(1024);

Agente de usuario

Puede suceder que una solicitud devuelva una respuesta inesperada, mientras funciona bien con un navegador web real . El lado del servidor probablemente esté bloqueando las solicitudes en función del User-Agentencabezado de la solicitud. Por URLConnectiondefecto, la establecerá en Java/1.6.0_19donde la última parte es obviamente la versión JRE. Puede anular esto de la siguiente manera:

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.

Use la cadena de User-Agent de un navegador reciente .


Manejo de errores

Si el código de respuesta HTTP es 4nn(Error del cliente) o 5nn(Error del servidor), es posible que desee leer el HttpURLConnection#getErrorStream()para ver si el servidor ha enviado información de error útil.

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

Si el código de respuesta HTTP es -1, entonces algo salió mal con la conexión y el manejo de la respuesta. La HttpURLConnectionimplementación está en JRE anteriores algo defectuosos para mantener las conexiones vivas. Es posible que desee desactivarlo estableciendo la http.keepAlivepropiedad del sistema en false. Puede hacer esto mediante programación al comienzo de su aplicación:

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

Subiendo archivos

Normalmente usaría la multipart/form-datacodificación para contenido POST mixto (datos binarios y de caracteres). La codificación se describe con más detalle en 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 el otro lado es a HttpServlet, entonces doPost()se llamará a su método y las partes estarán disponibles por HttpServletRequest#getPart()(¡nota, por lo tanto no,getParameter() etc.!). El getPart()método es, sin embargo relativamente nuevo, es introducido en Servlet 3.0 (Glassfish 3, Tomcat 7, etc). Antes de Servlet 3.0, su mejor opción es usar Apache Commons FileUpload para analizar una multipart/form-datasolicitud. Consulte también esta respuesta para ver ejemplos de los enfoques FileUpload y Servelt 3.0.


Manejo de sitios HTTPS no confiables o mal configurados

A veces necesita conectar una URL HTTPS, tal vez porque está escribiendo un raspador web. En ese caso, es probable que se enfrente javax.net.ssl.SSLException: Not trusted server certificatea algunos sitios HTTPS que no mantienen sus certificados SSL actualizados, java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] foundo javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_namea algunos sitios HTTPS mal configurados.

El siguiente staticinicializador de una sola vez en su clase de raspador web debería ser HttpsURLConnectionmás indulgente con respecto a esos sitios HTTPS y, por lo tanto, ya no arrojará esas excepciones.

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

Ultimas palabras

El Apatt HttpComponents HttpClient es mucho más conveniente en todo esto :)


Analizar y extraer HTML

Si todo lo que quieres es analizar y extraer datos de HTML, mejor usa un analizador HTML como Jsoup

BalusC
fuente
119
Primero debe colocar el enlace de apache, para que las personas que buscan una solución la encuentren más rápido;)
ZeissS
40
@ivanceras: si no puede reducirlo según la información de esta respuesta, presione el Ask Questionbotón en la parte superior derecha.
BalusC
3
@Brais: Por favor, lea las especificaciones. La --parte no es parte del límite en sí. Es solo una cadena de separación. Revertí tu edición no válida.
BalusC
77
@BalusC muchas gracias por un tutorial tan perfecto. Incluya también un encabezado como "Cerrar transmisiones / conexiones". Estoy realmente confundido acerca de cuándo y qué flujos / conexiones cerrar.
10
La parte triste es que en Android se no recomienda utilizar el Apache HttpClientahora y HttpURLConnectiones cruel. android-developers.blogspot.in/2011/09/…
yati sagade
91

Cuando se trabaja con HTTP, casi siempre es más útil hacer referencia a ella HttpURLConnectionque a la clase base URLConnection(ya que URLConnectiones una clase abstracta cuando se solicita URLConnection.openConnection()en una URL HTTP que es lo que obtendrá de todos modos).

Luego, en lugar de confiar en URLConnection#setDoOutput(true)establecer implícitamente el método de solicitud en POST, puede hacer lo httpURLConnection.setRequestMethod("POST")que algunos consideren más natural (y que también le permite especificar otros métodos de solicitud como PUT , DELETE , ...).

También proporciona constantes HTTP útiles para que pueda hacer:

int responseCode = httpURLConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {
Paal Thorstensen
fuente
1
setDoOutPut verdadero fue mi problema que configuró mi GET en una POST. Gracias
Patrick Kafka
22
Si está intentando escribir datos en el flujo de salida, aún debe establecerlo; setDoOutput()de lo truecontrario, se generará una excepción (incluso si usted setRequestMethod("POST")). Para ser claros: la configuración URLConnection#setDoOutput(true)de establecer trueimplícitamente el método de solicitud en POST, pero la configuración httpURLConnection.setRequestMethod("POST")de POST no se establece implícitamente setDoOutput()en true.
Tony Chan
54

Inspirado por esta y otras preguntas sobre SO, he creado un cliente básico básico de código abierto http que incorpora la mayoría de las técnicas que se encuentran aquí.

google-http-java-client también es un excelente recurso de código abierto.

David Chandler
fuente
Estaba pensando lo mismo. Pero también podría ser bueno tener una biblioteca Java básica o simple que solo use el código URLConnection como se presenta aquí y que encapsula el código a métodos más simples para hacer HTTP GET, POST, etc. La biblioteca se puede compilar y empaquetar como JAR y importado / usado en código Java o el archivo de clase fuente puede incluirse en un proyecto Java si no se desean archivos JAR externos. Esto podría hacerse con otras bibliotecas como Apache, etc., pero es más complicado en comparación con una biblioteca de clase de 1 archivo simple que usa URLConnection.
David
rapidvaluesolutions.com/tech_blog/… favorece HttpURLConnection sobre HttpClient
Ravindra babu
24

Le sugiero que eche un vistazo al código en kevinsawicki / http-request , es básicamente un contenedor HttpUrlConnectionque proporciona una API mucho más simple en caso de que solo quiera hacer las solicitudes en este momento o puede consultar las fuentes ( no es demasiado grande) para ver cómo se manejan las conexiones.

Ejemplo: realice una GETsolicitud con tipo de contenido application/jsony algunos parámetros de consulta:

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

Hay 2 opciones a las que puede acceder con HTTP URL Hits: GET / POST

OBTENER Solicitud: -

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

Solicitud POSTAL: -

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
fuente
3
¿Cómo puede ver la respuesta JSON real?
Sora
21

También me inspiró mucho esta respuesta.

A menudo estoy en proyectos en los que necesito hacer algo de HTTP, y es posible que no quiera traer muchas dependencias de terceros (que traen otros y así sucesivamente, etc.)

Comencé a escribir mis propias utilidades basadas en parte de esta conversación (no en ninguna parte):

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 {

Entonces solo hay un montón o métodos estáticos.

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

Luego publica ...

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

Bueno, ya captas la idea....

Aquí están las pruebas:

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


}

Puedes encontrar el resto aqui:

https://github.com/RichardHightower/boon

Mi objetivo es proporcionar las cosas comunes que uno quisiera hacer de una manera un poco más fácil que ...

RickHigh
fuente
2
Es extraño que en el doPostmétodo haya un charsetparámetro, que se usa para establecer el encabezado de la solicitud, pero luego los datos se escriben con un conjunto de caracteres codificado IO.CHARSET. ¿Un insecto?
Vit Khudenko
21

Actualizar

El nuevo Cliente HTTP se envió con Java 9 pero como parte de un módulo Incubator llamado jdk.incubator.httpclient. Los módulos de incubadora son un medio de poner las API no finales en manos de los desarrolladores mientras las API avanzan hacia la finalización o eliminación en una versión futura.

En Java 9, puede enviar una GETsolicitud como:

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

Luego puede examinar el devuelto HttpResponse:

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

Dado que este nuevo Cliente HTTP está en java.httpclient jdk.incubator.httpclientmódulo, debe declarar esta dependencia en su module-info.javaarchivo:

module com.foo.bar {
    requires jdk.incubator.httpclient;
}
revs Ali Dehghani
fuente
1
Actualización adicional: el módulo está fuera del estado de incubación. Ahora es java.net.http , no jdk.incubator.httpclient.
VGR
17

Inicialmente fui engañado por este artículo que favoreceHttpClient .

Más tarde me di cuenta de que HttpURLConnectioneste artículo se va a quedar

Según el blog de Google :

El cliente HTTP Apache tiene menos errores en Eclair y Froyo. Es la mejor opción para estos lanzamientos. Para Gingerbread, HttpURLConnection es la mejor opción. Su API simple y su pequeño tamaño lo hacen ideal para Android.

La compresión transparente y el almacenamiento en caché de respuesta reducen el uso de la red, mejoran la velocidad y ahorran batería. Las nuevas aplicaciones deben usar HttpURLConnection; es donde gastaremos nuestra energía en el futuro.

Después de leer este artículo y algunas otras preguntas de stack over flow, estoy convencido de que HttpURLConnectionse quedará por más tiempo.

Algunas de las preguntas SE favorecen HttpURLConnections:

En Android, realice una solicitud POST con datos de formulario codificado de URL sin usar UrlEncodedFormEntity

HttpPost funciona en proyectos Java, no en Android

Ravindra babu
fuente
15

También está OkHttp , que es un cliente HTTP que es eficiente por defecto:

  • El soporte HTTP / 2 permite que todas las solicitudes al mismo host compartan un socket.
  • La agrupación de conexiones reduce la latencia de la solicitud (si HTTP / 2 no está disponible).
  • GZIP transparente reduce los tamaños de descarga.
  • El almacenamiento en caché de respuestas evita la red por completo para solicitudes repetidas.

Primero cree una instancia de OkHttpClient:

OkHttpClient client = new OkHttpClient();

Luego, prepare su GETsolicitud:

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

finalmente, use OkHttpClientpara enviar preparado Request:

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

Para más detalles, puede consultar la documentación de OkHttp

Ali Dehghani
fuente
14

También puede usar JdkRequestdesde jcabi-http (Soy un desarrollador), que hace todo este trabajo por usted, decorando HttpURLConnection, disparando solicitudes HTTP y analizando respuestas, por ejemplo:

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

Consulte esta publicación de blog para obtener más información: http://www.yegor256.com/2014/04/11/jcabi-http-intro.html

yegor256
fuente
1
¿Cómo manejas las cookies?
Dejell
12

si está utilizando http get, elimine esta línea

urlConnection.setDoOutput(true);
Nison Cheruvathur
fuente