参考: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 权威指南》:
get和post都可以这样来做。这里我使用的是4.2的版本的jar包,发现不同版本之间的差别还是挺大的,使用可以注意自己所用的版本。
org.apache.http.client.HttpClient 不推荐使用现在的版本 HttpClient httpClient=new DefaultHttpClient();
AuthCache authCache = new BasicAuthCache();设置认证缓存
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(); } }
参考:https://blog.csdn.net/wuyou1336/article/details/53769043
HTTP1.1由以下几种请求组成:GET,HEAD, POST, PUT, DELETE, TRACE ,OPTIONS,
因此对应到HttpClient程序包中分别用HttpGet,HttpHead, HttpPost, HttpPut, HttpDelete, HttpTrace, HttpOptions 这几个类来创建请求。
所有的这些类均实现了HttpUriRequest接口,故可以作为execute的执行参数使用。
- HttpUriRequest request = new HttpPost(
- "http://localhost/index.html");
- HttpUriRequest request = new HttpGet(
- "http://localhost/index.html?param1=value1¶m2=value2");
- URI uri = URIUtils.createURI("http", "localhost", -1, "/index.html",
- "param1=value1¶m2=value2", null);
- HttpUriRequest request = new HttpGet(uri);
- System.out.println(request.getURI());
- http://localhost/index.html?param1=value1¶m2=value2
- String param = "param1=" + URLEncoder.encode("中国", "UTF-8") + "¶m2=value2";
- URI uri = URIUtils.createURI("http", "localhost", 8080,
- "/sshsky/index.html", param, null);
- System.out.println(uri);
- http://localhost/index.html?param1=%E4%B8%AD%E5%9B%BD¶m2=value2
- List params = new ArrayList();
- params.add(new BasicNameValuePair("param1", "中国"));
- params.add(new BasicNameValuePair("param2", "value2"));
- String param = URLEncodedUtils.format(params, "UTF-8");
- URI uri = URIUtils.createURI("http", "localhost", 8080,
- "/sshsky/index.html", param, null);
- System.out.println(uri);
- http://localhost/index.html?param1=%E4%B8%AD%E5%9B%BD¶m2=value2
- <form action="http://localhost/index.html" method="POST">
- <input type="text" name="param1" value="中国"/>
- <input type="text" name="param2" value="value2"/>
- <inupt type="submit" value="submit"/>
- </form>
- List formParams = new ArrayList();
- formParams.add(new BasicNameValuePair("param1", "中国"));
- formParams.add(new BasicNameValuePair("param2", "value2"));
- HttpEntity entity = new UrlEncodedFormEntity(formParams, "UTF-8");
- HttpPost request = new HttpPost(“http://localhost/index.html”);
- request.setEntity(entity);
- List formParams = new ArrayList();
- formParams.add(new BasicNameValuePair("param1", "中国"));
- formParams.add(new BasicNameValuePair("param2", "value2"));
- UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, "UTF-8");
- System.out.println(entity.getContentType());
- System.out.println(entity.getContentLength());
- System.out.println(EntityUtils.getContentCharSet(entity));
- System.out.println(EntityUtils.toString(entity));
- Content-Type: application/x-www-form-urlencoded; charset=UTF-8
- 39
- UTF-8
- param1=%E4%B8%AD%E5%9B%BD¶m2=value2
- <form action="http://localhost/index.html" method="POST"
- enctype="multipart/form-data">
- <input type="text" name="param1" value="中国"/>
- <input type="text" name="param2" value="value2"/>
- <input type="file" name="param3"/>
- <inupt type="submit" value="submit"/>
- </form>
- MultipartEntity entity = new MultipartEntity();
- entity.addPart("param1", new StringBody("中国", Charset.forName("UTF-8")));
- entity.addPart("param2", new StringBody("value2", Charset.forName("UTF-8")));
- entity.addPart("param3", new FileBody(new File("C:\\1.txt")));
- HttpPost request = new HttpPost(“http://localhost/index.html”);
- request.setEntity(entity);
二、HTTP响应
- HttpUriRequest request = ...;
- HttpResponse response =httpClient.execute(request);
- // 从response中取出HttpEntity对象
- HttpEntity entity =response.getEntity();
- // 查看entity的各种指标
- System.out.println(entity.getContentType());
- System.out.println(entity.getContentLength());
- System.out.println(EntityUtils.getContentCharSet(entity));
- // 取出服务器返回的数据流
- 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容器设置用户认证参考