HttpClient -- Request execution

来源:互联网 发布:jsp导入java类 编辑:程序博客网 时间:2024/05/29 07:42

HttpClient – Request execution

在实际的项目中,网络环境比较复杂,尽管java.net提供了通过HTTP协议访问资源的基本功能,但代码量非常大,需要处理HTTP返回的状态码,设置HTTP代理,处理HTTPS协议等工作。HttpClient则封装了这些操作,简化开发。

HttpClient不是一个浏览器,它只是一个HTTP报文(HTTP message)传输的客户端库,HttpClient的目的是为了发送和接受HTTP报文,所以HttpClient不会处理镶嵌在HTML页面里的javascript代码。

基本功能

HttpClient最基本的功能就是执行HTTP方法,HTTP方法包括一个或多个HTTP request / HTTP response。只要用户提供请求对象(request object),HttpClient会负责传输这个请求对象到目标服务器并且返回一个相应的返回对象(response object)。

下面是请求执行过程的一个例子

CloseableHttpClient httpclient = HttpClients.createDefault();HttpGet httpget = new HttpGet("http://localhost/");CloseableHttpResponse response = httpclient.execute(httpget);try {    <...>} finally {    response.close();}

HTTP request

一个HTTP请求行包括方法名,请求的URI,以及HTTP协议版本号。HttpClient支持所有定义在HTTP/1.1规范里的方法,分别有:GET,HEAD,POST,PUT,DELETE,TRACE和OPTIONS。HttpClient。

请求URI是统一资源标志符(Uniform Resource Identifier),由请求协议,主机名,可选端口号,资源路径以及可选查询参数组成。

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

HttpClient提供了URIBuilder实体类来简化URI的创建和修改。

URI uri = new URIBuilder()        .setScheme("http")        .setHost("www.google.com")        .setPath("/search")        .setParameter("q", "httpclient")        .setParameter("btnG", "Google Search")        .setParameter("aq", "f")        .setParameter("oq", "")        .build();HttpGet httpget = new HttpGet(uri);System.out.println(httpget.getURI());

输出 >

http://www.google.com/search?q=httpclient&btnG=Google+Search&aq=f&oq=

HTTP response

HTTP response 是服务器处理客户端请求后返回的报文。报文的第一行包括协议版本号,HTTP状态码以及文字描述。

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());

输出 >

HTTP/1.1200OKHTTP/1.1 200 OK

Message headers

一个HTTP报文可以包括一系列用来描述HTTP报文的属性的头部,例如Content-TypeContent-Length等等。HttpClient提供一系列方法来检索,添加,移除和列举头部。

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 h1 = response.getFirstHeader("Set-Cookie");System.out.println(h1);Header h2 = response.getLastHeader("Set-Cookie");System.out.println(h2);Header[] hs = response.getHeaders("Set-Cookie");System.out.println(hs.length);

输出 >

Set-Cookie: c1=a; path=/; domain=localhostSet-Cookie: c2=b; path="/", c3=c; domain="localhost"2

HTTP entity

HTTP报文可以携带和请求和响应相关的内容实体。实体可以在一些请求和响应中找到,因为它们也是可选的。使用了实体的请求被称为封闭实体请求。HTTP规范定义了两种封闭实体的方法:POST和PUT。响应通常期望包含一个内容实体。这个规则也有特例,比如HEAD方法的响应和204 NoContent,304 Not Modified和205 Reset Content响应。

HttpClient根据其内容出自何处区分三种类型的实体:
* streamed流式:内容从流中获得,或者在运行中产生。特别是这种分类包含从HTTP响应中获取的实体。流式实体是不可重复生成的。
* self-contained自包含式:内容在内存中或通过独立的连接或其它实体中获得。自我包含式的实体是可以重复生成的。这种类型的实体会经常用于封闭HTTP请求的实体。
* wrapping包装式:内容从另外一个实体中获得。

当从一个HTTP响应中获取流式内容时,这个区别对于连接管理很重要。对于由应用程序创建而且只使用HttpClient发送的请求实体,流式和自我包含式的不同就不那么重要了。这种情况下,建议考虑如流式这种不能重复的实体,和可以重复的自我包含式实体。

可重复实体

一个实体可以重复意味着它的内容可以多次读取,只有自包含式实体才可以重复。

使用HTTP实体

实体是在使用封闭内容执行请求,或当请求已经成功执行,或当响应体结果发送到客户端时创建的。

要读取实体的内容,可以通过HttpEntity#getContent()方法从输入流中获取,或者通过HttpEntity#writeTo(OutputStream)方法写入到指定的输出流中。

当从HTTP报文中提取到实体时,可以通过HttpEntity#getContentType()方法和HttpEntity#getContentLength()方法来读取通用元数据例如Content-TypeContent-Length。因为Content-Type头部包含对文本MIME类型的字符编码,可以通过HttpEntity#getContentEncoding()获取字符编码信息。如果该头部信息不可用,会返回长度-1并且内容类型返回NULL。如果该头部信息可用,会返回一个Header对象。

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);

输出 >

Content-Type: text/plain; charset=utf-817important message17

确保低级别资源释放

在使用了响应和与内容流之后必须要关闭,这样确保了系统资源的释放。

CloseableHttpClient httpclient = HttpClients.createDefault();HttpGet httpget = new HttpGet("http://localhost/");CloseableHttpResponse response = httpclient.execute(httpget);try {    HttpEntity entity = response.getEntity();    if (entity != null) {        InputStream instream = entity.getContent();        try {            // do something useful        } finally {            instream.close();        }    }} finally {    response.close();}

关闭内容流和关闭响应的的区别在与前者会试图保持与实体的底层连接而后者会立即关闭并且舍弃连接。

当处理流实体时,可以使用EntityUtils#consume(HttpEntity)方法确保实体内容已经被读取完并且底层流已经被关闭。

这里有一种特殊情况,当只有实体的一部分内容需要读取,并且读取剩余的内容的开销大,连接可重用代价太高,可以通过关闭响应来终止相关流。

CloseableHttpClient httpclient = HttpClients.createDefault();HttpGet httpget = new HttpGet("http://localhost/");CloseableHttpResponse response = httpclient.execute(httpget);try {    HttpEntity entity = response.getEntity();    if (entity != null) {        InputStream instream = entity.getContent();        int byteOne = instream.read();        int byteTwo = instream.read();        // Do not need the rest    }} finally {    response.close();}

这种情况下连接是不可重用的,但所有资源都被正确释放了。

读取实体内容

读取实体内容的推荐使用HttpEntity#getContent()或者HttpEntity#writeTo(OutputStream)方法。HttpClient还提供了EntityUtils类,该类提供了很多静态方法,可以从实体中读取内容并转换成String / byte array类型。然而,官方不推荐使用这个类除非响应实体是来自一个可信的HTTP服务器并且是已知有限长度的。

CloseableHttpClient httpclient = HttpClients.createDefault();HttpGet httpget = new HttpGet("http://localhost/");CloseableHttpResponse response = httpclient.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 {            // Stream content out        }    }} finally {    response.close();}

有时可能需要多次读取实体内容。在这种情况中,实体内容必须通过某种形式在内存或者硬盘中缓存起来,最简单的方式是通过BufferedHttpEntity类包装原来的实体,使得原来的实体写进内存缓存中。

CloseableHttpResponse response = <...>HttpEntity entity = response.getEntity();if (entity != null) {    entity = new BufferedHttpEntity(entity);}

生成实体内容

HttpClient提供一些类,它们可以用来流输出内容。这些类的实例与实体闭包请求有关,HttpClient为一些通用数据容器(String,byte array,input stream and file)提供了数个类,StringEntity, ByteArrayEntity, InputStreamEntity, 和 FileEntity.

File file = new File("somefile.txt");FileEntity entity = new FileEntity(file,     ContentType.create("text/plain", "UTF-8"));        HttpPost httppost = new HttpPost("http://localhost/action.do");httppost.setEntity(entity);

注意:InputStreamEntity是不可重复的,它只能被底层数据流读取一次。推荐使用自包含类HttpEntity代替类InputStreamEntity和类FileEntity

HTTP表单

许多应用需要模拟HTTP表单的提交过程,HttpClient提供了实体类UrlEncodedFormEntity来完成这个操作。

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://localhost/handler.do");httppost.setEntity(entity);

URL编码会产生以下内容

param1=value1&param2=value2

内容分块(Content chunking)

通常情况下,推荐让HttpClient通过传输的HTTP报文属性选择最合适的编码传输。如果可能,通过设置HttpEntity#setChunked()为true提示HttpClient分块编码。然而,当使用不支持分块编码的HTTP协议时,例如HTTP/1.0,HttpClient会忽略这个值。

StringEntity entity = new StringEntity("important message",        ContentType.create("plain/text", Consts.UTF_8));entity.setChunked(true);HttpPost httppost = new HttpPost("http://localhost/acrtion.do");httppost.setEntity(entity);

响应处理

官方推荐使用ResponseHandler接口来处理响应,这个接口里面包含handleResponse(HttpResponse response)方法,这个方法减轻用户对于连接管理的担忧。当使用ResponseHandler接口时,HttpClient会自动处理,确保相关连接资源的释放,不管该请求执行是否成功。

CloseableHttpClient httpclient = HttpClients.createDefault();HttpGet httpget = new HttpGet("http://localhost/json");ResponseHandler<MyJsonObject> rh = new ResponseHandler<MyJsonObject>() {    @Override    public JsonObject handleResponse(            final HttpResponse response) throws 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("Response contains no content");        }        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);    }};MyJsonObject myjson = client.execute(httpget, rh);

参考链接:
1、HttpComponents —— HTTP实体(HttpEntity)
2、HttpClient Tutorial

1 0
原创粉丝点击