Primeiro um aviso prévio: os trechos de código publicados são todos exemplos básicos. Você vai precisar para lidar com triviais IOException
s e RuntimeException
s como NullPointerException
, ArrayIndexOutOfBoundsException
e 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¶m2=%s",
URLEncoder.encode(param1, charset),
URLEncoder.encode(param2, charset));
Os parâmetros da consulta devem estar no name=value
formato 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-Charset
cabeç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-Charset
cabeç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 true
define 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-urlencoded
em 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=value
pares de qualquer <input type="hidden">
elemento para a string de consulta e, claro, também o name=value
par 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 URLConnection
para HttpURLConnection
e 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
Status da resposta HTTP :
Você precisa de um HttpURLConnection
aqui. Elenco primeiro, se necessário.
int status = httpConnection.getResponseCode();
Cabeçalhos de resposta HTTP :
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
System.out.println(header.getKey() + "=" + header.getValue());
}
Codificação de resposta HTTP :
Quando o Content-Type
contém um charset
parâ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 CookieHandler
API para manter os cookies. Você precisa preparar um CookieManager
com um CookiePolicy
de ACCEPT_ALL
antes 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-Cookie
cabeçalhos da resposta do login ou da primeira GET
solicitaçã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 HttpURLConnection
padrã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 OutOfMemoryException
s 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-Encoding
cabeçalho HTTP para o chunked
qual 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-Agent
cabeçalho da solicitação. Por URLConnection
padrão, o irá configurá-lo para Java/1.6.0_19
onde 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 HttpURLConnection
implementação está em JREs mais antigos, de certa forma com problemas para manter as conexões ativas. Você pode desativá-lo configurando a http.keepAlive
propriedade 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-data
codificaçã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-data
solicitaçã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 certificate
em 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] found
ou javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
em alguns sites mal configurado HTTPS.
O static
inicializador de execução única a seguir na sua classe de raspador da web deve ser HttpsURLConnection
mais 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
Ask Question
botão no canto superior direito.--
peça não faz parte do próprio limite. É apenas uma sequência separadora. Reverti sua edição inválida.HttpClient
agora eHttpURLConnection
é cruel. android-developers.blogspot.in/2011/09/...Ao trabalhar com HTTP, é quase sempre mais útil consultar a
HttpURLConnection
classe base do que a classe baseURLConnection
(já queURLConnection
é uma classe abstrata quando você solicitaURLConnection.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 ohttpURLConnection.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:
fonte
setDoOutput()
para,true
caso contrário, uma exceção será lançada (mesmo que vocêsetRequestMethod("POST")
). Para ficar claro: a configuraçãoURLConnection#setDoOutput(true)
paratrue
define implicitamente o método de solicitação como POST, mas a configuraçãohttpURLConnection.setRequestMethod("POST")
para POST não é definida implicitamentesetDoOutput()
comotrue
.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.
fonte
Sugiro que você dê uma olhada no código no kevinsawicki / http-request , que é basicamente um invólucro
HttpUrlConnection
que 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
GET
solicitação com o tipo de conteúdoapplication/json
e alguns parâmetros de consulta:fonte
Existem 2 opções que você pode acessar com os URLs HTTP: GET / POST
Solicitação GET: -
Pedido POST: -
fonte
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):
Depois, existem apenas vários métodos estáticos.
Depois poste ...
Bem, você entendeu a idéia ....
Aqui estão os testes:
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 ....
fonte
doPost
método exista umcharset
parâmetro, usado para definir o cabeçalho da solicitação, mas os dados sejam gravados com algum conjunto de caracteres codificadoIO.CHARSET
. Um inseto?Atualizar
No Java 9, você pode enviar uma
GET
solicitação como:Então você pode examinar o retornado
HttpResponse
:Como esse novo cliente HTTP está em
java.httpclient
jdk.incubator.httpclient
módulo, você deve declarar esta dependência no seumodule-info.java
arquivo:fonte
Inicialmente, fui enganado por este artigo que favorece
HttpClient
.Mais tarde eu percebi que
HttpURLConnection
vai ficar neste artigoDe acordo com o blog do Google :
Depois de ler este artigo e algumas outras perguntas sobre o fluxo excedente, estou convencido de que
HttpURLConnection
permanecerá 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
fonte
Há também o OkHttp , que é um cliente HTTP eficiente por padrão:
Primeiro, crie uma instância de
OkHttpClient
:Em seguida, prepare sua
GET
solicitação:finalmente, use
OkHttpClient
para enviar preparadoRequest
:Para mais detalhes, você pode consultar a documentação do OkHttp
fonte
Você também pode usar
JdkRequest
a 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:Verifique esta publicação no blog para obter mais informações: http://www.yegor256.com/2014/04/11/jcabi-http-intro.html
fonte
se você estiver usando o http get por favor remova esta linha
fonte