HttpClient(七)
一、定义
1、什么是HttpClient?在什么场景要用到HttpClient?
http协议可以说是现在Internet上面最重要,使用最多的协议之一了,越来越多的java应用需要使用http协议来访问网络资源,特别是现在rest api的流行,HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient,很多的java的爬虫也是通过HttpClient实现的,研究HttpClient对我们来说是非常重要的。
2、HttpClient不是浏览器
很多人觉得既然HttpClient是一个HTTP客户端编程工具,很多人把他当做浏览器来理解,但是其实HttpClient不是浏览器,它是一个HTTP通信库,因此它只提供一个通用浏览器应用程序所期望的功能子集,最根本的区别是HttpClient中没有用户界面,浏览器需要一个渲染引擎来显示页面,并解释用户输入,例如鼠标点击显示页面上的某处,有一个布局引擎,计算如何显示HTML页面,包括级联样式表和图像。javascript解释器运行嵌入HTML页面或从HTML页面引用的javascript代码。来自用户界面的事件被传递到javascript解释器进行处理。除此之外,还有用于插件的接口,可以处理Applet,嵌入式媒体对象(如pdf文件,Quicktime电影和Flash动画)或ActiveX控件(可以执行任何操作)。HttpClient只能以编程的方式通过其API用于传输和接受HTTP消息。HttpClient也是完全内容不可知的。
另一个主要区别是对错误输入或HTTP标准违规的容忍。 需要允许无效的用户输入,以使浏览器用户友好。 还需要对从服务器检索的畸形文档的容忍度,以及在执行协议时服务器行为的缺陷,使尽可能多的用户可访问的网站。 然而,HttpClient努力在默认情况下尽可能接近并遵守HTTP标准规范和相关标准。 它还提供了一些手段来放松规范所施加的一些限制,这些限制允许或要求与不兼容的HTTP源或代理服务器兼容。
3、HttpClient入门使用
注意这个版本主要是基于HttpClient4.5.2版本的来讲解的,也是现在最新的版本,之所以要提供版本说明的是因为HttpClient 3版本和HttpClient 4版本差别还是很多大的,基本HttpClient里面的接口都变了,你把HttpClient 3版本的代码拿到HttpClient 4上面都运行不起来,会报错的。所以这儿一定要注意,好了废话不多说了,开始。
4、依赖
<!--httpclient的接口基本都在这儿--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5</version> </dependency> <!--httpclient缓存--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient-cache</artifactId> <version>4.5</version> </dependency> <!--http的mime类型都在这里面--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpmime</artifactId> <version>4.5</version> </dependency> <dependency> <groupId>commons-httpclient</groupId> <artifactId>commons-httpclient</artifactId> <version>3.1</version> </dependency>
二、POST代码
1、工具类
package com.asd.reserve.utils.httpclient; import com.alibaba.fastjson.JSONObject; import org.apache.commons.httpclient.*; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.StringRequestEntity; import org.apache.commons.httpclient.params.HttpClientParams; import org.apache.commons.httpclient.params.HttpMethodParams; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.io.IOException; import java.net.URLDecoder; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 准备金系统 * HTTP处理工具 * Created by ZS on 2019/12/25 */ public class HttpClientUtils { /** 连接超时时间,毫秒 **/ private static final int connectionTimeout = 2000 * 1000; private static final int RETRY_COUNT = 3; private static final String CONTENT_TYPE = "text/xml"; private static final boolean SENT_RETRY = true; /** 请求编码格式 **/ private static final String REQUEST_ENCODE = "GBK"; /** 返回编码格式 **/ private static final String RESPONSE_ENCODE = "GBK"; /** 读取数据超时时间,毫秒 **/ private static int soTimeout = 30000; /** HttpClient对象 **/ private static CloseableHttpClient httpclient = HttpClients.custom().disableAutomaticRetries().build(); /** 超时设置:设置请求和传输超时时间*/ private static RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(soTimeout).setConnectTimeout(connectionTimeout).build(); /** * 读取request请求中的信息(数据封装方式为报文) * @param request 请求 * @return * @throws IOException */ public static String getRequestMessage(HttpServletRequest request) throws IOException { // String requestUI = request.getRequestURI(); List ioList = IOUtils.readLines(request.getInputStream()); //从Request中读取请求报文 String clientIP = request.getRemoteHost(); String clientURL = request.getRequestURL().toString(); HttpSession session = request.getSession(); String id = (session != null) ? session.getId().toUpperCase() : ""; String infoPre = "[CommServer:" + id + "]"; System.out.println("服务器提示信息:" + infoPre + " Begin - IP: " + clientIP + "; URI: " + clientURL); String message = message(ioList); return message; } /** * 读取request请求中的信息(数据封装方式为报文),自定义编码格式 * @param request 请求 * @return * @throws IOException */ public static String getRequestMessage(HttpServletRequest request,String encoding) throws IOException { // String requestUI = request.getRequestURI(); List ioList = IOUtils.readLines(request.getInputStream(), encoding);//从Request中读取请求报文 String clientIP = request.getRemoteHost(); String clientURL = request.getRequestURL().toString(); HttpSession session = request.getSession(); String id = (session != null) ? session.getId().toUpperCase() : ""; String infoPre = "[CommServer:" + id + "]"; System.out.println("服务器提示信息:" + infoPre + " Begin - IP: " + clientIP + "; URI: " + clientURL); String message = message(ioList); return message; } public static String message(List ioList){ StringBuilder sb = null; String message = null; try{ sb = new StringBuilder(4096); for (int i = 0; i < ioList.size(); ++i) { sb.append(ioList.get(i)); } message = sb.toString(); }catch (Exception e){ e.printStackTrace(); } return message; } /** * 通过post发送请求 * @param url 发送的地址 * @param xmlString 发送的报文 * @return * @throws IOException */ public static String httpPostSend(String url, String xmlString) throws IOException,HttpException { HttpClientParams httpClientParams = new HttpClientParams(); httpClientParams.setConnectionManagerTimeout(connectionTimeout); httpClientParams.setSoTimeout(connectionTimeout); MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); HttpClient httpClient = new HttpClient(httpClientParams, connectionManager); PostMethod postMethod = new PostMethod(url); postMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(RETRY_COUNT, SENT_RETRY)); String result = ""; // - 参数 postMethod.setRequestEntity(new StringRequestEntity(xmlString, CONTENT_TYPE, REQUEST_ENCODE)); postMethod.addRequestHeader("Connection", "close"); // - 执行 int statusCode = httpClient.executeMethod(postMethod); // - 返回 // 判断是否链接url成功 if (statusCode != HttpStatus.SC_OK) { throw new HttpException("连接失败"); } else { // 得到返回值 byte[] bodydata = postMethod.getResponseBody(); result = new String(bodydata, RESPONSE_ENCODE); } //postMethod.releaseConnection(); return result; } /** * HttpClient post发送请求 *@Author: zs on 2019/11/20 16:46 *@param: url:远程地址 addr:方法 json:请求参数json串 *@return: *@Description: */ public static String httpPostSend(String url,String addr, String json) throws IOException,HttpException{ HttpClientParams httpClientParams = new HttpClientParams(); httpClientParams.setConnectionManagerTimeout(1000*3600); httpClientParams.setSoTimeout(1000*3600); MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); HttpClient httpClient = new HttpClient(httpClientParams, connectionManager); long s = System.currentTimeMillis(); PostMethod postMethod = new PostMethod(url + addr); //参数设置 //postMethod.setRequestBody(json); postMethod.setRequestEntity(new StringRequestEntity(json, "application/json", "UTF-8")); postMethod.addRequestHeader("Content-Type", "application/json;charset=UTF-8"); postMethod.addRequestHeader("Referer", "123"); //执行 int statusCode = httpClient.executeMethod(postMethod); long t = System.currentTimeMillis(); // - 返回 String response = ""; // 判断是否链接url成功 if (statusCode != HttpStatus.SC_OK) { throw new HttpException("连接失败"); } else { // 得到返回值 //String responseCharSet = postMethod.getResponseCharSet(); //response = postMethod.getResponseBodyAsString(); byte[] bodydata = postMethod.getResponseBody(); response = new String(bodydata, "UTF-8"); } System.out.println("执行时长:" + (t - s) + "ms"); //释放资源 //postMethod.releaseConnection(); return response; } /** * post方式请求数据 * @param url 请求的完整地址 * @return JSONObject 结果 * @author zs */ public static String httpPostSend(String url){ HttpClient client = new HttpClient(); PostMethod postMethod = new PostMethod(url); String obj = null; try { int response = client.executeMethod(postMethod); System.out.println("code:"+response); if(200 == response){ //成功 String res = postMethod.getResponseBodyAsString(); obj = res; } } catch (HttpException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ postMethod.releaseConnection(); client.getHttpConnectionManager().closeIdleConnections(0); } return obj; } /** * 根据给定的URL地址和参数字符串,以post方法调用,如果成功返回true,如果失败返回false * @param strURL String url地址,不含参数 * @param param Map<String, Object> 参数字表单 * @return boolean true-成功,false失败,如果返回成功可以getStrGetResponseBody() * 获取返回内容字符串,如果失败,则可访问getErrorInfo()获取错误提示。 */ public static String executePostMethod(String strURL, String param) { System.out.println("step into executePostMethod"); String strResult = ""; HttpPost post = new HttpPost(strURL); post.setConfig(requestConfig); StringEntity entity; try { System.out.println("it is json"); entity = new StringEntity(param,"utf-8"); // 解决中文乱码问题 entity.setContentEncoding("UTF-8"); entity.setContentType("application/json"); post.setEntity(entity); /*try { System.out.println("step into try"); if(isJson.isJson(param)){ System.out.println("it is json"); entity = new StringEntity(param,"utf-8"); // 解决中文乱码问题 entity.setContentEncoding("UTF-8"); entity.setContentType("application/json"); post.setEntity(entity); }else if(isJson.isXML(param)){ System.out.println("it is xml"); entity = new StringEntity(param,"utf-8"); // 解决中文乱码问题 entity.setContentEncoding("UTF-8"); entity.setContentType("text/xml"); post.setEntity(entity); }else{ entity = new StringEntity(param,"utf-8"); // 解决中文乱码问题 entity.setContentEncoding("UTF-8"); entity.setContentType("application/x-www-form-urlencoded"); post.setEntity(entity); }*/ //发起请求 HttpResponse httpResponse = httpclient.execute(post); // 请求结束,返回结果 strResult = EntityUtils.toString(httpResponse.getEntity()); System.out.println(strResult); } catch (Exception e) { e.printStackTrace(); } return strResult; } /** * java httpClient4.5 post请求 */ @SuppressWarnings("unchecked") public static Map<String, Object> sendPost(String sendMsg, String sendUrl) { HttpPost httpPost = new HttpPost(sendUrl); HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); CloseableHttpClient closeableHttpClient = httpClientBuilder.build(); StringEntity entity; Map<String,Object> mres = new HashMap<String, Object>(); try { entity = new StringEntity(sendMsg, "UTF-8"); //解决参数中文乱码问题 entity.setContentEncoding("UTF-8");//设置编码格式 entity.setContentType("application/json"); httpPost.setEntity(entity); // 发起请求 HttpResponse httpResponse = closeableHttpClient.execute(httpPost); // 请求结束,返回结果。并解析json。 String resData = EntityUtils.toString(httpResponse.getEntity(),"UTF-8"); mres = (Map<String, Object>) net.sf.json.JSONObject.toBean(net.sf.json.JSONObject.fromObject(resData), Map.class); } catch (Exception e) { e.printStackTrace(); } finally { if (null != closeableHttpClient) { try { closeableHttpClient.close(); } catch (Exception e) { e.printStackTrace(); } } } return mres; } /** * java原生HttpClient3.1 post请求 */ @SuppressWarnings("unchecked") public static Map<String, Object> sendPost2(String sendMsg, String sendUrl) { Map<String, Object> map = new HashMap<String, Object>(); map.put("user_name", "lanqinger"); map.put("real_name", "蓝卿儿"); sendMsg = net.sf.json.JSONObject.fromObject(map).toString(); sendUrl = "http://localhost:8080/sm/test/getPostData.form"; HttpClient httpclient = null; PostMethod post = null; String info = ""; Map<String, Object> mres = new HashMap<String, Object>(); try { httpclient = new HttpClient(); post = new PostMethod(sendUrl); //解决中文乱码问题 post.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8"); post.addParameter("info", sendMsg); httpclient.executeMethod(post); info = new String(post.getResponseBody(), "UTF-8"); mres = (Map<String, Object>) net.sf.json.JSONObject.toBean(net.sf.json.JSONObject.fromObject(info), Map.class); } catch (Exception e) { mres.put("status", "9999"); mres.put("message", "请求处理异常" + e.getMessage()); } finally { // 关闭连接,释放资源 post.releaseConnection(); } return mres; } }
2、接收请求
/** *@Author: zs on 2019/11/26 17:16 *@param: *@return: *@Description:接收生成流量三角形的post请求 */ //produces = { "application/json;charset=UTF-8" }用以解决接受的中文乱码
接收并返回请求信息:若不加上produces = { "application/json;charset=UTF-8" },则编码格式默认为ISO-8859-1,返 回到客户端,会中文乱码。
@RequestMapping(value = "getFlowTriangle",produces = { "application/json;charset=UTF-8" }) @ResponseBody public String createFtgData(HttpServletRequest request) throws IOException, InterruptedException { System.out.println(request.getCharacterEncoding()); String referer = request.getHeader("Referer"); String remoteAddr = request.getRemoteAddr(); String requestURL = request.getRequestURL().toString(); String remoteHost = request.getRemoteHost(); String forwarded = request.getHeader("x-forwarded-for"); BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream(),"utf-8")); String line = null; StringBuilder sb = new StringBuilder(); while((line = br.readLine())!=null){ sb.append(line); } JSONObject jsonObject = JSONObject.parseObject(sb.toString()); Object object = jsonObject.get("assessDate"); String status = "{\"status\":\"生成成功\"}"; return status; }
三、GET请求
2.1.抓取网页的内容并打印到控制台的demo
先直接贴代码:
package fangdd.HttpClientDemo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Locale;
import org.apache.http.HttpEntity;
import org.apache.http.client.HttpClient;
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;
public classHttpGetNewSample{
public static void main(String[] args) {
// TODO Auto-generated method stub
String url="http://www.baidu.com";
//1.使用默认的配置的httpclient
CloseableHttpClient client = HttpClients.createDefault();
//2.使用get方法
HttpGet httpGet = new HttpGet(url);
InputStream inputStream = null;
CloseableHttpResponse response = null;
try {
//3.执行请求,获取响应
response = client.execute(httpGet);
//看请求是否成功,这儿打印的是http状态码
System.out.println(response.getStatusLine().getStatusCode());
//4.获取响应的实体内容,就是我们所要抓取得网页内容
HttpEntity entity = response.getEntity();
//5.将其打印到控制台上面
//方法一:使用EntityUtils
if (entity != null) {
System.out.println(EntityUtils.toString(entity, "utf-8"));
}
EntityUtils.consume(entity);
//方法二 :使用inputStream
/* if (entity != null) {
inputStream = entity.getContent();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line = "";
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
}*/
} catch (UnsupportedOperationException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (response != null) {
try {
response.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
2.2HttpClient编写程序流程总结
其实从上面我们就可以总结出使用HttpClient其实分为6个步骤
1.创建HttpClient对象
这儿使用的是org.apache.http.impl.client.CloseableHttpClient,他是HttpClient接口的一个实例,创建该对象的最简单方法是CloseableHttpClient client = HttpClients.createDefault();
HttpClients是创建CloseableHttpClient的工厂,采用默认的配置来创建实例,一般情况下我们就用这个默认的实例就足够,后面我们可以去看下怎么定制自己需求配置的来创建HttpClient接口的实例。如果你去看这个函数的源代码,你可以看到org.apache.http.client.CookieStore,org.apache.http.client.config.RequestConfig等等都是采用默认的。后面我们会专门有篇博客探讨怎么根据自己的需求定制httpclient。
2.创建某种请求方法的实例。
创建某种请求的实例,并指定请求的url,如果是get请求,创建对象HttpGet,如果是post 请求,创建对象HttpPost。类型的还有 HttpHead, HttpPost, HttpPut, HttpDelete, HttpTrace, 还有 HttpOptions。分别对应HEAD、POST PUT、DELETE、TRACE、OPTIONS方法,每个方法是做什么的如下表:
方法 | 描述 | 是否包含主体 |
GET | 从服务器获取一份文档 | 否 |
HEAD | 只从服务器获取文档的首部 | 否 |
POST | 向服务器发送需要处理的数据 | 是 |
PUT | 将请求的主体部分存储在服务器上 | 是 |
TRACE | 对可能经过代理服务器传送到服务器上去的报文进行追踪 | 否 |
OPTIONS | 决定可以在服务器上执行哪些方法 | 否 |
DELETE | 从服务器上删除一份文档 | 否 |
可以看得到在Http协议中,只有post方法和put方法的请求里面有实体
3.如果有请求参数的话,Get方法直接写在url后面,例如
HttpGet httpget = new HttpGet(
“http://www.google.com/search?hl=zh-CN&q=httpclient&btnG=Google+Search&aq=f&oq=”);
或者使用setParameter来设置参数
URI uri = new URIBuilder()
.setScheme(“http”)
.setHost(“www.google.com”)
.setPath(“/ search”)
.setParameter(“q”,“httpclient”)
.setParameter(“btnG”,“Google搜索”)
.setParameter(“aq”,“f”)
.setParameter(“oq”,“”)
。建立();
HttpGet httpget = new HttpGet(uri);
System.out.println(httpget.getURI());
stdout>
http://www.google.com/search?q=httpclient&btnG=Google+Search&aq=f&oq=
post方法用setEntity(HttpEntity entity)方法来设置请求参数。
后面会详细的探讨Entity这个东西,专门会有一篇博客的,这儿就不在赘叙。
4.发送请求。
调用CloseableHttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个CloseableHttpResponse对象。
CloseableHttpResponse response = client.execute(post);
,很明显CloseableHttpResponse就是用了处理返回数据的实体,通过它我们可以拿到返回的状态码、首部、实体等等我们需要的东西。
5.获取请求结果。
调用CloseableHttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用CloseableHttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。
HttpEntity entity = response.getEntity();
//5.将其打印到显示器上面
//方法一:使用EntityUtils
/*
if(entity!=null)
{
System.out.println(EntityUtils.toString(entity,"utf-8"));
}
EntityUtils.consume(entity)
*/
//方法二
InputStream inputStream = entity.getContent();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line = "";
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
通过CloseableHttpEntity的getEntity取得实体之后,有两种处理结果的方法,
方法一:使用EntityUtils来处理。
该类是官方提供的一个处理实体的工具类,toSting方法将返回的实体转换为字符串,但是官网不建议使用这个,除非响应实体从一个可信HTTP服务器发起和已知是有限长度的。
方法二:使用InputStream来读取
因为httpEntity.getContent方法返回的就是InputStream类型。这种方法是官网推荐的方式,需要记得的是要自己释放底层资源。
6.关闭连接,释放资源。
如果是使用EntityUtils来处理实体的使用 EntityUtils.consume(entity)来释放资源,可以看得到该函数源码为:
publicstaticvoidconsume(final HttpEntity entity)throws IOException {
if (entity == null) {
return;
}
if (entity.isStreaming()) {
final InputStream instream = entity.getContent();
if (instream != null) {
instream.close();
}
}
}
其实还是通过关闭inputStream,然后最后我们再关闭CloseableHttpResponse就可以了
如果是使用InputStream来处理实体的,释放代码如下
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{
//做一些有用的事情
} finally {
intream.close();
}}
}}
} finally {
response.close();
}}
关闭内容流和关闭响应之间的区别是:前者将尝试通过消耗实体内容来保持底层连接活动,而后者立即关闭并丢弃连接
GET方式参考自链接:https://www.cnblogs.com/LuckyBao/p/6096145.html并没有测试。
在所有的矛盾中,要优先解决主要矛盾,其他矛盾也就迎刃而解。
不要做个笨蛋,为失去的郁郁寡欢,聪明的人,已经找到了解决问题的办法,或正在寻找。