Como usar o java.net.URLConnection para disparar e manipular solicitações HTTP?

1948

O uso de java.net.URLConnectioné frequentemente solicitado aqui, e o tutorial do Oracle é muito conciso.

Esse tutorial basicamente mostra apenas como acionar uma solicitação GET e ler a resposta. Ele não explica em nenhum lugar como usá-lo para, entre outros, executar uma solicitação POST, definir cabeçalhos de solicitação, ler cabeçalhos de resposta, lidar com cookies, enviar um formulário HTML, enviar um arquivo, etc.

Então, como posso usar java.net.URLConnectionpara disparar e manipular solicitações HTTP "avançadas"?

BalusC
fonte

Respostas:

2711

Primeiro um aviso prévio: os trechos de código publicados são todos exemplos básicos. Você vai precisar para lidar com triviais IOExceptions e RuntimeExceptions como NullPointerException, ArrayIndexOutOfBoundsExceptione consortes si mesmo.


Preparando

Primeiro precisamos saber pelo menos o URL e o conjunto de caracteres. Os parâmetros são opcionais e dependem dos requisitos funcionais.

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

Os parâmetros da consulta devem estar no name=valueformato e ser concatenados por &. Normalmente, você também codifica por URL os parâmetros de consulta com o conjunto de caracteres especificado usando URLEncoder#encode().

O String#format()é apenas por conveniência. Eu prefiro quando preciso do operador de concatenação String +mais de duas vezes.


Disparando uma solicitação HTTP GET com (opcionalmente) parâmetros de consulta

É uma tarefa trivial. É o método de solicitação padrão.

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

Qualquer string de consulta deve ser concatenada para a URL usando ?. O Accept-Charsetcabeçalho pode sugerir ao servidor em que codificação estão os parâmetros. Se você não enviar nenhuma string de consulta, poderá deixar o Accept-Charsetcabeçalho ausente. Se você não precisar definir nenhum cabeçalho, poderá usar o URL#openStream()método de atalho.

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

De qualquer maneira, se o outro lado for a HttpServlet, seu doGet()método será chamado e os parâmetros estarão disponíveis por HttpServletRequest#getParameter().

Para fins de teste, você pode imprimir o corpo da resposta no stdout como abaixo:

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

Disparando uma solicitação HTTP POST com parâmetros de consulta

Definir URLConnection#setDoOutput()como truedefine implicitamente o método de solicitação como POST. O HTTP POST padrão, como fazem os formulários da Web, é do tipo application/x-www-form-urlencodedem que a cadeia de consulta é gravada no corpo da solicitação.

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: sempre que você desejar enviar um formulário HTML programaticamente, não esqueça de levar os name=valuepares de qualquer <input type="hidden">elemento para a string de consulta e, claro, também o name=valuepar do <input type="submit">elemento que você deseja "pressionar" programaticamente (porque isso geralmente é usado no lado do servidor para distinguir se um botão foi pressionado e, em caso afirmativo, qual).

Você também pode lançar o obtido URLConnectionpara HttpURLConnectione usar a sua HttpURLConnection#setRequestMethod()vez. Mas se você está tentando usar a conexão para a saída você ainda precisa definir URLConnection#setDoOutput()a true.

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

De qualquer maneira, se o outro lado for a HttpServlet, seu doPost()método será chamado e os parâmetros estarão disponíveis por HttpServletRequest#getParameter().


Realmente disparando a solicitação HTTP

Você pode disparar a solicitação HTTP explicitamente com URLConnection#connect(), mas a solicitação será disparada automaticamente sob demanda quando você desejar obter informações sobre a resposta HTTP, como o corpo de resposta usando URLConnection#getInputStream()etc. Os exemplos acima fazem exatamente isso; portanto, a connect()chamada é de fato supérflua.


Reunindo informações de resposta HTTP

  1. Status da resposta HTTP :

    Você precisa de um HttpURLConnectionaqui. Elenco primeiro, se necessário.

    int status = httpConnection.getResponseCode();
  2. Cabeçalhos de resposta HTTP :

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

    Quando o Content-Typecontém um charsetparâmetro, o corpo da resposta provavelmente é baseado em texto e, em seguida, gostaríamos de processar o corpo da resposta com a codificação de caracteres especificada no 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.
    }
    

Manutenção da sessão

A sessão do servidor geralmente é apoiada por um cookie. Alguns formulários da Web exigem que você esteja logado e / ou seja rastreado por uma sessão. Você pode usar a CookieHandlerAPI para manter os cookies. Você precisa preparar um CookieManagercom um CookiePolicyde ACCEPT_ALLantes de enviar todos os pedidos 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();
// ...

Observe que isso nem sempre funciona corretamente em todas as circunstâncias. Se isso falhar, o melhor é reunir e definir manualmente os cabeçalhos dos cookies. Basicamente, você precisa pegar todos os Set-Cookiecabeçalhos da resposta do login ou da primeira GETsolicitação e depois passar isso pelas solicitações subsequentes.

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

O split(";", 2)[0]está lá para se livrar de atributos de cookie que são irrelevantes para o lado do servidor, como expires, path, etc. Alternativamente, você também pode usar cookie.substring(0, cookie.indexOf(';'))em vez de split().


Modo de transmissão

Por HttpURLConnectionpadrão, o buffer será armazenado em todo o corpo da solicitação antes de enviá-lo, independentemente de você ter definido um tamanho fixo de conteúdo connection.setRequestProperty("Content-Length", contentLength);. Isso pode causar OutOfMemoryExceptions sempre que você envia simultaneamente grandes solicitações POST (por exemplo, upload de arquivos). Para evitar isso, você gostaria de definir o HttpURLConnection#setFixedLengthStreamingMode().

httpConnection.setFixedLengthStreamingMode(contentLength);

Mas se o tamanho do conteúdo não for realmente conhecido de antemão, você poderá usar o modo de streaming em pedaços, configurando-o de HttpURLConnection#setChunkedStreamingMode()acordo. Isso definirá o Transfer-Encodingcabeçalho HTTP para o chunkedqual forçará o envio do corpo da solicitação em pedaços. O exemplo abaixo enviará o corpo em pedaços de 1 KB.

httpConnection.setChunkedStreamingMode(1024);

Agente de usuário

Pode acontecer que uma solicitação retorne uma resposta inesperada, enquanto funciona bem com um navegador da Web real . O lado do servidor provavelmente está bloqueando solicitações com base no User-Agentcabeçalho da solicitação. Por URLConnectionpadrão, o irá configurá-lo para Java/1.6.0_19onde a última parte é obviamente a versão do JRE. Você pode substituir isso da seguinte maneira:

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 a sequência User-Agent de um navegador recente .


Manipulação de erros

Se o código de resposta HTTP for 4nn(Erro do cliente) ou 5nn(Erro do servidor), convém ler o HttpURLConnection#getErrorStream()para ver se o servidor enviou alguma informação útil sobre o erro.

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

Se o código de resposta HTTP for -1, algo deu errado com a manipulação de conexões e respostas. A HttpURLConnectionimplementação está em JREs mais antigos, de certa forma com problemas para manter as conexões ativas. Você pode desativá-lo configurando a http.keepAlivepropriedade do sistema para false. Você pode fazer isso programaticamente no início de seu aplicativo:

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

Upload de arquivos

Você usaria normalmente a multipart/form-datacodificação para conteúdo POST misto (dados binários e de caracteres). A codificação é descrita em mais detalhes na 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 o outro lado for a HttpServlet, seu doPost()método será chamado e as partes estarão disponíveis por HttpServletRequest#getPart()(observe, portanto não getParameter() e assim por diante!). O getPart()método, no entanto, é relativamente novo, foi introduzido no Servlet 3.0 (Glassfish 3, Tomcat 7, etc.). Antes do Servlet 3.0, sua melhor opção era usar o Apache Commons FileUpload para analisar uma multipart/form-datasolicitação. Consulte também esta resposta para obter exemplos das abordagens FileUpload e Servelt 3.0.


Lidando com sites HTTPS não confiáveis ​​ou mal configurados

Às vezes, você precisa conectar um URL HTTPS, talvez porque esteja escrevendo um raspador da Web. Nesse caso, você pode provavelmente enfrentar uma javax.net.ssl.SSLException: Not trusted server certificateem alguns sites HTTPS que não mantêm seus certificados SSL até à data, ou um java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] foundou javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_nameem alguns sites mal configurado HTTPS.

O staticinicializador de execução única a seguir na sua classe de raspador da web deve ser HttpsURLConnectionmais tolerante com esses sites HTTPS e, portanto, não lançar mais essas exceções.

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

Últimas palavras

O Apache HttpComponents HttpClient é muito mais conveniente em tudo isso :)


Analisando e Extraindo HTML

Se tudo o que você deseja é analisar e extrair dados do HTML, use melhor um analisador de HTML como o Jsoup

BalusC
fonte
119
Você deve colocar o link apache em primeiro lugar, para que as pessoas que procuram uma solução encontrá-lo mais rápido;)
ZeissS
40
@ivanceras: Se você não pode resumir tudo com base nas informações desta resposta, pressione o Ask Questionbotão no canto superior direito.
precisa saber é o seguinte
3
@Brais: Por favor, leia as especificações. A --peça não faz parte do próprio limite. É apenas uma sequência separadora. Reverti sua edição inválida.
BalusC
7
O @BalusC agradece muito por um tutorial tão perfeito. Inclua também um cabeçalho como "Fechando fluxos / conexões". Estou realmente confuso sobre quando e quais fluxos / conexões fechar.
10
A parte triste é que o Android é não recomendado o uso do Apache HttpClientagora e HttpURLConnectioné cruel. android-developers.blogspot.in/2011/09/...
yati sagade
91

Ao trabalhar com HTTP, é quase sempre mais útil consultar a HttpURLConnectionclasse base do que a classe base URLConnection(já que URLConnectioné uma classe abstrata quando você solicita URLConnection.openConnection()uma URL HTTP que é o que você receberá de qualquer maneira).

Em vez disso, você pode URLConnection#setDoOutput(true)configurar implicitamente o método de solicitação para POST, em vez disso, fazer o httpURLConnection.setRequestMethod("POST")que alguns podem achar mais natural (e também permitir que você especifique outros métodos de solicitação, como PUT , DELETE , ...).

Ele também fornece constantes HTTP úteis para que você possa fazer:

int responseCode = httpURLConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {
Paal Thorstensen
fonte
1
setDoOutPut true foi meu problema que definiu meu GET como POST. Obrigado
Patrick Kafka
22
Se você estiver tentando gravar dados no fluxo de saída, ainda assim deverá ser configurado setDoOutput()para, truecaso contrário, uma exceção será lançada (mesmo que você setRequestMethod("POST")). Para ficar claro: a configuração URLConnection#setDoOutput(true)para truedefine implicitamente o método de solicitação como POST, mas a configuração httpURLConnection.setRequestMethod("POST")para POST não é definida implicitamente setDoOutput()como true.
Tony Chan
54

Inspirado por essa e outras perguntas sobre SO, criei um cliente-básico básico http mínimo que incorpora a maioria das técnicas encontradas aqui.

O google-http-java-client também é um ótimo recurso de código aberto.

David Chandler
fonte
Eu estava pensando o mesmo. Mas também pode ser bom ter uma biblioteca Java simples / barebones / que use apenas o código URLConnection como apresentado aqui, que encapsula o código para métodos mais simples de executar HTTP GET, POST etc. A biblioteca pode ser compilada e compactada como JAR e importados / usados ​​no código Java ou no arquivo de classe de origem podem ser incluídos no projeto Java se JARs externos não forem desejados. Isso pode ser feito com outras bibliotecas como o Apache, etc., mas é mais trabalhoso comparado a uma biblioteca de classe de arquivo 1 simples usando URLConnection.
David
rapidvaluesolutions.com/tech_blog/… favorece HttpURLConnection sobre HttpClient
Ravindra babu
24

Sugiro que você dê uma olhada no código no kevinsawicki / http-request , que é basicamente um invólucro HttpUrlConnectionque fornece uma API muito mais simples, caso você queira apenas fazer as solicitações agora ou dar uma olhada nas fontes ( não é muito grande) para ver como as conexões são tratadas.

Exemplo: faça uma GETsolicitação com o tipo de conteúdo application/jsone alguns 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
fonte
24

Existem 2 opções que você pode acessar com os URLs HTTP: GET / POST

Solicitação GET: -

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

String url = "https://name_of_the_url";
URL request_url = new URL(url);
HttpURLConnection http_conn = (HttpURLConnection)request_url.openConnection();
http_conn.setConnectTimeout(100000);
http_conn.setReadTimeout(100000);
http_conn.setInstanceFollowRedirects(true);
System.out.println(String.valueOf(http_conn.getResponseCode()));

Pedido POST: -

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

String url = "https://name_of_the_url"
URL request_url = new URL(url);
HttpURLConnection http_conn = (HttpURLConnection)request_url.openConnection();
http_conn.setConnectTimeout(100000);
http_conn.setReadTimeout(100000);
http_conn.setInstanceFollowRedirects(true);
http_conn.setDoOutput(true);
PrintWriter out = new PrintWriter(http_conn.getOutputStream());
if (urlparameter != null) {
   out.println(urlparameter);
}
out.close();
out = null;
System.out.println(String.valueOf(http_conn.getResponseCode()));
Utkash Bhatt
fonte
3
Como você pode visualizar a resposta JSON real?
Sora
21

Também fiquei muito inspirado por essa resposta.

Frequentemente, estou em projetos nos quais preciso fazer HTTP e talvez não queira incluir muitas dependências de terceiros (que trazem outras e assim por diante etc.)

Comecei a escrever meus próprios utilitários com base em algumas dessas conversas (e nenhuma delas foi feita):

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 {

Depois, existem apenas vários 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;
    }
}

Depois poste ...

public static String postBody(
        final String url,
        final String body) {
    URLConnection connection;
    try {
        connection = doPost(url, null, "text/plain", null, body);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}

public static String postBodyWithHeaders(
        final String url,
        final Map<String, ? extends Object> headers,
        final String body) {
    URLConnection connection;
    try {
        connection = doPost(url, headers, "text/plain", null, body);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}



public static String postBodyWithContentType(
        final String url,
        final Map<String, ? extends Object> headers,
        final String contentType,
        final String body) {

    URLConnection connection;
    try {
        connection = doPost(url, headers, contentType, null, body);


        return extractResponseString(connection);


    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }


}


public static String postBodyWithCharset(
        final String url,
        final Map<String, ? extends Object> headers,
        final String contentType,
        final String charSet,
        final String body) {

    URLConnection connection;
    try {
        connection = doPost(url, headers, contentType, charSet, body);


        return extractResponseString(connection);


    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }


}

private static URLConnection doPost(String url, Map<String, ? extends Object> headers,
                                    String contentType, String charset, String body
                                    ) throws IOException {
    URLConnection connection;/* Handle output. */
    connection = new URL(url).openConnection();
    connection.setDoOutput(true);
    manageContentTypeHeaders(contentType, charset, connection);

    manageHeaders(headers, connection);


    IO.write(connection.getOutputStream(), body, IO.CHARSET);
    return connection;
}

private static void manageHeaders(Map<String, ? extends Object> headers, URLConnection connection) {
    if (headers != null) {
        for (Map.Entry<String, ? extends Object> entry : headers.entrySet()) {
            connection.setRequestProperty(entry.getKey(), entry.getValue().toString());
        }
    }
}

private static void manageContentTypeHeaders(String contentType, String charset, URLConnection connection) {
    connection.setRequestProperty("Accept-Charset", charset == null ? IO.CHARSET : charset);
    if (contentType!=null && !contentType.isEmpty()) {
        connection.setRequestProperty("Content-Type", contentType);
    }
}

private static URLConnection doGet(String url, Map<String, ? extends Object> headers,
                                    String contentType, String charset) throws IOException {
    URLConnection connection;/* Handle output. */
    connection = new URL(url).openConnection();
    manageContentTypeHeaders(contentType, charset, connection);

    manageHeaders(headers, connection);

    return connection;
}

private static String extractResponseString(URLConnection connection) throws IOException {
/* Handle input. */
    HttpURLConnection http = (HttpURLConnection)connection;
    int status = http.getResponseCode();
    String charset = getCharset(connection.getHeaderField("Content-Type"));

    if (status==200) {
        return readResponseBody(http, charset);
    } else {
        return readErrorResponseBody(http, status, charset);
    }
}

private static String readErrorResponseBody(HttpURLConnection http, int status, String charset) {
    InputStream errorStream = http.getErrorStream();
    if ( errorStream!=null ) {
        String error = charset== null ? read( errorStream ) :
            read( errorStream, charset );
        throw new RuntimeException("STATUS CODE =" + status + "\n\n" + error);
    } else {
        throw new RuntimeException("STATUS CODE =" + status);
    }
}

private static String readResponseBody(HttpURLConnection http, String charset) throws IOException {
    if (charset != null) {
        return read(http.getInputStream(), charset);
    } else {
        return read(http.getInputStream());
    }
}

private static String getCharset(String contentType) {
    if (contentType==null)  {
        return null;
    }
    String charset = null;
    for (String param : contentType.replace(" ", "").split(";")) {
        if (param.startsWith("charset=")) {
            charset = param.split("=", 2)[1];
            break;
        }
    }
    charset = charset == null ?  IO.CHARSET : charset;

    return charset;
}

Bem, você entendeu a idéia ....

Aqui estão os testes:

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


}

Você pode encontrar o resto aqui:

https://github.com/RichardHightower/boon

Meu objetivo é fornecer as coisas comuns que alguém gostaria de fazer de uma maneira um pouco mais fácil do que ....

RickHigh
fonte
2
É estranho que no doPostmétodo exista um charsetparâmetro, usado para definir o cabeçalho da solicitação, mas os dados sejam gravados com algum conjunto de caracteres codificado IO.CHARSET. Um inseto?
Vit Khudenko
21

Atualizar

O novo cliente HTTP foi enviado com o Java 9, mas como parte de um módulo Incubator chamado jdk.incubator.httpclient. Os módulos da incubadora são um meio de colocar APIs não finais nas mãos dos desenvolvedores, enquanto as APIs avançam em direção à finalização ou remoção em uma versão futura.

No Java 9, você pode enviar uma GETsolicitação como:

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

Então você pode examinar o retornado HttpResponse:

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

Como esse novo cliente HTTP está em java.httpclient jdk.incubator.httpclientmódulo, você deve declarar esta dependência no seu module-info.javaarquivo:

module com.foo.bar {
    requires jdk.incubator.httpclient;
}
Ali Dehghani
fonte
1
Atualização adicional: o módulo está fora do status de incubação. Agora é java.net.http , não jdk.incubator.httpclient.
VGR
17

Inicialmente, fui enganado por este artigo que favorece HttpClient.

Mais tarde eu percebi que HttpURLConnectionvai ficar neste artigo

De acordo com o blog do Google :

O cliente HTTP Apache possui menos erros no Eclair e no Froyo. É a melhor escolha para esses lançamentos. Para Gingerbread, HttpURLConnection é a melhor escolha. Sua API simples e tamanho pequeno o tornam ideal para o Android.

A compactação transparente e o cache de resposta reduzem o uso da rede, melhoram a velocidade e economizam bateria. Novos aplicativos devem usar HttpURLConnection; é onde gastaremos nossa energia daqui para frente.

Depois de ler este artigo e algumas outras perguntas sobre o fluxo excedente, estou convencido de que HttpURLConnectionpermanecerá por períodos mais longos.

Algumas das questões do SE são favoráveis HttpURLConnections:

No Android, faça uma solicitação POST com dados do formulário codificado por URL sem usar UrlEncodedFormEntity

O HttpPost funciona no projeto Java, não no Android

Ravindra babu
fonte
15

Há também o OkHttp , que é um cliente HTTP eficiente por padrão:

  • O suporte a HTTP / 2 permite que todas as solicitações ao mesmo host compartilhem um soquete.
  • O pool de conexões reduz a latência da solicitação (se HTTP / 2 não estiver disponível).
  • O GZIP transparente reduz os tamanhos de download.
  • O cache de resposta evita a rede completamente para solicitações repetidas.

Primeiro, crie uma instância de OkHttpClient:

OkHttpClient client = new OkHttpClient();

Em seguida, prepare sua GETsolicitação:

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

finalmente, use OkHttpClientpara enviar preparado Request:

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

Para mais detalhes, você pode consultar a documentação do OkHttp

Ali Dehghani
fonte
14

Você também pode usar JdkRequesta partir jcabi-http (Eu sou um desenvolvedor), o que faz todo este trabalho para você, decorar HttpURLConnection, disparando solicitações HTTP e analisar respostas, por exemplo:

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

Verifique esta publicação no blog para obter mais informações: http://www.yegor256.com/2014/04/11/jcabi-http-intro.html

yegor256
fonte
1
Como você lida com cookies?
Dejell
12

se você estiver usando o http get por favor remova esta linha

urlConnection.setDoOutput(true);
Nison Cheruvathur
fonte