Configuração avançada do HttpClient
1. Visão geral
Neste artigo, veremos o uso avançado da biblioteca ApacheHttpClient.
Veremos os exemplos de adição de cabeçalhos personalizados a solicitações HTTP e veremos como configurar o cliente para autorizar e enviar solicitações por meio de um servidor proxy.
Usaremos o Wiremock para remover o servidor HTTP. Se você quiser ler mais sobre a Wiremock, verifiquethis article.
2. Solicitação HTTP com um cabeçalhoUser-Agent personalizado
Digamos que queremos adicionar um cabeçalhoUser-Agent personalizado a uma solicitação HTTP GET. O cabeçalhoUser-Agent contém uma string de característica que permite aos pares do protocolo de rede identificar o tipo de aplicativo, sistema operacional e fornecedor de software ou versão de software do agente de usuário de software solicitante.
Antes de começarmos a escrever nosso cliente HTTP, precisamos iniciar nosso servidor simulado incorporado:
@Rule
public WireMockRule serviceMock = new WireMockRule(8089);
Quando estamos criando uma instânciaHttpGet, podemos simplesmente usar um métodosetHeader() para passar o nome do nosso cabeçalho junto com o valor. Esse cabeçalho será adicionado a uma solicitação HTTP:
String userAgent = "exampleAgent/1.0";
HttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet("http://localhost:8089/detail");
httpGet.setHeader(HttpHeaders.USER_AGENT, userAgent);
HttpResponse response = httpClient.execute(httpGet);
assertEquals(response.getStatusLine().getStatusCode(), 200);
Estamos adicionando um cabeçalhoUser-Agent e enviando essa solicitação por meio de um métodoexecute().
Quando a solicitação GET é enviada para um URL/detail com cabeçalhoUser-Agent que tem um valor igual a “exampleAgent / 1.0” entãoserviceMock retornará 200 código de resposta HTTP:
serviceMock.stubFor(get(urlEqualTo("/detail"))
.withHeader("User-Agent", equalTo(userAgent))
.willReturn(aResponse().withStatus(200)));
3. Enviando dados no corpo de solicitação POST
Normalmente, quando estamos executando o método HTTP POST, queremos passar uma entidade como um corpo de solicitação. Ao criar uma instância de um objetoHttpPost, podemos adicionar o corpo a essa solicitação usando um métodosetEntity():
String xmlBody = "1 ";
HttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost("http://localhost:8089/person");
httpPost.setHeader("Content-Type", "application/xml");
StringEntity xmlEntity = new StringEntity(xmlBody);
httpPost.setEntity(xmlEntity);
HttpResponse response = httpClient.execute(httpPost);
assertEquals(response.getStatusLine().getStatusCode(), 200);
Estamos criando uma instânciaStringEntity com um corpo que está no formatoXML. É importante definir o cabeçalhoContent-Type como “application/xml” para passar informações ao servidor sobre o tipo de conteúdo que estamos enviando. Quando oserviceMock recebe a solicitação POST com corpo XML, ele responde com o código de status 200 OK:
serviceMock.stubFor(post(urlEqualTo("/person"))
.withHeader("Content-Type", equalTo("application/xml"))
.withRequestBody(equalTo(xmlBody))
.willReturn(aResponse().withStatus(200)));
4. Envio de solicitações por meio de um servidor proxy
Freqüentemente, nosso serviço da web pode serbehind a proxy server que executa alguma lógica adicional, armazena recursos estáticos em cache, etc. Quando estamos criando o cliente HTTP e enviando uma solicitação para um serviço real, não queremos lidar com isso em cada solicitação HTTP.
Para testar este cenário, precisaremos iniciar outro servidor da web incorporado:
@Rule
public WireMockRule proxyMock = new WireMockRule(8090);
Com dois servidores incorporados, o primeiro serviço real está na porta 8089 e um servidor proxy está escutando na porta 8090.
Estamos configurando nossoHttpClient para enviar todas as solicitações via proxy, criando umDefaultProxyRoutePlanner que usa o proxy de instânciaHttpHost como argumento:
HttpHost proxy = new HttpHost("localhost", 8090);
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
HttpClient httpclient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.build();
Nosso servidor proxy está redirecionando todas as solicitações para o serviço real que escuta na porta 8090. No final do teste, verificamos que a solicitação foi enviada ao nosso serviço real por meio de um proxy:
proxyMock.stubFor(get(urlMatching(".*"))
.willReturn(aResponse().proxiedFrom("http://localhost:8089/")));
serviceMock.stubFor(get(urlEqualTo("/private"))
.willReturn(aResponse().withStatus(200)));
assertEquals(response.getStatusLine().getStatusCode(), 200);
proxyMock.verify(getRequestedFor(urlEqualTo("/private")));
serviceMock.verify(getRequestedFor(urlEqualTo("/private")));
5. Configurando o Cliente HTTP para Autorizar por Proxy
Estendendo o exemplo anterior, há alguns casos em que o servidor proxy é usado para executar a autorização. Nessa configuração, um proxy pode autorizar todas as solicitações e passá-las para o servidor oculto atrás de um proxy.
Podemos configurar o HttpClient para enviar cada requisição via proxy, junto com o cabeçalhoAuthorization que será usado para realizar um processo de autorização.
Suponha que tenhamos um servidor proxy que autoriza apenas um usuário - “username_admin“, com uma senha “secret_password“.
Precisamos criar a instânciaBasicCredentialsProvider com credenciais do usuário que será autorizado via proxy. Para fazerHttpClient adicionar automaticamente o cabeçalhoAuthorization com o valor adequado, precisamos criar umHttpClientContext com as credenciais fornecidas e umBasicAuthCache que armazena credenciais:
HttpHost proxy = new HttpHost("localhost", 8090);
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
//Client credentials
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(proxy),
new UsernamePasswordCredentials("username_admin", "secret_password"));
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
BasicScheme basicAuth = new BasicScheme();
authCache.put(proxy, basicAuth);
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
context.setAuthCache(authCache);
HttpClient httpclient = HttpClients.custom()
.setRoutePlanner(routePlanner)
.setDefaultCredentialsProvider(credentialsProvider)
.build();
Quando configuramos nossoHttpClient,, fazer solicitações ao nosso serviço resultará no envio de uma solicitação via proxy com um cabeçalhoAuthorization para realizar o processo de autorização. Ele será definido em cada solicitação automaticamente.
Vamos executar uma solicitação real para o serviço:
HttpGet httpGet = new HttpGet("http://localhost:8089/private");
HttpResponse response = httpclient.execute(httpGet, context);
A verificação de um métodoexecute() emhttpClient com nossa configuração confirma que uma solicitação passou por um proxy com um cabeçalhoAuthorization:
proxyMock.stubFor(get(urlMatching("/private"))
.willReturn(aResponse().proxiedFrom("http://localhost:8089/")));
serviceMock.stubFor(get(urlEqualTo("/private"))
.willReturn(aResponse().withStatus(200)));
assertEquals(response.getStatusLine().getStatusCode(), 200);
proxyMock.verify(getRequestedFor(urlEqualTo("/private"))
.withHeader("Authorization", containing("Basic")));
serviceMock.verify(getRequestedFor(urlEqualTo("/private")));
6. Conclusão
Este artigo mostra como configurar o ApacheHttpClient para realizar chamadas HTTP avançadas. Vimos como enviar solicitações por meio de um servidor proxy e como autorizar via proxy.
A implementação de todos esses exemplos e trechos de código pode ser encontrada emGitHub project - este é um projeto Maven, portanto, deve ser fácil de importar e executar como está.