1. Обзор
В этой статье мы рассмотрим расширенное использование библиотеки Apache HttpClient .
Мы рассмотрим примеры добавления пользовательских заголовков в HTTP-запросы, а также рассмотрим, как настроить клиент для авторизации и отправки запросов через прокси-сервер.
Мы будем использовать Wiremock для заглушения HTTP-сервера. Если вы хотите узнать больше о Wiremock, ознакомьтесь с этой статьей .
2. HTTP-запрос С Пользовательским заголовком Агента пользователя
Допустим, мы хотим добавить пользовательский заголовок User-Agent в запрос HTTP GET. Заголовок User-Agent содержит характеристическую строку, которая позволяет одноранговым узлам сетевого протокола идентифицировать тип приложения, операционную систему и поставщика программного обеспечения или версию программного обеспечения запрашивающего агента пользователя программного обеспечения.
Прежде чем мы начнем писать ваш HTTP-клиент, нам нужно запустить наш встроенный mockserver:
@Rule public WireMockRule serviceMock = new WireMockRule(8089);
Когда мы создаем HttpGet экземпляр мы можем просто использовать метод setHeader () , чтобы передать имя вашего заголовка вместе со значением. Этот заголовок будет добавлен в HTTP-запрос:
String userAgent = "BaeldungAgent/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 , который имеет значение, равное “BaeldungAgent/1.0”, то serviceMock вернет код ответа HTTP 200:
serviceMock.stubFor(get(urlEqualTo("/detail")) .withHeader("User-Agent", equalTo(userAgent)) .willReturn(aResponse().withStatus(200)));
3. Отправка данных в теле запроса POST
Обычно, когда мы выполняем метод HTTP POST, мы хотим передать сущность в качестве тела запроса. При создании экземпляра a Http Post object, мы можем добавить тело к этому запросу с помощью метода setEntity() :
String xmlBody = ""; 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); 1
Мы создаем экземпляр String Entity с телом в формате XML . Важно установить заголовок Content-Type в ” application/xml “, чтобы передать серверу информацию о типе отправляемого контента. Когда service Mock получает запрос POST с телом XML, он отвечает кодом состояния 200 OK:
serviceMock.stubFor(post(urlEqualTo("/person")) .withHeader("Content-Type", equalTo("application/xml")) .withRequestBody(equalTo(xmlBody)) .willReturn(aResponse().withStatus(200)));
4. Отправка запросов через прокси-сервер
Часто наш веб-сервис может находиться за прокси-сервером , который выполняет некоторую дополнительную логику, кэширует статические ресурсы и т. Д. Когда мы создаем HttpClient и отправляем запрос в реальную службу, мы не хотим иметь дело с этим при каждом 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, поэтому его должно быть легко импортировать и запускать как есть.