Расширенная настройка HttpClient

Расширенная настройка HttpClient

1. обзор

В этой статье мы рассмотрим расширенное использование библиотеки ApacheHttpClient.

Мы рассмотрим примеры добавления пользовательских заголовков к HTTP-запросам и увидим, как настроить клиент для авторизации и отправки запросов через прокси-сервер.

Мы будем использовать Wiremock для заглушки HTTP-сервера. Если вы хотите узнать больше о Wiremock, посмотритеthis article.

2. HTTP-запрос с настраиваемым заголовкомUser-Agent

Допустим, мы хотим добавить настраиваемый заголовокUser-Agent в HTTP-запрос GET. ЗаголовокUser-Agent содержит характеристическую строку, которая позволяет одноранговым узлам сетевого протокола определять тип приложения, операционную систему и поставщика программного обеспечения или версию программного обеспечения запрашивающего программного обеспечения пользовательского агента.

Прежде чем мы начнем писать наш HTTP-клиент, нам нужно запустить наш встроенный макет-сервер:

@Rule
public WireMockRule serviceMock = new WireMockRule(8089);

Когда мы создаем экземплярHttpGet, мы можем просто использовать методsetHeader() для передачи имени нашего заголовка вместе со значением. Этот заголовок будет добавлен к 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);

Мы добавляем заголовокUser-Agent и отправляем этот запрос с помощью методаexecute().

Когда GET-запрос отправляется для URL/detail с заголовкомUser-Agent, значение которого равно «exampleAgent / 1.0», тоserviceMock вернет код ответа HTTP 200:

serviceMock.stubFor(get(urlEqualTo("/detail"))
  .withHeader("User-Agent", equalTo(userAgent))
  .willReturn(aResponse().withStatus(200)));

3. Отправка данных в теле запроса POST

Обычно, когда мы выполняем метод HTTP POST, мы хотим передать сущность в качестве тела запроса. При создании экземпляра объектаHttpPost мы можем добавить тело к этому запросу, используя методsetEntity():

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

Мы создаем экземплярStringEntity с телом в форматеXML. Важно установить заголовокContent-Type на «application/xml», чтобы передавать на сервер информацию о типе отправляемого контента. КогдаserviceMock получает запрос POST с телом XML, он отвечает кодом состояния 200 OK:

serviceMock.stubFor(post(urlEqualTo("/person"))
  .withHeader("Content-Type", equalTo("application/xml"))
  .withRequestBody(equalTo(xmlBody))
  .willReturn(aResponse().withStatus(200)));

4. Отправка запросов через прокси-сервер

Часто наш веб-сервис может бытьbehind a proxy server, который выполняет некоторую дополнительную логику, кэширует статические ресурсы и т. Д. Когда мы создаем HTTP-клиент и отправляем запрос реальной службе, мы не хотим иметь дело с этим для каждого HTTP-запроса.

Чтобы протестировать этот сценарий, нам потребуется запустить еще один встроенный веб-сервер:

@Rule
public WireMockRule proxyMock = new WireMockRule(8090);

С двумя встроенными серверами первая фактическая служба находится на порте 8089, а прокси-сервер прослушивает порт 8090.

Мы настраиваем нашHttpClient для отправки всех запросов через прокси, создаваяDefaultProxyRoutePlanner, который принимает прокси экземпляраHttpHost в качестве аргумента:

HttpHost proxy = new HttpHost("localhost", 8090);
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
HttpClient httpclient = HttpClients.custom()
  .setRoutePlanner(routePlanner)
  .build();

Наш прокси-сервер перенаправляет все запросы фактической службе, которая прослушивает порт 8090. В конце теста мы проверяем, что запрос был отправлен нашему фактическому сервису через прокси:

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. Настройка HTTP-клиента для авторизации через прокси

Расширяя предыдущий пример, в некоторых случаях прокси-сервер используется для выполнения авторизации. В такой конфигурации прокси может авторизовать все запросы и передавать их на сервер, который скрыт за прокси.

Мы можем настроить HttpClient для отправки каждого запроса через прокси вместе с заголовкомAuthorization, который будет использоваться для выполнения процесса авторизации.

Предположим, у нас есть прокси-сервер, который авторизует только одного пользователя - «username_admin», с паролем «secret_password».

Нам нужно создать экземплярBasicCredentialsProvider с учетными данными пользователя, который будет авторизован через прокси. ЧтобыHttpClient автоматически добавлял заголовокAuthorization с правильным значением, нам нужно создатьHttpClientContext с предоставленными учетными данными иBasicAuthCache, в котором хранятся учетные данные:

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

Когда мы настраиваемHttpClient,, отправка запросов к нашей службе приведет к отправке запроса через прокси с заголовкомAuthorization для выполнения процесса авторизации. Он будет установлен в каждом запросе автоматически.

Выполним реальный запрос к сервису:

HttpGet httpGet = new HttpGet("http://localhost:8089/private");
HttpResponse response = httpclient.execute(httpGet, context);

Проверка методаexecute() наhttpClient с нашей конфигурацией подтверждает, что запрос прошел через прокси с заголовкомAuthorization:

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. Заключение

В этой статье показано, как настроить ApacheHttpClientдля выполнения расширенных HTTP-вызовов. Мы увидели, как отправлять запросы через прокси-сервер и как авторизоваться через прокси.

Реализация всех этих примеров и фрагментов кода можно найти вGitHub project - это проект Maven, поэтому его должно быть легко импортировать и запускать как есть.