Advanced Apache HttpClient Configuration – 高级Apache HttpClient配置

最后修改: 2017年 2月 16日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we will be looking at the advanced usage of the Apache HttpClient library.

在这篇文章中,我们将研究Apache HttpClient库的高级用法。

We’ll look at the examples of adding custom headers to HTTP requests, and we’ll see how to configure the client to authorize and send requests through a proxy server.

我们将看看在HTTP请求中添加自定义头信息的例子,我们将看到如何配置客户端以授权并通过代理服务器发送请求。

We will be using Wiremock for stubbing the HTTP server. If you want to read more about Wiremock, check out this article.

我们将使用Wiremock对HTTP服务器进行存根。如果你想阅读更多关于Wiremock的信息,请查看这篇文章

2. HTTP Request With a Custom User-Agent Header

2.带有自定义用户代理头的HTTP请求

Let’s say that we want to add a custom User-Agent header to an HTTP GET request. The User-Agent header contains a characteristic string that allows the network protocol peers to identify the application type, operating system, and software vendor or software version of the requesting software user agent.

假设我们想在一个HTTP GET请求中添加一个自定义的User-Agent头。User-Agent头包含一个特征字符串,允许网络协议对等体识别请求软件用户代理的应用程序类型、操作系统和软件供应商或软件版本。

Before we start writing our HTTP client, we need to start our embedded mock server:

在我们开始编写我们的HTTP客户端之前,我们需要启动我们的嵌入式模拟服务器。

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

When we’re creating a HttpGet instance we can simply use a setHeader() method to pass a name of our header together with the value. That header will be added to an HTTP request:

当我们创建一个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);

We’re adding a User-Agent header and sending that request via an execute() method.

我们正在添加一个用户代理头,并通过执行()方法发送该请求。

When GET request is sent for a URL /detail with header User-Agent that has a value equal to “BaeldungAgent/1.0” then serviceMock will return 200 HTTP response code:

当GET请求被发送到一个URL /detail,其标题User-Agent的值等于 “BaeldungAgent/1.0 “时,serviceMock将返回200 HTTP响应代码。

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

3. Sending Data in the POST Request Body

3.在POST请求正文中发送数据

Usually, when we are executing the HTTP POST method, we want to pass an entity as a request body. When creating an instance of a HttpPost object, we can add the body to that request using a setEntity() method:

通常情况下,当我们执行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);

We are creating a StringEntity instance with a body that is in the XML format. It is important to set the Content-Type header to “application/xml” to pass information to the server about the type of content we’re sending. When the serviceMock receives the POST request with XML body, it responds with status code 200 OK:

我们正在创建一个StringEntity实例,其主体为XML格式。将Content-Type 头设置为”application/xml“是很重要的,这样可以向服务器传递关于我们要发送的内容类型的信息。当serviceMock收到带有XML主体的POST请求时,它的响应状态代码为200 OK。

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

4. Sending Requests via a Proxy Server

4.通过代理服务器发送请求

Often, our web service can be behind a proxy server that performs some additional logic, caches static resources, etc. When we’re creating the HTTP Client and sending a request to an actual service, we don’t want to deal with that on each and every HTTP request.

通常,我们的网络服务可以在代理服务器后面,执行一些额外的逻辑,缓存静态资源等。当我们创建HTTP客户端并向一个实际的服务发送请求时,我们不想在每一个HTTP请求中都处理这些问题。

To test this scenario, we’ll need to start up another embedded web server:

为了测试这种情况,我们需要启动另一个嵌入式Web服务器。

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

With two embedded servers, the first actual service is on the 8089 port and a proxy server is listening on the 8090 port.

有了两个嵌入式服务器,第一个实际服务在8089端口,一个代理服务器在8090端口监听。

We are configuring our HttpClient to send all requests via proxy by creating a DefaultProxyRoutePlanner that takes the HttpHost instance proxy as an argument:

我们正在配置我们的HttpClient,通过创建一个DefaultProxyRoutePlanner,将HttpHost实例代理作为一个参数来发送所有请求。

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

Our proxy server is redirecting all requests to the actual service that listens on the 8090 port. In the end of the test, we verify that request was sent to our actual service via a proxy:

我们的代理服务器正在将所有请求重定向到实际的服务,该服务在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. Configuring the HTTP Client to Authorize via Proxy

5.配置HTTP客户端以通过代理进行授权

Extending the previous example, there are some cases when the proxy server is used to perform authorization. In such configuration, a proxy can authorize all requests and pass them to the server that is hidden behind a proxy.

延伸前面的例子,有一些情况下,代理服务器被用来执行授权。在这样的配置中,代理可以授权所有的请求,并将它们传递给隐藏在代理后面的服务器。

We can configure the HttpClient to send each request via proxy, together with the Authorization header that will be used to perform an authorization process.

我们可以配置HttpClient通过代理发送每个请求,连同将用于执行授权过程的Authorization标头。

Suppose that we have a proxy server that authorizes only one user – “username_admin, with a password “secret_password.

假设我们有一个代理服务器,只授权一个用户–“用户名_admin,密码为”secret_password.

We need to create the BasicCredentialsProvider instance with credentials of the user that will be authorized via proxy. To make HttpClient automatically add the Authorization header with the proper value, we need to create a HttpClientContext with credentials provided and a BasicAuthCache that stores credentials:

我们需要创建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();

When we set up our HttpClient, making requests to our service will result in sending a request via proxy with an Authorization header to perform authorization process. It will be set in each request automatically.

当我们设置了我们的HttpClient,向我们的服务提出请求,将导致通过代理发送一个带有Authorization头的请求,以执行授权过程。它将被自动设置在每个请求中。

Let’s execute an actual request to the service:

让我们执行一个对服务的实际请求。

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

Verifying an execute() method on the httpClient with our configuration confirms that a request went through a proxy with an Authorization header:

用我们的配置验证httpClient上的execute()方法,证实了一个请求是通过一个带有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. Conclusion

6.结论

This article shows how to configure the Apache HttpClient to perform advanced HTTP calls. We saw how to send requests via a proxy server and how to authorize via proxy.

本文展示了如何配置Apache HttpClient 来执行高级HTTP调用。我们看到了如何通过代理服务器发送请求以及如何通过代理进行授权。

The implementation of all these examples and code snippets can be found in the GitHub project – this is a Maven project, so it should be easy to import and run as it is.

所有这些例子和代码片段的实现都可以在GitHub项目中找到–这是一个Maven项目,所以应该很容易导入并按原样运行。