从源码研究实现Java发送get和post请求

一、get和post的一些基础知识

get请求:

  1. 原理:get请求本质上是去数据库里面查资源;

  2. 表现形式:请求数据会依附在url只有,以?分割,参数与参数之间以&符号相连,请求参数最多只能是1024个字节;

  3. 提交形式:请求和header一起发出;

  4. 安全性:安全性低,参数明文传输。

post请求:

  1. 原理:post请求本质是向数据库提交数据,增删改操作;

  2. 表现形式:提交的数据放在http的body体内,http协议对post请求的参数大小没有限制,但是不排除各大浏览器自己会做限制;

  3. 提交形式:请求header先发出,收到服务器返回的response header后,body再发出;

  4. 安全性:相对get请求,post请求的请求参数放在body体内相对安全一些,涉及用户登录密码和金钱相关的数据要特别注意加密处理。

二、http协议和https协议

二者的区别:

  1. http协议:超文本传输协议,且信息是明文传输;若攻击者截取客户端和服务器之间的传输报文,就可以获取用户信息,不安全;
  2. https协议:是具有安全性的ssl加密传输协议,为客户端和服务器之间的通信加密,确保数据传输安全。
  3. http结构:http–tcp–ip;https结构:http–ssl–tcp–ip;
  4. http协议连接简单,是无状态的;https是有ssl+http协议构建的加密传输、身份认证的网络协议;
  5. http协议的端口是80;https协议的端口是443;
  6. http证书申请免费,https证书需要到ca申请,需要交费。

三、Java-HTTPclient官方网站

2.1 官网地址:

http://hc.apache.org/httpcomponents-client-4.5.x/quickstart.html

2.2 eclipse中maven工程在pom.xml中配置HTTPclient依赖

<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
		<dependency>
		    <groupId>org.apache.httpcomponents</groupId>
		    <artifactId>httpclient</artifactId>
		    <version>4.5.6</version>
		</dependency>

四、java发送get请求

  • A. 定义一个HTTPClients类的对象client,用来执行发送请求操作。类似于是一个浏览器的driver,client执行完发送请求的操作后,服务端返回的cookie(和session)信息会存储在client中;
    • A.1 取cookie过程需要在定义client时调用另一个类方法,即方法:HttpClients.custom()方法,它的返回值类型是HttpClientBuilder,最后通过研究这个源码类使用以下两行代码得到CookieStore对象存储服务器返回的session值,这个值存放在cookie中;
CookieStore cookieStore = new BasicCookieStore();
client = HttpClients.custom().setDefaultCookieStore(cookieStore).build();

如果想要输出cookie信息,则需要注意两点:
1、输出cookie信息的操作要在执行请求后,即服务器返回结果后,在关闭response和client对象之前;
2、通过cookieStore.getCookies()方法得到的是一个List对象,数组的输出通过for循环即可得到结果。

  • B. 定义一个HTTPGet类的对象httpGet,用来携带需要发送的信息,例如请求url、请求头header信息、请求超时等信息;
    • B.1 请求url:初始化时,调用HttpGet类的构造方法时传参实现。get请求url是接口地址和请求参数拼接组成,代码中第5行;
    • B.2 请求头header信息:通过拿到的httpGet对象,向上转型到HttpMessage源码类,调用HttpMessage源码类中的setHeader(final String name, final String value)方法,该方法已经在子类中被覆写,子类是:AbstractHttpMessage;
    • B.3 请求超时 通过HttpGet对象向上转型调用父类HttpRequestBase源码类中的setConfig(final RequestConfig config)方法添加到httpGet对象中,其中setConfig()方法中传参是RequestConfig类型,通过研究RequestConfig这个源码类就可设置成功超时时间,调用的是setConnectionRequestTimeout(final int connectionRequestTimeout)方法;
  • C. 准备好发送请求的数据后,使用client.execute(httpGet)发送请求,得到对象response,是CloseableHttpResponse类型,我们想要是String或者Json类型结构的数据,还没达到目标;
  • D. 判断response.getStatusLine().getStatusCode()是否返回的时200,当是200时证明服务器返回正常,我们再通过response.getEntity()方法得到一个HttpEntity类型的返回对象entity;
  • E. 最后通过EntityUtils工具类中的EntityUtils.toString(entity, “utf-8”)方法把上一步得到的entity对象转化成String类型,返回结果;
  • F. 最最后,我们要恢复环境执行response.close()和client.close()。

简单封装后的发送get请求整个过程代码如下:

public static void sendGet(String url, String param) throws Exception {
		String result = null;
		CloseableHttpClient client = null;
		CloseableHttpResponse response = null;
		String finUrl = url + "?" + param;
		try {
			client = HttpClients.createDefault();
			HttpGet httpGet = new HttpGet(finUrl);
			
			//设置请求头
			httpGet.setHeader("Content-Type", "application/json; charset=utf-8");
			httpGet.setHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36");
			
			//设置请求超时 
			Builder builder = RequestConfig.custom();
    		builder.setConnectionRequestTimeout(3000);
			RequestConfig config = builder.build();
			httpGet.setConfig(config);
			httpGet.setConfig(RequestConfig.custom().setConnectionRequestTimeout(3000).build());
			
			response = client.execute(httpGet);
			int code = response.getStatusLine().getStatusCode();
   
			if (code == HttpStatus.SC_OK) {
				HttpEntity entity = response.getEntity();
				result = EntityUtils.toString(entity, "utf-8");
			}else {
				result = "服务器状态码为:" + code + " ,请检查接口地址和请求参数。";
			}	
		}finally {
			response.close();
			client.close();
		}
		return result;
	}

五、java发送post请求

  • A. 定义一个HTTPClients类的对象client,用来执行发送请求操作。类似于是一个浏览器的driver,client执行完发送请求的操作后,服务端返回的cookie(和session)信息会存储在client中;
  • B. 定义一个HTTPGet类的对象httpPost,用来携带需要发送的信息,例如请求url、请求头header信息、请求超时等信息;
  • C. 准备请求参数,post请求参数存放在请求body体内,如果使用官网方法,需要传参为NameValuePair类型的数据,即官网示例如下:
List <NameValuePair> nvps = new ArrayList <NameValuePair>();
nvps.add(new BasicNameValuePair("username", "vip"));
nvps.add(new BasicNameValuePair("password", "secret"));
httpPost.setEntity(new UrlEncodedFormEntity(nvps));

因为我们正常提供的测试case,在param字段中一般存String类型,所以找传String类型的请求参数,放到httpPost对象中(通过httpPost.setEntity()方法),携带发送请求。经过研究发现httpPost.setEntity()方法的入参是HttpEntity源码类的对象,但是HttpEntity是个接口,只能通过实例化子类来得到父类对象,找到StringEntity源码类,发现使用这个源码类中的其中一个构造方法,可以传String类型的参数,new子类通过向上转型,从而得到HttpEntity的对象,到此我们把sendPost方法中的String param字段添加到了httpPost对象内;

  • D. 准备好发送请求的数据后,使用client.execute(httpPost)发送请求,得到对象response,是CloseableHttpResponse类型,我们想要是String或者Json类型结构的数据,还没达到目标;
  • E. 判断response.getStatusLine().getStatusCode()是否返回的时200,当是200时证明服务器返回正常,我们再通过response.getEntity()方法得到一个HttpEntity类型的返回对象entity;
  • F. 最后通过EntityUtils工具类中的EntityUtils.toString(entity, “utf-8”)方法把上一步得到的entity对象转化成String类型,返回结果;
  • G. 最最后,我们要恢复环境执行response.close()和client.close()。

简单封装后的发送post请求整个过程代码如下:

public static String sendPost(String url, String param) throws Exception {
		String result = null;
		CloseableHttpClient client = null;
		CloseableHttpResponse response = null;
		try {
			client = HttpClients.createDefault();
			HttpPost httpPost = new HttpPost(url);
			//设置请求头
			httpPost.setHeader("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148");
			httpPost.setHeader("Content-Type", "application/json");
			
			//设置请求超时 
			Builder builder = RequestConfig.custom();
			builder.setConnectionRequestTimeout(3000);
			RequestConfig config = builder.build();
			httpPost.setConfig(config);
			httpPost.setConfig(RequestConfig.custom().setConnectionRequestTimeout(3000).build());
			
			//设置post请求参数
//			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
//			BasicNameValuePair bnvp = new BasicNameValuePair(null, null);
			// 直接添加NameValuePair类型的数据 遇到一级value下又有多级数据时也是直接写入吗?例如:"h_ids": {"idfa": "0CB4E1A5-1782-4F31-8B30-0218453BBCBE"}
//			nvps.add(new BasicNameValuePair("name", "zhaoliu"));
//			httpPost.setEntity(new UrlEncodedFormEntity(nvps));
			
                   //入参是HttpEntity类型,HTTPEntity是个接口,需要通过子类实例化得到父类对象,找HTTPEntity的实现类,StringEntity
//			httpPost.setEntity(HTTPEntity httpEntity);
			httpPost.setEntity(new StringEntity(param, "utf-8"));
			
			// 执行发送请求操作
			response = client.execute(httpPost);
			int code = response.getStatusLine().getStatusCode();
			
			if (code == HttpStatus.SC_OK) {
				HttpEntity entity = response.getEntity();
				result = EntityUtils.toString(entity, "utf-8");
			}else {
				result = "服务器状态码为:" + code + " ,请检查接口地址和请求参数。";
			}
		}finally {
			response.close();
			client.close();
		}
		return result;
	}

六、总结

6.1 不管发送post请求还是get请求,都需要先定义CloseableHttpClient的对象client。client对象可以生成一个CookieStore的对象管理和存储header的cookie和服务器返回的session信息,即cookie和session信息是在client对象这里操作的;

6.2 得到HttpGet或者HttpPost对象后,我们要设置的请求超时、请求header数据都是在这一步生成和操作的;

6.3 get请求的参数通过拼接即可实现,但post请求的请求参数是在body体内的,要通过httpPost.setEntity(new StringEntity(param, “utf-8”))代码绑定到HttpPost的对象上;

6.4 以上三步准备好之后,通过client.excute(httpPost) / client.excute(httpGet)拿到CloseableHttpResponse类型的一个返回值,我们初步拿到了服务器的返回结果;

6.5 对服务器返回结果做处理,通过response.getEntity()方法把返回结果转化成HttpEntity类型,最后通过EntityUtils工具类中的toString()方法把服务器返回的结果转化成我们常见的String类型;

6.6 最后就是善后工作,关闭response对象,关闭client对象。

赶快自己去实验一下吧!
中间的一些源码转换没有详细记录,哪里看不太懂欢迎和我交流!

上一篇:【避坑点】json返回数据为?????


下一篇:网易云信课题实践-注册请求处理