在发送Http请求之后 - 我们得到一个org.apache.http.HttpResponse的实例- 它允许我们访问响应的状态行,并隐含地显示状态码:
response.getStatusLine().getStatusCode()
使用这个,我们可以验证我们从服务器收到的代码确实是正确的:
@Test
public void givenGetRequestExecuted_whenAnalyzingTheResponse_thenCorrectStatusCode()
throws ClientProtocolException, IOException {
HttpClient client = HttpClientBuilder.create().build();
HttpResponse response = client.execute(new HttpGet(SAMPLE_URL));
int statusCode = response.getStatusLine().getStatusCode();
assertThat(statusCode, equalTo(HttpStatus.SC_OK));
}
请注意,我们通过org.apache.http.HttpStatus使用库中预定义的状态码。
该HttpClient的自带了大量的配置参数-所有这些都可以在一个通用的,地图状的方式来设置。
有3个超时参数需要配置:
DefaultHttpClient httpClient = new DefaultHttpClient();
int timeout = 5; // seconds
HttpParams httpParams = httpClient.getParams();
httpParams.setParameter(
CoreConnectionPNames.CONNECTION_TIMEOUT, timeout * 1000);
httpParams.setParameter(
CoreConnectionPNames.SO_TIMEOUT, timeout * 1000);
// httpParams.setParameter(
// ClientPNames.CONN_MANAGER_TIMEOUT, new Long(timeout * 1000));
注意:由于此JIRA(4.3.2中的规定),最后一个参数 - 连接管理器超时 - 在使用4.3.0或4.3.1版本时被注释掉了。
这些参数中最重要的参数 - 前两个 - 也可以通过更安全的API来设置:
DefaultHttpClient httpClient = new DefaultHttpClient();
int timeout = 5; // seconds
HttpParams httpParams = httpClient.getParams();
HttpConnectionParams.setConnectionTimeout(
httpParams, timeout * 1000); // http.connection.timeout
HttpConnectionParams.setSoTimeout(
httpParams, timeout * 1000); // http.socket.timeout
第三个参数在HttpConnectionParams中没有自定义设置器,它仍然需要通过setParameter方法手动设置。
4.3中介绍的流畅的构建器API提供了在较高级别设置超时的正确方法:
int timeout = 5;
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeout * 1000)
.setConnectionRequestTimeout(timeout * 1000)
.setSocketTimeout(timeout * 1000).build();
CloseableHttpClient client =
HttpClientBuilder.create().setDefaultRequestConfig(config).build();
现在,我们来解释这些不同类型的超时的含义:
前两个参数 - 连接和套接字超时 - 是最重要的,但为获得连接设置超时在高负载情况下显然非常重要,这就是为什么第三个参数不应该被忽略的原因。
配置完成后,客户端不能用于执行HTTP请求:
HttpGet getMethod = new HttpGet("http://host:8080/path");
HttpResponse response = httpClient.execute(getMethod);
System.out.println(
"HTTP Status of response: " + response.getStatusLine().getStatusCode());
使用先前定义的客户端,与主机的连接将在5秒内超时,如果连接已建立但未收到数据,则超时也将增加5秒。
请注意,连接超时将导致org.apache.http.conn.ConnectTimeoutException被抛出,而套接字超时将导致一个java.net.SocketTimeoutException。
虽然在建立HTTP连接和不接收数据时设置超时非常有用,但有时我们需要为整个请求设置硬超时。
例如,下载一个潜在的大文件适合这个类别 - 在这种情况下,连接可能会成功建立,数据可能会一直通过,但我们仍然需要确保操作不会超过某个特定时间阈。
HttpClient没有任何允许我们为请求设置总超时的配置; 但它确实为请求提供了异常功能,所以我们可以利用该机制来实现一个简单的超时机制:
HttpGet getMethod = new HttpGet(
"http://localhost:8080/spring-security-rest-template/api/bars/1");
int hardTimeout = 5; // seconds
TimerTask task = new TimerTask() {
@Override
public void run() {
if (getMethod != null) {
getMethod.abort();
}
}
};
new Timer(true).schedule(task, hardTimeout * 1000);
HttpResponse response = httpClient.execute(getMethod);
System.out.println(
"HTTP Status of response: " + response.getStatusLine().getStatusCode());
我们使用java.util.Timer和java.util.TimerTask来设置一个简单的延迟任务,在5秒硬超时后中止HTTP GET请求。
一些较大的域将使用DNS循环配置是非常常见的 - 基本上将相同的域映射到多个IP地址。由于HttpClient尝试连接到超时域的方式,这就给这个域的超时带来了新的挑战:
所以,正如你所看到的那样 - 当我们预期它的整体操作不会超时。取而代之的是,当所有可能的路由超时以及更多的时候 - 它将完全透明地发生在客户端(除非你在DEBUG级别配置了你的日志)。以下是一个可以运行并复制此问题的简单示例:
int timeout = 3;
RequestConfig config = RequestConfig.custom().
setConnectTimeout(timeout * 1000).
setConnectionRequestTimeout(timeout * 1000).
setSocketTimeout(timeout * 1000).build();
CloseableHttpClient client = HttpClientBuilder.create()
.setDefaultRequestConfig(config).build();
HttpGet request = new HttpGet("http://www.google.com:81");
response = client.execute(request);
您将注意到具有DEBUG日志级别的重试逻辑:
DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.212:81
DEBUG o.a.h.i.c.HttpClientConnectionOperator -
Connect to www.google.com/173.194.34.212:81 timed out. Connection will be retried using another IP address
DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.208:81
DEBUG o.a.h.i.c.HttpClientConnectionOperator -
Connect to www.google.com/173.194.34.208:81 timed out. Connection will be retried using another IP address
DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.209:81
DEBUG o.a.h.i.c.HttpClientConnectionOperator -
Connect to www.google.com/173.194.34.209:81 timed out. Connection will be retried using another IP address
//...
要中止正在进行的请求,客户可以简单地使用:
request.abort();
这将确保客户端不必使用整个请求来释放连接:
@Test
public void whenRequestIsCanceled_thenCorrect()
throws ClientProtocolException, IOException {
HttpClient instance = HttpClients.custom().build();
HttpGet request = new HttpGet(SAMPLE_URL);
HttpResponse response = instance.execute(request);
try {
System.out.println(response.getStatusLine());
request.abort();
} finally {
response.close();
}
}
在旧版本的Http客户端(4.3版之前)中,我们可以配置客户端重定向的功能,如下所示:
@Test
public void givenRedirectsAreDisabled_whenConsumingUrlWhichRedirects_thenNotRedirected()
throws ClientProtocolException, IOException {
DefaultHttpClient instance = new DefaultHttpClient();
HttpParams params = new BasicHttpParams();
params.setParameter(ClientPNames.HANDLE_REDIRECTS, false);
// HttpClientParams.setRedirecting(params, false); // alternative
HttpGet httpGet = new HttpGet("http://t.co/I5YYd9tddw");
httpGet.setParams(params);
CloseableHttpResponse response = instance.execute(httpGet);
assertThat(response.getStatusLine().getStatusCode(), equalTo(301));
}
注意可以使用替代API来配置重定向行为,而不用设置实际的原始 http.protocol.handle-redirects参数:
HttpClientParams.setRedirecting(params, false);
另外请注意,在禁用了后续重定向的情况下,我们现在可以检查Http Response状态代码是否确实是301永久移动 - 因为它应该是。
HttpClient 4.3引入了更清洁,更高层次的API来构建和配置客户端:
@Test
public void givenRedirectsAreDisabled_whenConsumingUrlWhichRedirects_thenNotRedirected()
throws ClientProtocolException, IOException {
HttpClient instance = HttpClientBuilder.create().disableRedirectHandling().build();
HttpResponse response = instance.execute(new HttpGet("http://t.co/I5YYd9tddw"));
assertThat(response.getStatusLine().getStatusCode(), equalTo(301));
}
请注意,新的API使用此重定向行为配置整个客户端 - 而不仅仅是个别请求。
您可以使用简单的setHeader调用在请求上设置任何自定义标题:
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(SAMPLE_URL);
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
HttpClient 4.3引入了一种构建请求的新方法 - RequestBuilder。要设置标题,我们将在构建器上使用相同的setHeader方法:
HttpClient client = HttpClients.custom().build();
HttpUriRequest request = RequestBuilder.get()
.setUri(SAMPLE_URL)
.setHeader(HttpHeaders.CONTENT_TYPE, "application/json")
.build();
client.execute(request);
除了在每个请求上设置标题,您还可以将其配置为客户端本身的默认标题:
Header header = new BasicHeader(
HttpHeaders.CONTENT_TYPE, "application/json");
List<Header> headers = Lists.newArrayList(header);
HttpClient client = HttpClients.custom()
.setDefaultHeaders(headers).build();
HttpUriRequest request = RequestBuilder.get()
.setUri(SAMPLE_URL).build();
client.execute(request);
当所有请求的头部需要相同时,这非常有用 - 例如自定义应用程序头部。
创建http客户端
CloseableHttpClient client = HttpClientBuilder.create().build();
发送基本的GET请求
instance.execute(new HttpGet("http://www.google.com"));
获取HTTP响应的状态码
CloseableHttpResponse response = instance.execute(new HttpGet("http://www.google.com"));
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
获取响应的媒体类型
CloseableHttpResponse response = instance.execute(new HttpGet("http://www.google.com"));
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
获取响应的媒体类型
CloseableHttpResponse response = instance.execute(new HttpGet("http://www.google.com"));
String contentMimeType = ContentType.getOrDefault(response.getEntity()).getMimeType();
assertThat(contentMimeType, equalTo(ContentType.TEXT_HTML.getMimeType()));
得到响应的主体
CloseableHttpResponse response = instance.execute(new HttpGet("http://www.google.com"));
String bodyAsString = EntityUtils.toString(response.getEntity());
assertThat(bodyAsString, notNullValue());
配置请求的超时时间
@Test(expected = SocketTimeoutException.class)
public void givenLowTimeout_whenExecutingRequestWithTimeout_thenException()
throws ClientProtocolException, IOException {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(1000).setConnectTimeout(1000).setSocketTimeout(1000).build();
HttpGet request = new HttpGet(SAMPLE_URL);
request.setConfig(requestConfig);
instance.execute(request);
}
在整个客户端上配置超时
RequestConfig requestConfig = RequestConfig.custom().
setConnectionRequestTimeout(1000).setConnectTimeout(1000).setSocketTimeout(1000).build();
HttpClientBuilder builder = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig);
发送POST请求
instance.execute(new HttpPost(SAMPLE_URL));
将参数添加到请求
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("key1", "value1"));
params.add(new BasicNameValuePair("key2", "value2"));
request.setEntity(new UrlEncodedFormEntity(params, Consts.UTF_8));
配置如何处理HTTP请求的重定向
CloseableHttpClient instance = HttpClientBuilder.create().disableRedirectHandling().build();
CloseableHttpResponse response = instance.execute(new HttpGet("http://t.co/I5YYd9tddw"));
assertThat(response.getStatusLine().getStatusCode(), equalTo(301));
为请求配置标题
HttpGet request = new HttpGet(SAMPLE_URL);
request.addHeader(HttpHeaders.ACCEPT, "application/xml");
response = instance.execute(request);
从响应中获取头文件
CloseableHttpResponse response = instance.execute(new HttpGet(SAMPLE_URL));
Header[] headers = response.getHeaders(HttpHeaders.CONTENT_TYPE);
assertThat(headers, not(emptyArray()));
关闭/释放资源
response = instance.execute(new HttpGet(SAMPLE_URL));
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
instream.close();
}
} finally {
response.close();
}
https://www.leftso.com/article/394.html