1. Обзор
В этой статье мы рассмотрим расширенное использование библиотеки Apache HttpClient
.
Мы рассмотрим примеры добавления пользовательских заголовков к HTTP-запросам и увидим, как настроить клиент для авторизации и отправки запросов через прокси-сервер.
Мы будем использовать Wiremock для заглушки HTTP-сервера. Если вы хотите узнать больше о Wiremock, ознакомьтесь с этой статьей .
2. HTTP-запрос с пользовательским заголовком User-Agent
Допустим, мы хотим добавить пользовательский заголовок User-Agent
в HTTP-запрос GET. Заголовок User-Agent
содержит характеристическую строку, которая позволяет одноранговым узлам сетевого протокола идентифицировать тип приложения, операционную систему и поставщика программного обеспечения или версию программного обеспечения запрашивающего программного агента пользователя.
Прежде чем мы начнем писать наш HTTP-клиент, нам нужно запустить наш встроенный фиктивный сервер:
@Rule
public WireMockRule serviceMock = new WireMockRule(8089);
Когда мы создаем экземпляр HttpGet
, мы можем просто использовать метод setHeader()
для передачи имени нашего заголовка вместе со значением. Этот заголовок будет добавлен к HTTP-запросу:
String userAgent = "ForEachAgent/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
, который имеет значение, равное «ForEachAgent/1.0», тогда serviceMock
вернет 200 код ответа HTTP:
serviceMock.stubFor(get(urlEqualTo("/detail"))
.withHeader("User-Agent", equalTo(userAgent))
.willReturn(aResponse().withStatus(200)));
3. Отправка данных в теле запроса POST
Обычно, когда мы выполняем метод HTTP POST, мы хотим передать объект в качестве тела запроса. При создании экземпляра объекта HttpPost
мы можем добавить тело к этому запросу, используя метод setEntity()
:
String xmlBody = "<xml><id>1</id></xml>";
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. Отправка запросов через прокси-сервер
Часто наша веб-служба может находиться за прокси-сервером , который выполняет некоторую дополнительную логику, кэширует статические ресурсы и т. д. Когда мы создаем 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. Заключение
В этой статье показано, как настроить Apache HttpClient
для выполнения расширенных вызовов HTTP. Мы увидели, как отправлять запросы через прокси-сервер и как авторизоваться через прокси.
Реализацию всех этих примеров и фрагментов кода можно найти в проекте GitHub — это проект Maven, поэтому его должно быть легко импортировать и запускать как есть.