相对于httpurlconnection ,httpclient更加丰富,也更加强大,其中apache有两个项目都是httpclient,一个是commonts包下的,这个是通用的,更专业的是org.apache.http.包下的,所以我一般用后者;
httpclient可以处理长连接,保存会话,重连接,以及请求过滤器,连接重用等等...
下面是测试代码(全部总结来自官方文档,以及翻译)
须要下载核心包:httpclient-4.3.4.jar ,也可在官网下载:http://hc.apache.org/downloads.cgi
- package httpClientTest;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.InterruptedIOException;
- import java.net.URI;
- import java.net.URISyntaxException;
- import java.net.UnknownHostException;
- import java.nio.charset.Charset;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.atomic.AtomicInteger;
- import javax.net.ssl.SSLException;
- import org.apache.http.Consts;
- import org.apache.http.HeaderElement;
- import org.apache.http.HeaderElementIterator;
- import org.apache.http.HttpClientConnection;
- import org.apache.http.HttpEntity;
- import org.apache.http.HttpEntityEnclosingRequest;
- import org.apache.http.HttpException;
- import org.apache.http.HttpHost;
- import org.apache.http.HttpRequest;
- import org.apache.http.HttpRequestInterceptor;
- import org.apache.http.HttpResponse;
- import org.apache.http.NameValuePair;
- import org.apache.http.client.ClientProtocolException;
- import org.apache.http.client.HttpRequestRetryHandler;
- import org.apache.http.client.ResponseHandler;
- import org.apache.http.client.config.RequestConfig;
- import org.apache.http.client.entity.UrlEncodedFormEntity;
- import org.apache.http.client.methods.CloseableHttpResponse;
- import org.apache.http.client.methods.HttpGet;
- import org.apache.http.client.methods.HttpPost;
- import org.apache.http.client.protocol.HttpClientContext;
- import org.apache.http.client.utils.URIBuilder;
- import org.apache.http.client.utils.URIUtils;
- import org.apache.http.conn.ConnectTimeoutException;
- import org.apache.http.conn.ConnectionKeepAliveStrategy;
- import org.apache.http.conn.ConnectionRequest;
- import org.apache.http.conn.HttpClientConnectionManager;
- import org.apache.http.conn.routing.HttpRoute;
- import org.apache.http.entity.ContentType;
- import org.apache.http.impl.client.CloseableHttpClient;
- import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
- import org.apache.http.impl.client.HttpClients;
- import org.apache.http.impl.client.LaxRedirectStrategy;
- import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
- import org.apache.http.message.BasicHeaderElementIterator;
- import org.apache.http.message.BasicNameValuePair;
- import org.apache.http.protocol.HTTP;
- import org.apache.http.protocol.HttpContext;
- import org.apache.http.util.EntityUtils;
- public class Main {
- public static void main(String[] args) {
- try {
- test9();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- /**
- * 测试1: 构建复杂uri,这种方式会很方便的设置多个参数;
- *
- * HttpClients类是client的具体一个实现类;
- *
- * URIBuilder包含:协议,主机名,端口(可选),资源路径,和多个参数(可选)
- *
- */
- private static void test1() {
- CloseableHttpClient client = HttpClients.createDefault();
- URI uri = null;
- try {
- uri = new URIBuilder()
- .setScheme("http")
- .setHost("webservice.webxml.com.cn")
- .setPath("/WebServices/MobileCodeWS.asmx/getDatabaseInfo")
- .setParameter("", "")//这里可以设置多个参数
- .setParameter("", "")
- .setParameter("", "")
- .setParameter("", "")
- .build();
- } catch (URISyntaxException e1) {
- e1.printStackTrace();
- }
- //http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getDatabaseInfo
- HttpGet get = new HttpGet(uri);
- try {
- CloseableHttpResponse response = client.execute(get);
- if(response.getStatusLine().getStatusCode()==200){
- System.out.println(EntityUtils.toString(response.getEntity()));
- //以下这种方式读取流也可以,只不过用EntityUtils会更方便
- /*InputStream is = response.getEntity().getContent();
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- byte[] buffer = new byte[1024];
- int len=-1;
- while((len = is.read(buffer))!=-1){
- os.write(buffer,0,len);
- }
- os.close();
- is.close();
- System.out.println(os.size()+new String(os.toByteArray(),"utf-8"));*/
- }
- } catch (ClientProtocolException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- /**
- * 为uri进行加密,并进行表单提交;
- *
- * 许多应用需要模拟提交的HTML表单的处理,例如,在
- 为了登录到Web应用程序或提交的输入数据。 HttpClient提供的实体类
- UrlEncodedFormEntity可以实现该过程;
- */
- private static void test2(){
- CloseableHttpClient client = HttpClients.createDefault();
- HttpPost httppost = new HttpPost("http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getMobileCodeInfo");
- //构建请求参数
- List<NameValuePair> list = new ArrayList<NameValuePair>();
- list.add(new BasicNameValuePair("mobileCode", "110"));
- list.add(new BasicNameValuePair("userID", ""));
- //构建url加密实体,并以utf-8方式进行加密;
- UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, Consts.UTF_8);
- httppost.setEntity(entity);
- try {
- CloseableHttpResponse response = client.execute(httppost);
- if(response.getStatusLine().getStatusCode()==200){
- //org.apache.http.util.EntityUtils类可以快速处理服务器返回实体对象
- System.out.println(EntityUtils.toString(response.getEntity()));
- }
- } catch (ClientProtocolException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- /**
- * 以回调方式处理返回结果
- *
- * 处理响应的最简单和最方便的方法是通过使用ResponseHandler的
- 接口。用户不必担心连接管理的问题。当使用一个
- ResponseHandler的时候,无论是否请求执行成功或导致异常,HttpClient将会自动释放连接。
- */
- private static void test3(){
- CloseableHttpClient client = HttpClients.createDefault();
- //==============================================================
- ResponseHandler<Object> handler = new ResponseHandler<Object>() {
- @Override
- public Object handleResponse(final HttpResponse response) throws IOException {
- HttpEntity entity = response.getEntity();
- if (entity == null) {
- throw new ClientProtocolException("返回结果为空");
- }
- if(response.getStatusLine().getStatusCode()==200){
- //获取返回结果的字符集 如:utf-8 gbk,并以这种字符集来读取流信息
- ContentType contentType = ContentType.getOrDefault(entity);
- Charset charset = contentType.getCharset();
- InputStreamReader reader = new InputStreamReader(entity.getContent(), charset);
- BufferedReader br = new BufferedReader(reader);
- StringBuffer sb = new StringBuffer();
- char[] buffer = new char[1024];
- while (br.read(buffer)!=-1) {
- sb.append(new String(buffer));
- }
- return sb.toString();
- }
- return null;
- }
- };
- //===================================================================
- URI uri = null;//构建uri实体
- try {
- uri = new URIBuilder()
- .setScheme("http")
- .setHost("webservice.webxml.com.cn")
- .setPath("/WebServices/MobileCodeWS.asmx/getDatabaseInfo")
- .setParameter("", "")
- .setParameter("", "")
- .setParameter("", "")
- .build();
- } catch (URISyntaxException e) {
- e.printStackTrace();
- }
- HttpPost post = new HttpPost(uri);
- try {
- //handler回调
- Object obj = client.execute(post, handler);
- System.out.println("返回结果:"+obj);
- } catch (ClientProtocolException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- /**
- * 设置长连接策略,根据服务器的约束或者客户端的约束来设置长连接的时长;
- */
- private static void test4() {
- ConnectionKeepAliveStrategy strategy = new DefaultConnectionKeepAliveStrategy() {
- /**
- * 服务器端配置(以tomcat为例):keepAliveTimeout=60000,表示在60s内内,服务器会一直保持连接状态。
- * 也就是说,如果客户端一直请求服务器,且间隔未超过60s,则该连接将一直保持,如果60s内未请求,则超时。
- *
- * getKeepAliveDuration返回超时时间;
- */
- @Override
- public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
- //如果服务器指定了超时时间,则以服务器的超时时间为准
- HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
- while (it.hasNext()) {
- HeaderElement he = it.nextElement();
- String param = he.getName();
- String value = he.getValue();
- if (value != null && param.equalsIgnoreCase("timeout")) {
- try {
- System.out.println("服务器指定的超时时间:" + value + " 秒");
- return Long.parseLong(value) * 1000;
- } catch (NumberFormatException ignore) {
- ignore.printStackTrace();
- }
- }
- }
- long keepAlive = super.getKeepAliveDuration(response, context);
- // 如果服务器未指定超时时间,则客户端默认30s超时
- if (keepAlive == -1) {
- keepAlive = 30 * 1000;
- }
- return keepAlive;
- /*如果访问webservice.webxml.com.cn主机,则超时时间5秒,其他主机超时时间30秒
- HttpHost host = (HttpHost) context.getAttribute(HttpClientContext.HTTP_TARGET_HOST);
- if ("webservice.webxml.com.cn".equalsIgnoreCase(host.getHostName())) {
- keepAlive = 10 * 1000;
- } else {
- keepAlive = 30 * 1000;
- }*/
- }
- };
- CloseableHttpClient client = HttpClients.custom().setKeepAliveStrategy(strategy).build();
- URI uri = null;//构建uri实体
- try {
- uri = new URIBuilder()
- .setScheme("http")
- .setHost("webservice.webxml.com.cn")
- .setPath("/WebServices/MobileCodeWS.asmx/getDatabaseInfo")
- .setParameter("", "")
- .build();
- } catch (URISyntaxException e) {
- e.printStackTrace();
- }
- HttpPost post = new HttpPost(uri);
- try {
- CloseableHttpResponse response = client.execute(post);
- if(response.getStatusLine().getStatusCode()==200){
- String result = EntityUtils.toString(response.getEntity());
- System.out.println("返回的结果====="+result);
- }
- } catch (ClientProtocolException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- /**
- * 构建复杂请求体
- *
- * RequestConfig将会保存在context上下文中,并会在连续的请求中进行传播(来自官方文档);
- *
- */
- private void test5(){
- CloseableHttpClient client = HttpClients.createDefault();
- //构建请求配置
- RequestConfig config = RequestConfig.
- custom().
- setSocketTimeout(1000*10).
- setConnectTimeout(1000*10).
- build();
- //==============================================================
- ResponseHandler<Object> handler = new ResponseHandler<Object>() {//回调
- @Override
- public Object handleResponse(final HttpResponse response) throws IOException {
- HttpEntity entity = response.getEntity();
- if (entity == null) {
- throw new ClientProtocolException( "返回结果为空");
- }
- if(response.getStatusLine().getStatusCode()==200){
- ContentType contentType = ContentType.getOrDefault(entity);
- Charset charset = contentType.getCharset();
- InputStreamReader reader = new InputStreamReader(entity.getContent(), charset);
- BufferedReader br = new BufferedReader(reader);
- StringBuffer sb = new StringBuffer();
- char[] buffer = new char[1024];
- while (br.read(buffer)!=-1) {
- sb.append(new String(buffer));
- }
- return sb.toString();
- }
- return null;
- }
- };
- //===================================================================
- URI uri = null;
- try {
- uri = new URIBuilder()
- .setScheme("http")
- .setHost("webservice.webxml.com.cn")
- .setPath("/WebServices/MobileCodeWS.asmx/getDatabaseInfo")
- .setParameter("", "")
- .setParameter("", "")
- .setParameter("", "")
- .build();
- } catch (URISyntaxException e) {
- e.printStackTrace();
- }
- HttpPost post = new HttpPost(uri);
- post.setConfig(config);//设置请求配置
- try {
- Object obj = client.execute(post, handler);
- System.out.println("返回结果:"+obj);
- } catch (ClientProtocolException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- /**
- * 异常恢复机制:
- *
- * HttpRequestRetryHandler连接失败后,可以针对相应的异常进行相应的处理措施;
- * HttpRequestRetryHandler接口须要用户自己实现;
- *
- */
- private static void test6(){
- HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {
- /**
- * exception异常信息;
- * executionCount:重连次数;
- * context:上下文
- */
- @Override
- public boolean retryRequest(IOException exception, int executionCount,HttpContext context) {
- System.out.println("重连接次数:"+executionCount);
- if (executionCount >= 5) {//如果连接次数超过5次,就不进行重复连接
- return false;
- }
- if (exception instanceof InterruptedIOException) {//io操作中断
- return false;
- }
- if (exception instanceof UnknownHostException) {//未找到主机
- // Unknown host
- return false;
- }
- if (exception instanceof ConnectTimeoutException) {//连接超时
- return true;
- }
- if (exception instanceof SSLException) {
- // SSL handshake exception
- return false;
- }
- HttpClientContext clientContext = HttpClientContext.adapt(context);
- HttpRequest request = clientContext.getRequest();
- boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
- if (idempotent) {
- // Retry if the request is considered idempotent
- return true;
- }
- return false;
- }
- };
- CloseableHttpClient client = HttpClients.custom().setRetryHandler(retryHandler).build();
- RequestConfig config = RequestConfig.
- custom().
- setSocketTimeout(1000*10).
- setConnectTimeout(1000*10).
- build();
- HttpPost post = new HttpPost("http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getDatabaseInfo");
- post.setConfig(config);
- try {
- HttpResponse response = client.execute(post);
- HttpEntity entity = response.getEntity();
- ContentType contentType = ContentType.getOrDefault(entity);
- Charset charset = contentType.getCharset();
- InputStreamReader reader = new InputStreamReader(entity.getContent(), charset);
- BufferedReader br = new BufferedReader(reader);
- StringBuffer sb = new StringBuffer();
- char[] buffer = new char[1024];
- while (br.read(buffer) != -1) {
- sb.append(new String(buffer));
- }
- System.out.println("返回的结果:"+sb.toString());
- } catch (ClientProtocolException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- /**
- * HTTP请求过滤器,在执行请求之前拦截HttpRequest 和 HttpContext;
- */
- private static void test7() throws ClientProtocolException, IOException{
- HttpRequestInterceptor requestInterceptor = new HttpRequestInterceptor() {
- /**
- * 处理请求:
- * 如果是客户端:这个处理在发送请求之前执行;
- * 如果是服务器:这个处理在接收到请求体之前执行;
- */
- public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
- AtomicInteger count = (AtomicInteger) context.getAttribute("count");
- request.addHeader("count", Integer.toString(count.getAndIncrement()));
- }
- };
- CloseableHttpClient httpclient = HttpClients.
- custom().
- addInterceptorLast(requestInterceptor).
- build();
- AtomicInteger count = new AtomicInteger(1);
- HttpClientContext context = HttpClientContext.create();
- context.setAttribute("count", count);
- HttpGet httpget = new HttpGet("http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getDatabaseInfo");
- for (int i = 0; i < 10; i++) {
- CloseableHttpResponse response = httpclient.execute(httpget,context);
- try {
- HttpEntity entity = response.getEntity();
- // System.out.println(EntityUtils.toString(entity));
- } finally {
- response.close();
- }
- }
- }
- /**
- *
- * httpclient会自动处理重定向;
- *
- * 301 永久重定向,告诉客户端以后应从新地址访问.
- 302 作为HTTP1.0的标准,以前叫做Moved Temporarily ,现在叫Found. 现在使用只是为了兼容性的 处理,包括PHP的默认Location重定向用的也是302.
- 但是HTTP 1.1 有303 和307作为详细的补充,其实是对302的细化
- 303:对于POST请求,它表示请求已经被处理,客户端可以接着使用GET方法去请求Location里的URI。
- 307:对于POST请求,表示请求还没有被处理,客户端应该向Location里的URI重新发起POST请求。
- */
- private static void test8() throws ClientProtocolException, IOException,
- URISyntaxException {
- LaxRedirectStrategy redirectStrategy = new LaxRedirectStrategy();
- CloseableHttpClient httpclient = HttpClients.custom().setRedirectStrategy(redirectStrategy).build();
- HttpClientContext context = HttpClientContext.create();
- HttpGet httpget = new HttpGet("http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getDatabaseInfo");
- CloseableHttpResponse response = httpclient.execute(httpget, context);
- try {
- HttpHost target = context.getTargetHost();
- List<URI> redirectLocations = context.getRedirectLocations();
- URI location = URIUtils.resolve(httpget.getURI(), target, redirectLocations);
- System.out.println("Final HTTP location: " + location.toASCIIString());
- } finally {
- response.close();
- }
- }
- }