Apache httpclient的使用

参考:https://blog.csdn.net/wangpeng047/article/details/19624529

Http协议的重要性相信不用我多说了,HttpClient相比传统JDK自带的URLConnection,增加了易用性和灵活性(具体区别,日后我们再讨论),它不仅是客户端发送Http请求变得容易,而且也方便了开发人员测试接口(基于Http协议的),即提高了开发的效率,也方便提高代码的健壮性。因此熟练掌握HttpClient是很重要的必修内容,掌握HttpClient后,相信对于Http协议的了解会更加深入。

简介

HttpClient是Apache Jakarta Common下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP协议的客户端编程工具包,并且它支持HTTP协议最新的版本和建议。HttpClient已经应用在很多的项目中,比如Apache Jakarta上很著名的另外两个开源项目Cactus和HTMLUnit都使用了HttpClient。

下载地址: http://hc.apache.org/downloads.cgi

特性

1. 基于标准、纯净的java语言。实现了Http1.0和Http1.1

2. 以可扩展的面向对象的结构实现了Http全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)。

3. 支持HTTPS协议。

4. 通过Http代理建立透明的连接。

5. 利用CONNECT方法通过Http代理建立隧道的https连接。

6. Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO/Kerberos认证方案。

7. 插件式的自定义认证方案。

8. 便携可靠的套接字工厂使它更容易的使用第三方解决方案。

9. 连接管理器支持多线程应用。支持设置最大连接数,同时支持设置每个主机的最大连接数,发现并关闭过期的连接。

10. 自动处理Set-Cookie中的Cookie。

11. 插件式的自定义Cookie策略。

12. Request的输出流可以避免流中内容直接缓冲socket服务器。

13. Response的输入流可以有效的从socket服务器直接读取相应内容。

14. 在http1.0和http1.1中利用KeepAlive保持持久连接。

15. 直接获取服务器发送的response code和 headers。

16. 设置连接超时的能力。

17. 实验性的支持http1.1 response caching。

18. 源代码基于Apache License 可免费获取。

使用方法

使用HttpClient发送请求、接收响应很简单,一般需要如下几步即可。

1. 创建HttpClient对象。

2. 创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。

3. 如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。

4. 调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse。

5. 调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。

6. 释放连接。无论执行方法是否成功,都必须释放连接

实际问题

需要通过http请求其他系统的数据,在获取数据之前需要进行用户认证。


不熟悉httpClient,也不知道如何使用里面一些类。只需要在请求的头部设置Authorization,就这样就可以了。

public static String get(String url, String userName, String passWd) {

        DefaultHttpClient httpClient = new DefaultHttpClient();
        try {
            HttpUriRequest httpReq = new HttpGet(url);

            StringBuilder sb = new StringBuilder(userName);
            sb.append(":").append(passWd);
            String encoding = Base64.encodeBase64String(sb.toString().getBytes());
            httpReq.setHeader("Authorization", "Basic " + encoding);
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            String reponse = httpClient.execute(httpReq, responseHandler);
            return reponse;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            httpClient.getConnectionManager().shutdown();
        }
    }

这里使用的直接在请求的认证头部加上Authorization。要实现 HTTP 身份认证,无论是 WWW 认证也好,Proxy 认证也罢,其实只需要在 HTTP 的请求头部添加一个认证的头部(Authorization 或者 Proxy-Authorization)。认证头部的信息就是用户名和密码,将用户名和密码使用冒号分割,然后再对其进行 Base64 编码即可。参考:https://blog.csdn.net/u012486840/article/details/52537321

这里可以看出我们用的是web容器认证,如果是代理认证,那么我的代码就会有问题。这里讲解更好,而且有些问题我还没研究过

关于 HTTP 的身份认证

我们这里给代理服务器设置了用户名和密码之后,无论在程序中,还是在浏览器里使用该代理时,都需要进行身份认证了。HTTP 协议最常见的认证方式有两种:基本认证(Basic authentication)和摘要认证(Digest authentication)。HTTP 的认证模型非常简单,就是所谓的质询/响应(challenge/response)框架:当用户向服务器发送一条 HTTP 请求报文时,服务器首先回复一个“认证质询”响应,要求用户提供身份信息,然后用户再一次发送 HTTP 请求报文,这次的请求头中附带上身份信息(用户名密码),如果身份匹配,服务器则正常响应,否则服务器会继续对用户进行质询或者直接拒绝请求。

摘要认证的实现比基本认证要复杂一点,在平时的使用中也并不多见,这里忽略,如果想详细了解它,可以查看维基百科上关于 HTTP 摘要认证 的解释。这里重点介绍下 HTTP 基本认证,因为无论是代理服务器对用户进行认证,还是 Web 服务器对用户进行认证,最常用的手段都是 HTTP 基本认证,它实现简单,容易理解,几乎所有的服务器都能支持它。

一个典型的 HTTP 基本认证,如下图所示,图片摘自《HTTP 权威指南》

http-basic-auth.png


get和post都可以这样来做。这里我使用的是4.2的版本的jar包,发现不同版本之间的差别还是挺大的,使用可以注意自己所用的版本。

参考地址:https://www.cnblogs.com/kobe08/p/6074904.html?utm_source=itdadao&utm_medium=referral
org.apache.http.impl.client.DefaultHttpClient这个主要是用来发送请求的执行体,并且可以设置请求和响应的拦截等。 apache.commons.httpclient.HttpClient 已不推荐使用
org.apache.http.client.HttpClient 不推荐使用现在的版本 HttpClient httpClient=new  DefaultHttpClient(); 
建议使用最新版本 CloseableHttpClient httpclient = HttpClients.createDefault();
org.apache.http.auth.AuthScope这个主要是来设置一些认证信息的。
org.apache.http.auth.UsernamePasswordCredentials设置认证的用户名和密码。

httpClient.getCredentialsProvider().setCredentials(authScope, credentials);在请求执行中设置用户凭证。

AuthCache authCache = new BasicAuthCache();设置认证缓存

BasicScheme basicScheme = new BasicScheme();认证方式,这是basi基本认证
HttpHost targetHost = new HttpHost("10.255.12.157", 9200, "http");设置目标请求的主机
authCache.put(targetHost, basicScheme);
BasicHttpContext localContext = new BasicHttpContext();认证上下文
localContext.setAttribute(ClientContext.AUTH_CACHE, authCache);

基于httpClient的http基本认证:

public static String get(String url, String userName, String passWd) {

        DefaultHttpClient httpClient = new DefaultHttpClient();

        try {
            HttpUriRequest httpReq = new HttpGet(url);
            // http基本认证
            AuthScope authScope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT);
            UsernamePasswordCredentials credentials =
                    new UsernamePasswordCredentials(userName, passWd);
            httpClient.getCredentialsProvider().setCredentials(authScope, credentials);
            AuthCache authCache = new BasicAuthCache();
            BasicScheme basicScheme = new BasicScheme();
            URI uri = new URI(url);
            HttpHost targetHost = new HttpHost(uri.getHost(), uri.getPort(), "http");
            authCache.put(targetHost, basicScheme);
            BasicHttpContext localContext = new BasicHttpContext();
            localContext.setAttribute(ClientContext.AUTH_CACHE, authCache);

            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            String reponse = httpClient.execute(httpReq, responseHandler, localContext);
            System.out.println(reponse);
            // System.out.println(reponse.getStatusLine());
            return reponse.toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            httpClient.getConnectionManager().shutdown();
        }
    }


http请求和响应讲解

参考:https://blog.csdn.net/wuyou1336/article/details/53769043

核心对象httpClient的调用非常直观,其execute方法传入一个request对象,返回一个response对象。
使用 httpClient发出HTTP请求时,系统可能抛出两种异常,分别是ClientProtocolException和IOException。
第一种异常的发生通常是协议错误导致,如在构造HttpGet对象时传入的协议不对(例如不小心将”http”写成”htp”),或者服务器端返回的内容不符合HTTP协议要求等;
第二种异常一般是由于网络原因引起的异常,如HTTP服务器未启动等。

从实际应用的角度看,HTTP协议由两大部分组成:HTTP请求和HTTP响应。那么HttpClient程序包是如何实现HTTP客户端应用的呢?实现过程中需要注意哪些问题呢?
一 、HTTP请求
 

HTTP1.1由以下几种请求组成:GET,HEAD, POST, PUT, DELETE, TRACE ,OPTIONS,

因此对应到HttpClient程序包中分别用HttpGet,HttpHead, HttpPost, HttpPut, HttpDelete, HttpTrace, HttpOptions 这几个类来创建请求。

所有的这些类均实现了HttpUriRequest接口,故可以作为execute的执行参数使用。


所有请求中最常用的是GET与POST两种请求,与创建GET请求的方法相同,可以用如下方法创建一个POST请求:
 
[java]  view plain  copy
  1. HttpUriRequest request = new HttpPost(  
  2.         "http://localhost/index.html");  

HTTP请求格式告诉我们,有两个位置或者说两种方式可以为request提供参数: request-line方式与request-body 方式。

    request-line
 
request-line方式是指在请求行上通过URI直接提供参数。

(1)
我们可以在生成request对象时提供带参数的URI,如:
[java]  view plain  copy
  1.    
  2. HttpUriRequest request = new HttpGet(  
  3.         "http://localhost/index.html?param1=value1¶m2=value2");  


(2)
另外,HttpClient程序包为我们提供了URIUtils工具类,可以通过它生成带参数的URI,如:
 
[java]  view plain  copy
  1. URI uri = URIUtils.createURI("http""localhost", -1"/index.html",  
  2.     "param1=value1¶m2=value2"null);  
  3. HttpUriRequest request = new HttpGet(uri);  
  4. System.out.println(request.getURI());  

上例的打印结果如下:
 
[html]  view plain  copy
  1. http://localhost/index.html?param1=value1m2=value2  

(3)
需要注意的是,如果参数中含有中文,需将参数进行URLEncoding处理,如:
 
[java]  view plain  copy
  1. String param = "param1=" + URLEncoder.encode("中国""UTF-8") + "¶m2=value2";  
  2. URI uri = URIUtils.createURI("http""localhost"8080,  
  3. "/sshsky/index.html", param, null);  
  4. System.out.println(uri);  


上例的打印结果如下:
 
[html]  view plain  copy
  1. http://localhost/index.html?param1=%E4%B8%AD%E5%9B%BD¶m2=value2  

 
(4)
对于参数的URLEncoding处理,HttpClient程序包为我们准备了另一个工具类:URLEncodedUtils。通过它,我们可以直观的(但是比较复杂)生成URI,如:
[java]  view plain  copy
  1.    
  2. List params = new ArrayList();  
  3. params.add(new BasicNameValuePair("param1""中国"));  
  4. params.add(new BasicNameValuePair("param2""value2"));  
  5. String param = URLEncodedUtils.format(params, "UTF-8");  
  6. URI uri = URIUtils.createURI("http""localhost"8080,  
  7. "/sshsky/index.html", param, null);  
  8. System.out.println(uri);  

 
上例的打印结果如下:
 
[html]  view plain  copy
  1. http://localhost/index.html?param1=%E4%B8%AD%E5%9B%BD¶m2=value2  

request-body
 
与request-line方式不同,request-body方式是在request-body中提供参数,此方式只能用于POST请求。
在 HttpClient程序包中有两个类可以完成此项工作,它们分别是UrlEncodedFormEntity类与MultipartEntity类。还有StringEntity类
这两个类均实现了HttpEntity接口。
(1)
使用最多的是UrlEncodedFormEntity类。通过该类创建的对象可以模拟传统的HTML表单传送POST请求中的参数。如下面的表单:
 
[html]  view plain  copy
  1. <form action="http://localhost/index.html" method="POST">  
  2.     <input type="text" name="param1" value="中国"/>  
  3.     <input type="text" name="param2" value="value2"/>  
  4.     <inupt type="submit" value="submit"/>  
  5. </form>  

我们可以用下面的代码实现:
 
[java]  view plain  copy
  1. List formParams = new ArrayList();  
  2. formParams.add(new BasicNameValuePair("param1""中国"));  
  3. formParams.add(new BasicNameValuePair("param2""value2"));  
  4. HttpEntity entity = new UrlEncodedFormEntity(formParams, "UTF-8");  
  5.    
  6. HttpPost request = new HttpPost(“http://localhost/index.html”);  
  7. request.setEntity(entity);  
当然,如果想查看HTTP数据格式,可以通过HttpEntity对象的各种方法取得(我觉得很有用,可以帮助查看问题)。如:
[java]  view plain  copy
  1.    
  2. List formParams = new ArrayList();  
  3. formParams.add(new BasicNameValuePair("param1""中国"));  
  4. formParams.add(new BasicNameValuePair("param2""value2"));  
  5. UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, "UTF-8");  
  6.    
  7. System.out.println(entity.getContentType());  
  8. System.out.println(entity.getContentLength());  
  9. System.out.println(EntityUtils.getContentCharSet(entity));  
  10. System.out.println(EntityUtils.toString(entity));  
上例的打印结果如下:
 
[html]  view plain  copy
  1. Content-Type: application/x-www-form-urlencoded; charset=UTF-8  
  2. 39  
  3. UTF-8  
  4. param1=%E4%B8%AD%E5%9B%BD¶m2=value2   
(2)
除了传统的application/x-www-form-urlencoded表单,我们另一个经常用到的是上传文件用的表单,这种表单的类型为  multipart/form-data
在HttpClient程序扩展包( HttpMime )中专门有一个类与之对应,那就是 MultipartEntity类。此类同样实现了HttpEntity接口。如下面的表单:
 
[html]  view plain  copy
  1. <form action="http://localhost/index.html" method="POST"  
  2.         enctype="multipart/form-data">  
  3.     <input type="text" name="param1" value="中国"/>  
  4.     <input type="text" name="param2" value="value2"/>  
  5.     <input type="file" name="param3"/>  
  6.     <inupt type="submit" value="submit"/>  
  7. </form>  


我们可以用下面的代码实现:

[java]  view plain  copy
  1.    
  2. MultipartEntity entity = new MultipartEntity();  
  3. entity.addPart("param1"new StringBody("中国", Charset.forName("UTF-8")));  
  4. entity.addPart("param2"new StringBody("value2", Charset.forName("UTF-8")));  
  5. entity.addPart("param3"new FileBody(new File("C:\\1.txt")));  
  6.    
  7. HttpPost request = new HttpPost(“http://localhost/index.html”);  
  8. request.setEntity(entity);  

二、HTTP响应

HttpClient 程序包对于HTTP响应的处理较请求来说简单多了,其过程同样使用了HttpEntity接口。
我们可以从HttpEntity对象中取出数据流(InputStream),该数据流就是服务器返回的响应数据。
需要注意的是,HttpClient程序包不负责 解析数据流中的内容。如:
[java]  view plain  copy
  1. HttpUriRequest request = ...;    
  2.     
  3. HttpResponse response =httpClient.execute(request);    
  4.     
  5. // 从response中取出HttpEntity对象     
  6.     
  7. HttpEntity entity =response.getEntity();    
  8.     
  9. // 查看entity的各种指标     
  10.     
  11. System.out.println(entity.getContentType());    
  12.     
  13. System.out.println(entity.getContentLength());    
  14.     
  15. System.out.println(EntityUtils.getContentCharSet(entity));    
  16.     
  17. // 取出服务器返回的数据流     
  18.     
  19. InputStream stream =entity.getContent();    

或者采用如下的接口方式httpClient.execute(request,new ResponseHandler<T> response)进行调用,它的返回值直接对应的即为用户自己想获取的数据的类型及值。

问题

在post请求中我们在请求头部设置认证信息,通过认证后请求发过去,发现对方取不到body里的请求参数,需要采用上面的httpClient的认证方式才可以解决问题。我不知道是啥问题,望大牛指点。

有问题的代码:

public static String postJson(String url, String json, String userName, String passWd,
            int timeOut) {

        DefaultHttpClient httpClient = new DefaultHttpClient();
        httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, timeOut); // 连接超时
        httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, timeOut); // 读取超时
        HttpPost httpPost = new HttpPost(url);
        try {
            StringBuilder sb = new StringBuilder(userName);
            sb.append(":").append(passWd);
            String encoding = Base64.encodeBase64String(sb.toString().getBytes());
            httpPost.setHeader("Authorization", "Basic " + encoding);
            // http基本认证
            // BasicHttpContext localContext = authentication(url, userName, passWd, httpClient);

            StringEntity entity = new StringEntity(json, "utf-8");
            entity.setContentEncoding("UTF-8");
            entity.setContentType("application/json");
            httpPost.setEntity(entity);
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            logger.error(httpPost.getEntity());
            logger.error(EntityUtils.toString(entity, "utf-8"));
            String reponse = httpClient.execute(httpPost, responseHandler);
            return reponse;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            httpClient.getConnectionManager().shutdown();
        }
    }
服务端web容器设置用户认证参考

猜你喜欢

转载自blog.csdn.net/jiang2011tao/article/details/79919332