HttpClient

HttpClient

1.关于HttpClient

1.1.背景

Http协议应该是互联网中最重要的协议。持续增长的web服务、可联网的家用电器等都在继承并拓展着Http协议,向着浏览器之外的方向发展。

虽然jdk中的java.net包中提供了一些基本的方法,通过http协议来访问网络资源,但是大多数场景下,它都不够灵活和强大。HttpClient致力于填补这个空白,它可以提供有效的、最新的、功能丰富的包来实现http客户端。

为了拓展,HttpClient即支持基本的http协议,还支持http-aware客户端程序,如web浏览器,Webservice客户端,以及利用or拓展http协议的分布式系统。

1.2.HttpClient的范围/特性

1)HttpClient是一个基于HttpCore的客户端Http传输类库

2)Http基于传统的IO(阻塞)

1.3.HttpClient局限

HttpClient不是浏览器,它是一个客户端http协议传输类库。HttpClient被用来发送和接受Http消息。HttpClient不会处理http消息的内容,不会进行javascript解析,不会关心contenttype,如果没有明确设置,httpclient也不会对请求进行格式化、重定向url,或者其他任何和http消息传输相关的功能。

2.HttpClient基本概念

2.1.请求执行

HttpClient最基本的功能就是执行Http方法。一个Http方法的执行涉及到一个或者多个Http请求/Http响应的交互,通常这个过程都会自动被HttpClient处理,对用户透明。用户只需要提供Http请求对象,HttpClient就会将http请求发送给目标服务器,并且接收服务器的响应,如果http请求执行不成功,httpclient就会抛出异样。

http请求执行:

package com.me.homesickness.practice.httpClient.requestExecution;

 

import java.io.IOException;

 

import org.apache.http.client.ClientProtocolException;

import org.apache.http.client.methods.CloseableHttpResponse;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.CloseableHttpClient;

import org.apache.http.impl.client.HttpClients;

 

/**

 * 请求执行

 * @author Administrator

 *

 */

public class RequestExecution001 {

 

    public static void main(String[] args) throws ClientProtocolException, IOException {

        CloseableHttpClient httpClient = HttpClients.createDefault();

        HttpGet httpGet = new HttpGet("https://www.baidu.com/");

        CloseableHttpResponse response = httpClient.execute(httpGet);

       

        try {

            System.out.println(response.getAllHeaders());

        } finally {

            response.close();

        }

    }

   

}

2.1.1.HTTP请求

1)所有的Http请求都有一个请求行(requestline),包括方法名、请求的URI和Http版本号。

2) HttpClient支持HTTP/1.1这个版本定义的所有Http方法:GET,HEAD,POST,PUT,DELETE,TRACE和OPTIONS。对于每一种http方法,HttpClient都定义了一个相应的类:

     HttpGet,HttpHead,HttpPost,HttpPut,HttpDelete,HttpTrace和HttpOpquertions。

3)Request-URI即统一资源定位符,用来标明Http请求中的资源。HttprequestURIs包含协议名、主机名、主机端口(可选)、资源路径、query(可选)和片段信息(可选)。

例:

HttpGet httpGet = new HttpGet("http://www.google.com/search?hl=en&q=httpclient&btnG=Google+Search&aq=f&oq=");

4)HttpClient提供URIBuilder工具类来简化URIs的创建和修改过程。

例:

package com.me.homesickness.practice.httpClient.requestExecution;

 

import java.net.URI;

import java.net.URISyntaxException;

 

import org.apache.http.client.methods.HttpGet;

import org.apache.http.client.utils.URIBuilder;

 

/**

 * URIBuilders工具类简化URIs的创建

 * @author Administrator

 *

 */

public class RequestExecution002 {

 

    public static void main(String[] args) throws URISyntaxException {

       

        URI uri = new URIBuilder()

                .setScheme("http")

                .setHost("www.baidu.com")

                .setPath("s")

                .setParameter("param1", "value1")

                .setParameter("param2", "value2")

                .build();

        HttpGet httpGet = new HttpGet(uri);

        System.out.println(httpGet.getURI());

       

    }

 

}

结果:

2.1.2.HTTP响应

服务器收到客户端的http请求后,就会对其进行解析,然后把响应发给客户端,这个响应就是HTTPresponse.HTTP响应第一行是协议版本,之后是数字状态码和相关联的文本段。

例:

package com.me.homesickness.practice.httpClient.requestExecution;

 

import org.apache.http.HttpResponse;

import org.apache.http.HttpStatus;

import org.apache.http.HttpVersion;

import org.apache.http.message.BasicHttpResponse;

 

/**

 * HTTP响应

 * @author Administrator

 *

 */

public class RequestExecution003 {

 

    public static void main(String[] args) {

       

        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1,

                HttpStatus.SC_OK,

                "OK");

       

        System.out.println(response.getProtocolVersion());

        System.out.println(response.getStatusLine().getStatusCode());

        System.out.println(response.getStatusLine().getReasonPhrase());

        System.out.println(response.getStatusLine().toString());

       

    }

   

}

结果:

2.1.3.消息头

一个Http消息可以包含一系列的消息头,用来对http消息进行描述,比如消息长度,消息类型等等。HttpClient提供了方法来获取、添加、移除、枚举消息头。

例:

package com.me.homesickness.practice.httpClient.requestExecution;

 

import org.apache.http.Header;

import org.apache.http.HttpResponse;

import org.apache.http.HttpStatus;

import org.apache.http.HttpVersion;

import org.apache.http.message.BasicHttpResponse;

 

/**

 * 消息头

 * @author Administrator

 *

 */

public class RequestExecution004 {

 

    public static void main(String[] args) {

        HttpResponse response=new BasicHttpResponse(HttpVersion.HTTP_1_1,HttpStatus.SC_OK,"OK");

        response.addHeader("Set-Cookie","c1=a;path=/;domain=localhost");

        response.addHeader("Set-Cookie","c2=b;path=\"/\",c3=c;domain=\"localhost\"");

       

        Header heade1 = response.getFirstHeader("Set-Cookie");

        System.out.println(heade1);

        Header heade2 = response.getLastHeader("Set-Cookie");

        System.out.println(heade2);

        Header[] headers = response.getHeaders("Set-Cookie");

        System.out.println(headers.length);

       

    }

   

}

结果:

最有效的获取指定类型的消息头的方法还是使用HeaderIterator接口。

例:

package com.me.homesickness.practice.httpClient.requestExecution;

 

import org.apache.http.Header;

import org.apache.http.HeaderIterator;

import org.apache.http.HttpResponse;

import org.apache.http.HttpStatus;

import org.apache.http.HttpVersion;

import org.apache.http.message.BasicHttpResponse;

 

/**

 * 消息头

 * @author Administrator

 *

 */

public class RequestExecution004 {

 

    public static void main(String[] args) {

        HttpResponse response=new BasicHttpResponse(HttpVersion.HTTP_1_1,HttpStatus.SC_OK,"OK");

        response.addHeader("Set-Cookie","c1=a;path=/;domain=localhost");

        response.addHeader("Set-Cookie","c2=b;path=\"/\",c3=c;domain=\"localhost\"");

       

        // 1) 获取Head

//      Header heade1 = response.getFirstHeader("Set-Cookie");

//      System.out.println(heade1);

//      Header heade2 = response.getLastHeader("Set-Cookie");

//      System.out.println(heade2);

//      Header[] headers = response.getHeaders("Set-Cookie");

//      System.out.println(headers.length);

       

        // 2) 使用HeaderIterator接口获取获取Head

        HeaderIterator it = response.headerIterator("Set-Cookie");

        while(it.hasNext()) {

            System.out.println(it.next());

        }

       

    }

   

}

结果:

HeaderIterator也提供非常便捷的方式,将Http消息解析成单独的消息头元素。

例:

package com.me.homesickness.practice.httpClient.requestExecution;

 

import org.apache.http.Header;

import org.apache.http.HeaderElement;

import org.apache.http.HeaderElementIterator;

import org.apache.http.HeaderIterator;

import org.apache.http.HttpResponse;

import org.apache.http.HttpStatus;

import org.apache.http.HttpVersion;

import org.apache.http.NameValuePair;

import org.apache.http.message.BasicHeaderElementIterator;

import org.apache.http.message.BasicHttpResponse;

 

/**

 * 消息头

 * @author Administrator

 *

 */

public class RequestExecution004 {

 

    public static void main(String[] args) {

        HttpResponse response=new BasicHttpResponse(HttpVersion.HTTP_1_1,HttpStatus.SC_OK,"OK");

        response.addHeader("Set-Cookie","c1=a;path=/;domain=localhost");

        response.addHeader("Set-Cookie","c2=b;path=\"/\",c3=c;domain=\"localhost\"");

       

        // 1) 获取Head

//      Header heade1 = response.getFirstHeader("Set-Cookie");

//      System.out.println(heade1);

//      Header heade2 = response.getLastHeader("Set-Cookie");

//      System.out.println(heade2);

//      Header[] headers = response.getHeaders("Set-Cookie");

//      System.out.println(headers.length);

       

        // 2) 使用HeaderIterator接口获取获取Head

//      HeaderIterator it = response.headerIterator("Set-Cookie");

//      while(it.hasNext()) {

//          System.out.println(it.next());

//      }

       

        //3) 使用HeaderIterator获取消息头元素

        HeaderIterator it = response.headerIterator("Set-Cookie");

        HeaderElementIterator itI = new BasicHeaderElementIterator(it);

        while(itI.hasNext()) {

            HeaderElement elem = itI.nextElement();

            System.out.println(elem.getName() + "=" + elem.getValue());

            NameValuePair[] params = elem.getParameters();

            for(int i = 0; i < params.length; i++) {

                System.out.println(" " + params[i]);

            }

        }

       

    }

   

}

结果:

2.1.4.HTTP实体

Http消息可以携带http实体,这个http实体既可以是http请求,也可以是http响应的。Http实体,可以在某些http请求或者响应中发现,但不是必须的。Http规范中定义了两种包含请求的方法:POST和PUT。HTTP响应一般会包含一个内容实体。当然这条规则也有异常情况,如Head方法的响应,204没有内容,304没有修改或者205内容资源重置。

2.1.4.1. HttpClient实体内容的分类

1)streamed流式

内容是通过流来接受或者在运行中产生。特别是,streamed这一类包含从http响应中获取的实体内容。一般说来,streamed实体是不可重复的。

2)self-contained自我包含式

内容在内存中或通过独立的连接或其它实体中获得。self-contained类型的实体内容通常是可重复的。这种类型的实体通常用于关闭http请求。

3)wrapping包装式:

这种类型的内容是从另外的http实体中获取的。

4)其它

当从Http响应中读取内容时,上面的三种区分对于连接管理器来说是非常重要的。对于由应用程序创建而且只使用HttpClient发送的请求实体,streamed和self-contained两种类型的不同就不那么重要了。这种情况下,建议考虑如streamed流式这种不能重复的实体,和可以重复的self-contained自我包含式实体。

2.1.4.2.可重复的实体

一个实体是可重复的,也就是说它的包含的内容可以被多次读取。这种多次读取只有selfcontained(自包含)的实体能做到(比如ByteArrayEntity或者StringEntity)。

2.1.4.3.使用Http实体

由于一个Http实体既可以表示二进制内容,又可以表示文本内容,所以Http实体要支持字符编码(为了支持后者,即文本内容)。当需要执行一个完整内容的Http请求或者Http请求已经成功,服务器要发送响应到客户端时,Http实体就会被创建。

当实体类已经被接受后,我们可以利用HttpEntity类的getContentType()和getContentLength()方法来读取Content-Type和Content-Length两个头消息(如果有的话)。由于Content-Type包含mime-types的字符编码,比如text/plain或者text/html,HttpEntity类的getContentEncoding()方法就是读取这个编码的。如果头信息不存在,getContentLength()会返回-1,getContentType()会返回NULL。如果Content-Type信息存在,就会返回一个Header类。当为发送消息创建Http实体时,需要同时附加meta信息。

例:

package com.me.homesickness.practice.httpClient.requestExecution;

 

import java.io.IOException;

 

import org.apache.http.ParseException;

import org.apache.http.entity.ContentType;

import org.apache.http.entity.StringEntity;

import org.apache.http.util.EntityUtils;

 

/**

 * Http实体

 * @author Administrator

 *

 */

public class RequestExecution005 {

 

    public static void main(String[] args) throws ParseException, IOException {

        StringEntity myEntity = new StringEntity("important message",

                ContentType.create("text/plain","UTF-8"));

        System.out.println(myEntity.getContentType()); // 获取请求头

        System.out.println(myEntity.getContentLength()); // 获取请求头长度

        System.out.println(EntityUtils.toString(myEntity));

        System.out.println(EntityUtils.toByteArray(myEntity).length);

    }

   

}

结果:

2.1.5.确保底层的资源连接被释放

2.1.5.1.管理Http实体的内容流

例:

package com.me.homesickness.practice.httpClient.requestExecution;

 

import java.io.IOException;

import java.io.InputStream;

 

import org.apache.http.HttpEntity;

import org.apache.http.client.ClientProtocolException;

import org.apache.http.client.methods.CloseableHttpResponse;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.CloseableHttpClient;

import org.apache.http.impl.client.HttpClients;

 

/**

 * 释放底层资源:管理Http实体的内容流

 * @author Administrator

 *

 */

public class RequestExecution006 {

 

    public static void main(String[] args) throws ClientProtocolException, IOException {

        // TODO Auto-generated method stub

        CloseableHttpClient closeableHttpClient = HttpClients.createDefault();

        HttpGet httpGet = new HttpGet("http://www.baidu.com");

        CloseableHttpResponse response = closeableHttpClient.execute(httpGet);

        try {

            HttpEntity entity = response.getEntity();

            if(entity != null) {

                InputStream instream = entity.getContent();

                try {

                    // doSomething

                } finally {

                    instream.close();

                }

            }

           

        } finally {

            response.close();

        }

    }

 

}

问题:

请注意HttpEntity的writeTo(OutputStream)方法,当Http实体被写入到OutputStream后,也要确保释放系统资源。如果这个方法内调用了HttpEntity的getContent()方法,那么它会有一个java.io.InpputStream的实例,我们需要在finally中关闭这个流。但是也有这样的情况,我们只需要获取Http响应内容的一小部分,而获取整个内容并、实现连接的可重复性代价太大,这时我们可以通过关闭响应的方式来关闭内容输入、输出流。

2.1.5.2.关闭响应的方式来关闭内容输入、输出流

例:

// 2.关闭响应的方式来关闭内容输入、输出流

        CloseableHttpClient closeableHttpClient = HttpClients.createDefault();

        HttpGet httpGet = new HttpGet("http://www.baidu.com");

        CloseableHttpResponse response = closeableHttpClient.execute(httpGet);

       

        try {

            HttpEntity entity = response.getEntity();

           

            if(entity != null) {

                InputStream instream = entity.getContent();

                int byteOne=instream.read();

                int byteTwo=instream.read();

                System.out.println(byteOne);

                //Donotneedtherest

            }

           

        } finally {

            response.close();

        }

2.1.5.3.以上两种释放底层资源的区别

关闭Http实体内容流和关闭Http响应的区别在于,前者通过消耗掉Http实体内容来保持相关的http连接,然后后者会立即关闭、丢弃http连接。

2.1.6.消耗HTTP实体内容

HttpClient推荐使用HttpEntity的getConent()方法或者HttpEntity的writeTo(OutputStream)方法来消耗掉Http实体内容。HttpClient也提供了EntityUtils这个类,这个类提供一些静态方法可以更容易地读取Http实体的内容和信息。和以java.io.InputStream流读取内容的方式相比,EntityUtils提供的方法可以以字符串或者字节数组的形式读取Http实体。但是,强烈不推荐使用EntityUtils这个类,除非目标服务器发出的响应是可信任的,并且http响应实体的长度不会过大。

例:

package com.me.homesickness.practice.httpClient.requestExecution;

 

import java.io.IOException;

import java.io.InputStream;

 

import org.apache.http.HttpEntity;

import org.apache.http.client.ClientProtocolException;

import org.apache.http.client.methods.CloseableHttpResponse;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.CloseableHttpClient;

import org.apache.http.impl.client.HttpClients;

import org.apache.http.util.EntityUtils;

 

/**

 * 消耗HTTP实体内容

 * @author Administrator

 *

 */

public class RequestExecution007 {

 

    public static void main(String[] args) throws ClientProtocolException, IOException {

        CloseableHttpClient closeableHttpClient = HttpClients.createDefault();

        HttpGet httpGet = new HttpGet("http://www.baidu.com");

        CloseableHttpResponse response = closeableHttpClient.execute(httpGet);

       

        try {

            HttpEntity entity = response.getEntity();

           

            if(entity != null) {

                long len = entity.getContentLength();

                if(len != -1 && len < 2048) {

                    System.out.println(EntityUtils.toString(entity));

                } else {

                    //Streamcontentout

                }

            }

           

        } finally {

            response.close();

        }

    }

   

}

有些情况下,我们希望可以重复读取Http实体的内容。这就需要把Http实体内容缓存在内存或者磁盘上。最简单的方法就是把HttpEntity转化成BufferedHttpEntity,这样就把原Http实体的内容缓冲到了内存中。后面我们就可以重复读取BufferedHttpEntity中的内容。

例:

package com.me.homesickness.practice.httpClient.requestExecution;

 

import java.io.IOException;

import java.io.InputStream;

 

import org.apache.http.HttpEntity;

import org.apache.http.client.ClientProtocolException;

import org.apache.http.client.methods.CloseableHttpResponse;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.entity.BufferedHttpEntity;

import org.apache.http.impl.client.CloseableHttpClient;

import org.apache.http.impl.client.HttpClients;

import org.apache.http.util.EntityUtils;

 

/**

 * 消耗HTTP实体内容

 * @author Administrator

 *

 */

public class RequestExecution007 {

 

    public static void main(String[] args) throws ClientProtocolException, IOException {

        CloseableHttpClient closeableHttpClient = HttpClients.createDefault();

        HttpGet httpGet = new HttpGet("http://www.baidu.com");

        CloseableHttpResponse response = closeableHttpClient.execute(httpGet);

       

        try {

            HttpEntity entity = response.getEntity();

           

            if(entity != null) {

                long len = entity.getContentLength();

                if(len != -1 && len < 2048) {

                    System.out.println(EntityUtils.toString(entity));

                } else {

                    //Streamcontentout

                }

               

                // HTTP内容缓冲到内存中

                entity = new BufferedHttpEntity(entity);

            }

           

        } finally {

            response.close();

        }

    }

   

}

2.1.7.创建HTTP实体内容

HttpClient提供了一些类,这些类可以通过http连接高效地输出Http实体内容。HttpClient提供的这几个类涵盖的常见的数据类型,如String,byte数组,输入流,和文件类型:StringEntity,ByteArrayEntity,InputStreamEntity,FileEntity。

请注意由于InputStreamEntity只能从下层的数据流中读取一次,所以它是不能重复的。推荐,通过继承HttpEntity这个自包含的类来自定义HttpEntity类,而不是直接使用InputStreamEntity这个类。FileEntity就是一个很好的起点(FileEntity就是继承的HttpEntity)。

例:

package com.me.homesickness.practice.httpClient.requestExecution;

 

/**

 * 创建HTTP实体内容

 */

import java.io.File;

 

import org.apache.http.client.methods.HttpPost;

import org.apache.http.entity.ContentType;

import org.apache.http.entity.FileEntity;

 

public class RequestExecution008 {

 

    public static void main(String[] args) {

        File file = new File("file.txt");

        FileEntity entity = new FileEntity(file,

                ContentType.create("text/plain", "UTF-8"));

        HttpPost httpPost = new HttpPost("http://www.baidu.com");

        httpPost.setEntity(entity);

    }

   

}

2.1.8.HTML表单

很多应用程序需要模拟提交Html表单的过程,举个例子,登陆一个网站或者将输入内容提交给服务器。HttpClient提供了UrlEncodedFormEntity这个类来帮助实现这一过程。

例:

package com.me.homesickness.practice.httpClient.requestExecution;

 

import java.util.ArrayList;

import java.util.List;

 

import org.apache.http.Consts;

import org.apache.http.NameValuePair;

import org.apache.http.client.entity.UrlEncodedFormEntity;

import org.apache.http.client.methods.HttpPost;

import org.apache.http.message.BasicNameValuePair;

 

/**

 * HTML表单

 * @author Administrator

 *

 */

public class RequestExecution009 {

 

    public static void main(String[] args) {

        // TODO Auto-generated method stub

        List<NameValuePair> formparams = new ArrayList<NameValuePair>();

        formparams.add(new BasicNameValuePair("param1", "value1"));

        formparams.add(new BasicNameValuePair("param2", "value2"));

        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);

        HttpPost httppost=new HttpPost("http://www.baidu.com");

        httppost.setEntity(entity);

        // 注意:UrlEncodedFormEntity实例会使用所谓的Url编码的方式对我们的参数进行编码,产生的结果如下:

    }

 

}

2.1.9.内容分块

一般来说,推荐让HttpClient自己根据Http消息传递的特征来选择最合适的传输编码。当然,如果非要手动控制也是可以的,可以通过设置HttpEntity的setChunked()为true。请注意:HttpClient仅会将这个参数看成是一个建议。如果Http的版本(如http1.0)不支持内容分块,那么这个参数就会被忽略。

例:

package com.me.homesickness.practice.httpClient.requestExecution;

 

import org.apache.http.Consts;

import org.apache.http.client.methods.HttpPost;

import org.apache.http.entity.ContentType;

import org.apache.http.entity.StringEntity;

 

/**

 * 内容分块

 * @author Administrator

 *

 */

public class RequestExecution010 {

 

    public static void main(String[] args) {

        // TODO Auto-generated method stub

        StringEntity entity = new StringEntity("import message",

                ContentType.create("plain/text", Consts.UTF_8));

        entity.setChunked(true);

        HttpPost httppost = new HttpPost("http://www.baidu.com");

        httppost.setEntity(entity);

    }

 

}

2.1.10 ResponseHandler接口

最简单也是最方便的处理http响应的方法就是使用ResponseHandler接口,这个接口中有handleResponse(HttpResponseresponse)方法。使用这个方法,用户完全不用关心http连接管理器。当使用ResponseHandler时,HttpClient会自动地将Http连接释放给Http管理器,即使http请求失败了或者抛出了异常。

代码:

package com.me.homesickness.practice.httpClient.requestExecution;

 

import java.io.IOException;

import java.io.InputStreamReader;

import java.io.Reader;

import java.nio.charset.Charset;

 

import org.apache.http.HttpEntity;

import org.apache.http.HttpResponse;

import org.apache.http.StatusLine;

import org.apache.http.client.ClientProtocolException;

import org.apache.http.client.HttpResponseException;

import org.apache.http.client.ResponseHandler;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.entity.ContentType;

import org.apache.http.impl.client.CloseableHttpClient;

import org.apache.http.impl.client.HttpClients;

 

import com.me.homesickness.practice.httpClient.object.MyJsonObject;

 

public class RequestExecution011 {

 

    public static void main(String[] args) throws ClientProtocolException, IOException {

        CloseableHttpClient httpclient = HttpClients.createDefault();

        HttpGet httpget = new HttpGet("http://www.baidu.com");

       

        ResponseHandler<MyJsonObject> rh = new ResponseHandler<MyJsonObject>() {

 

            @Override

            public MyJsonObject handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {

                StatusLine statusLine = response.getStatusLine();

                HttpEntity entity = response.getEntity();

                if(statusLine.getStatusCode() >= 300) {

                    throw new HttpResponseException(

                            statusLine.getStatusCode(),

                            statusLine.getReasonPhrase());

                }

                if(entity == null) {

                    throw new ClientProtocolException("Responsecontainsnocontent");

                }

               

                //Gson gson=new GsonBuilder().create();

                ContentType contentType = ContentType.getOrDefault(entity);

                Charset charset=contentType.getCharset();

                Reader reader=new InputStreamReader(entity.getContent(),charset);

                //return gson.fromJson(reader,MyJsonObject.class);

                return null;

            }

           

        };

       

        MyJsonObject myJsonObject = httpclient.execute(httpget, rh);

    }

   

}

3.HttpClient接口

3.1. HttpClient接口概述

对于Http请求执行过程来说,HttpClient的接口有着必不可少的作用。HttpClient接口没有对Http请求的过程做特别的限制和详细的规定,连接管理、状态管理、授权信息和重定向处理这些功能都单独实现。这样用户就可以更简单地拓展接口的功能(比如缓存响应内容)。一般说来,HttpClient实际上就是一系列特殊的handler或者说策略接口的实现,这些handler(测试接口)负责着处理Http协议的某一方面,比如重定向、认证处理、有关连接持久性和keepalive持续时间的决策。这样就允许用户使用自定义的参数来代替默认配置,实现个性化的功能。

HttpClient已经实现了线程安全。所以希望用户在实例化HttpClient时,也要支持为多个请求使用。

代码:

package com.me.homesickness.practice.httpClient.httpClientInterface;

 

import org.apache.http.HttpResponse;

import org.apache.http.conn.ConnectionKeepAliveStrategy;

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.protocol.HttpContext;

 

/**

 * HttpClient接口

 * @author Administrator

 *

 */

public class HttpClientInterface001 {

 

    public static void main(String[] args) {

       

        ConnectionKeepAliveStrategy  keepAliveStrat = new DefaultConnectionKeepAliveStrategy() {

           

            public long getKeepAliveDuration(

                    HttpResponse response,

                    HttpContext context) {

                long keepAlive = super.getKeepAliveDuration(response, context);

                if(keepAlive == -1) {

                    keepAlive = 5000;

                }

                return keepAlive;

            }

           

        };

       

        CloseableHttpClient httpClient = HttpClients.custom()

                .setKeepAliveStrategy(keepAliveStrat)

                .build();

 

    }

 

}

3.2.HttpClient的内存分配

当一个CloseableHttpClient的实例不再被使用,并且它的作用范围即将失效,和它相关的连接必须被关闭,关闭方法可以调用CloseableHttpClient的close()方法。

package com.me.homesickness.practice.httpClient.httpClientInterface;

 

import java.io.IOException;

 

import org.apache.http.impl.client.CloseableHttpClient;

import org.apache.http.impl.client.HttpClients;

 

public class HttpClientInterface002 {

 

    public static void main(String[] args) throws IOException {

        // TODO Auto-generated method stub

        CloseableHttpClient httpClient = HttpClients.createDefault();

        try {

            // doSomeThing

        } finally {

            httpClient.close();

        }

    }

 

}

4.Http执行上下文

最初,Http被设计成一种无状态的、面向请求-响应的协议。然而,在实际使用中,我们希望能够在一些逻辑相关的请求-响应中,保持状态信息。为了使应用程序可以保持Http的持续状态,HttpClient允许http连接在特定的Http上下文中执行。如果在持续的http请求中使用了同样的上下文,那么这些请求就可以被分配到一个逻辑会话中。HTTP上下文就和一个java.util.Map<String,Object>功能类似。它实际上就是一个任意命名的值的集合。应用程序可以在Http请求执行前填充上下文的值,也可以在请求执行完毕后检查上下文。

HttpContext可以包含任意类型的对象,因此如果在多线程中共享上下文会不安全。推荐每个线程都只包含自己的http上下文。

在Http请求执行的过程中,HttpClient会自动添加下面的属性到Http上下文中:·HttpConnection的实例,表示客户端与服务器之间的连接

·HttpHost的实例,表示要连接的目标服务器

·HttpRoute的实例,表示全部的连接路由

·HttpRequest的实例,表示Http请求。在执行上下文中,最终的HttpRequest对象会代表http消息的状态。Http/1.0和Http/1.1都默认使用相对的uri。但是如果使用了非隧道模式的代理服务器,就会使用绝对路径的uri。

·HttpResponse的实例,表示Http响应

·java.lang.Boolean对象,表示是否请求被成功的发送给目标服务器·RequestConfig对象,表示httprequest的配置信息

·java.util.List<Uri>对象,表示Http响应中的所有重定向地址我们可以使用HttpClientContext这个适配器来简化和上下文交互的过程。

代码:

HttpContext context = null; //这个值一般不会是null,会有相应的来源

        HttpClientContext clientContext = HttpClientContext.adapt(context);

        HttpHost target = clientContext.getTargetHost();

        HttpRequest request = clientContext.getRequest();

        HttpResponse response = clientContext.getResponse();

        RequestConfig config = clientContext.getRequestConfig();

同一个逻辑会话中的多个Http请求,应该使用相同的Http上下文来执行,这样就可以自动地在http请求中传递会话上下文和状态信息。在下面的例子中,我们在开头设置的参数,会被保存在上下文中,并且会应用到后续的http请求中。

代码:

package com.me.homesickness.practice.httpClient.context;

 

import java.io.IOException;

 

import org.apache.http.HttpEntity;

import org.apache.http.client.ClientProtocolException;

import org.apache.http.client.config.RequestConfig;

import org.apache.http.client.methods.CloseableHttpResponse;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.CloseableHttpClient;

import org.apache.http.impl.client.HttpClients;

import org.apache.http.protocol.HttpContext;

 

/**

 * HttpClient上下文

 * @author Administrator

 *

 */

public class Context002 {

 

    public static void main(String[] args) throws ClientProtocolException, IOException {

        HttpContext context = null; // //这个值一般不会是null,会有相应的来源

        CloseableHttpClient httpClient = HttpClients.createDefault();

        RequestConfig requestConfig = RequestConfig.custom()

                .setSocketTimeout(1000)

                .setConnectTimeout(1000)

                .build();

        HttpGet httpget1 = new HttpGet("http://www.baidu.com");

        httpget1.setConfig(requestConfig);

       

        CloseableHttpResponse response1 = httpClient.execute(httpget1, context);

        try {

            HttpEntity entity1 = response1.getEntity();

        } finally {

            response1.close();

        }

       

        HttpGet httpget2=new HttpGet("http://localhost/2");

        CloseableHttpResponse response2 = httpClient.execute(httpget1, context);

        try {

            HttpEntity entity2 = response2.getEntity();

        } finally {

            response1.close();

        }

       

    }

   

}

5.HttpClient异常处理

5.1.HttpClient抛出的异常分类

HttpClient会被抛出两种类型的异常,一种是java.io.IOException,当遇到I/O异常时抛出(socket超时,或者socket被重置);另一种是HttpException,表示Http失败,如Http协议使用不正确。通常认为,I/O错误时不致命、可修复的,而Http协议错误是致命了,不能自动修复的错误。

5.2.HTTP传输安全

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

任风雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值