小蜗牛,今天爬了多远?不急,继续爬总会到达终点。
朋友们,今天学习了多久?别慌,保持学习才会看到更好的自己。
觉得我的文章还不错的,欢迎大家还可以关注我的微信公众号:Java菜鸟进阶之路
最近会写一个系列的文章进行推出,值得期待和持续关注哦!
文章目录
情景引入
旁白:今天兴致极好,开开心心的与小白玩着“网页小游戏”
小白:真开心呀,好久没有玩这些网页游戏了呢。
我:确实,工作一来,哪有这样的闲情逸致。
小白:不过,你看,咱们玩的游戏都是通过网页链接请求,然后返回给咱们这么多对应的响应内容,它这里感觉好神奇。
我:当然,它里面涉及到的内容还是非常多,可不是一下就能完整掌握的,你这思考问题的能力大大加强了嘛!
小白:这可不嘛,学习到老才行呢。不过,我经常听到同事说,快给我一个接口地址,我要向你发出请求获取信息啦,这里面到底是怎么实现的,一直都没有机会去请教他们。
我:虚心求教,这是非常值得的,不要害怕,不懂就要问,这才是应该有的态度。既然,这样的话,那我就给你科普科普。这不,正好咱们也可以“中场休息”一会,再继续游戏呢。
小白:好呀好呀!学习,游戏两不误,岂不是人生一大快事!
我:小白,长大了!
基础知识
引言
当我们输入一个URL然后回车,页面会显示出对应的功能信息,那么它这个过程到底是如何的呢?其实,这里面涉及到的内容会非常多,比如缓存,地址解析,http协议,网络请求,OSI模型,服务器数据组装,页面渲染等等。这里,就不会说得那么详细(PS:但是这个链路过程,建议各位看官可以好好的琢磨琢磨,分析分析),本文主要是针对,如果对一个已知URL发出请求并获取到其实时的响应信息;
实际场景
我想,在实际开发中经常会碰到这样的事情,就是:需要Http请求其他同事开发的接口,而获取对应需要的内容,比如下载一个URL的文件或者获取数据列表信息等等以及调用第三方公共接口信息。那么,必不可少的就是通过代码去发出一个http的请求。那么,具体如何操作呢?客观,慢慢往下看!
操作方法
现在的方法有很多,且不限于本文说到的几种方式哦!本文主要采取循序渐进的方式,并没有说哪一种方法就是最好的,具体问题具体分析,要以一种开放的心态看待这个问题。
HttpUrlConnection
请求步骤
PS:该方式是最原始的方式,所以值得学习其内部执行流程;
package com.hnu.scw.utils;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
/**
* @ Author :scw
* @ Date :Created in 上午 10:34 2020/5/24 0024
* @ Description:请求URL的post方法工具类
* @ Modified By:
* @Version: $version$
*/
public class HttpPostUtils {
/**
* 请求URL的post请求
* @param url 请求url地址
* @param parameters 请求参数
* @return 响应结果字符串
* @throws Exception 异常信息
*/
public static String doRequestPostMethod(String url, Map<String, Object> parameters) throws Exception {
// 将参数转为String(PS:因为如果URL需要参数的话,那么都是采取字符串字节的形式)
String params = convertRequestParameter(parameters);
URL restURL = new URL(url);
// 请求失败的次数(PS:便于减少由于某次的网络抖动而影响请求)
int errorNumber = 0;
while( errorNumber < 3 ) {
try {
HttpURLConnection conn = (HttpURLConnection) restURL.openConnection();
// 设置请求超时时间(PS:可以不设置)
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
// 请求方式(PS:这里采取POST请求,如果需要GET请求,则为GET)
conn.setRequestMethod("POST");
// 设置请求的数据格式为JSON
conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
conn.setRequestProperty("Connection", "Keep-Alive");
// 是否从httpUrlConnection读入,默认情况下是true
conn.setDoOutput(true);
// 是否允许用户交互,假设为true,则允许用户交互的上下文中对此URL进行检查(比如对话框验证交互)。
conn.setAllowUserInteraction(false);
// 获取输出流(PS:这里方式很多,这里提供几种)
// 方式一
PrintStream ps = new PrintStream(conn.getOutputStream());
ps.print(params);
ps.close();
// 方式二
/* DataOutputStream out = new DataOutputStream(conn.getOutputStream());
out.write(params.getBytes("UTF-8"));
out.flush();
out.close();*/
// 建立连接
conn.connect();
// 获取响应输入流
BufferedReader bReader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
String line;
String resultStr = "";
// 拼接响应内容
while (null != (line = bReader.readLine())) {
resultStr += line;
}
bReader.close();
// 返回相应结果(PS:为字符串格式)
return resultStr;
} catch (Exception e) {
// 错误次数+1
errorNumber++;
}
}
throw new Exception("当前网络异常存在抖动,请稍后再试");
}
/**
* 将map形式的参数转为字符串形式
* @param parameters 请求参数
* @return 请求参数字符串
*/
private static String convertRequestParameter(Map<String, Object> parameters) throws UnsupportedEncodingException {
StringBuilder sb = new StringBuilder();
// 遍历接参数内容
for (Map.Entry<String,Object> param : parameters.entrySet()) {
if (sb.length() != 0) {
sb.append('&');
}
// 防止中文乱码
sb.append(URLEncoder.encode(param.getKey(), "UTF-8"));
sb.append('=');
sb.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
}
return sb.toString();
}
}
OkHttpClient
添加依赖
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.8.0</version>
</dependency>
请求步骤(GET和POST方式)
package com.hnu.scw.utils;
import okhttp3.*;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
/**
* @ Author :scw
* @ Date :Created in 上午 10:34 2020/5/24 0024
* @ Description:请求URL的post方法工具类
* @ Modified By:
* @Version: $version$
*/
public class HttpPostUtils {
/**
* 执行 POST 请求
* @param url 请求的URL
* @return 响应结果
*/
public String doRequestPostByOkHttpClient(String url) {
// ssl的认证重写(PS:用于防止请求URL时有些系统会进行安全验证而导致请求失败)
OkHttpClient okHttpClient = new OkHttpClient.Builder().hostnameVerifier(
new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
}
).build();
// 如果是JDK1.8以上的,那么可以采取lambda重写匿名函数的方式
/* OkHttpClient okHttpClient = new OkHttpClient.Builder().hostnameVerifier(
(s, sslSession) ->{
return true;
}
).build();*/
// 设置请求的参数信息
RequestBody requestBody = new FormBody.Builder()
.add("key1", "test")
.add("key2", "hello")
.build();
// 封装请求头信息
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.addHeader("Cookie", "JSESSIONID=299571E0E40DA6E9962E41B87A669BBB")
.addHeader("content-type", "application/json")
.addHeader("cache-control", "no-cache")
.build();
Call call = okHttpClient.newCall(request);
try {
Response response = call.execute();
// 返回相应结果
return response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
SpingRestTemplate
请求步骤(GET和POST方式)
方式一
package com.hnu.scw.utils;
import okhttp3.*;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
/**
* @ Author :scw
* @ Date :Created in 上午 10:34 2020/5/24 0024
* @ Description:请求URL的post方法工具类
* @ Modified By:
* @Version: $version$
*/
public class HttpPostUtils {
/**
* 执行 POST 请求 通过Spring 的restTemplate方式
* @param url 请求URL
* @param parameter 请求参数
* @return 响应结果字符串
*/
public String doRequestPostByRestTemplate(String url, Object parameter) {
RestTemplate template = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
// 设置请求的格式为:json
MediaType type = MediaType.parseMediaType("application/json;charset=UTF-8");
headers.setContentType(type);
HttpEntity entity = new HttpEntity(parameter,headers);
// POST方式,如果要用GET,则为getForObject即可
String result = template.postForObject(url, entity, String.class);
return result;
}
/**
* 执行 POST 请求 通过Spring 的restTemplate方式二
* @param url 请求URL
* @param parameter 请求参数(PS:一般为java实体)
* @param backDto 相应的结果实体
* @return 响应结果实体
*/
public <T> T doRequestPostByRestTemplate(String url, Object parameter, Class<T> backDto) {
RestTemplate template = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
// 设置请求的格式为:json
MediaType type = MediaType.parseMediaType("application/json;charset=UTF-8");
headers.setContentType(type);
HttpEntity entity = new HttpEntity(parameter,headers);
// POST方式,如果要用GET,则为getForObject即可
String result = template.postForObject(url, entity, String.class);
// 将json字符串转为实体
return JSON.parseObject(result, backDto);
}
}
方式二:
package com.hnu.scw.utils;
import okhttp3.*;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
/**
* @ Author :scw
* @ Date :Created in 上午 10:34 2020/5/24 0024
* @ Description:请求URL的post方法工具类
* @ Modified By:
* @Version: $version$
*/
public class HttpPostUtils {
/**
* 执行 POST 请求 通过Spring 的restTemplate方式的方式二
* @param url 请求URL
* @param parameter 请求参数
* @return 响应结果实体类型
*/
public <T> T doRequestPostByRestTemplateEntity(String url, Object parameter, Class<T> backDto) {
RestTemplate template = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
// 设置请求的格式为:json
MediaType type = MediaType.parseMediaType("application/json;charset=UTF-8");
headers.setContentType(type);
HttpEntity entity = new HttpEntity(parameter,headers);
// POST方式,如果要用GET,则为getForEntity即可
ResponseEntity<T> tResponseEntity = template.postForEntity(url, entity, backDto);
return tResponseEntity.getBody();
}
}
彩蛋
如何获取请求URL的响应流
背景:假设我们请求的这个URL,其返回的结果是一个文件流,那么我们就需要将这个文件流输出给前端,以实现下载文件的功能;
实现步骤:先获取请求URL的响应结果的输入流,然后再通过最开始前端的HTTPServletResponse请求响应流outputStream输出即可;
HttpUrlConnect
PS:如果采取原生的请求方式,那么就比较简单,直接获取其对应的响应结果的输入流,则可以返回。
/**
* 请求 URL 的post请求
* @param url 请求url地址
* @param parameters 请求参数
* @return 响应结果流
* @throws Exception 异常信息
*/
public static InputStream doRequestPostMethodInputStream(String url, Map<String, Object> parameters) throws Exception {
// 将参数转为String(PS:因为如果URL需要参数的话,那么都是采取字符串字节的形式)
String params = convertRequestParameter(parameters);
URL restURL = new URL(url);
// 请求失败的次数(PS:便于减少由于某次的网络抖动而影响请求)
int errorNumber = 0;
while( errorNumber < 3 ) {
try {
HttpURLConnection conn = (HttpURLConnection) restURL.openConnection();
// 设置请求超时时间(PS:可以不设置)
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
// 请求方式(PS:这里采取POST请求,如果需要GET请求,则为GET)
conn.setRequestMethod("POST");
// 设置请求的数据格式为JSON
conn.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
conn.setRequestProperty("Connection", "Keep-Alive");
// 是否从httpUrlConnection读入,默认情况下是true
conn.setDoOutput(true);
// 是否允许用户交互,假设为true,则允许用户交互的上下文中对此URL进行检查(比如对话框验证交互)。
conn.setAllowUserInteraction(false);
// 获取输出流
DataOutputStream out = new DataOutputStream(conn.getOutputStream());
out.write(params.getBytes("UTF-8"));
out.flush();
out.close();
// 建立连接
conn.connect();
// 获取响应输入流
return conn.getInputStream();
} catch (Exception e) {
// 错误次数+1
errorNumber++;
}
}
return null;
}
/**
* 请求URL并且下载其返回的文件流内容
* @param httpServletResponse http响应
* @throws Exception
*/
public void downLoadFile(HttpServletResponse httpResponse) throws Exception {
InputStream inputStream = doRequestPostMethodInputStream("xxx", new HashMap<>());
// 采取缓存流的方式
OutputStream outputStream = new BufferedOutputStream(httpResponse.getOutputStream());
httpResponse.setContentType("application/octer-stream");
String fileName = URLEncoder.encode("测试","UTF-8");
httpResponse.setHeader("Content-disposition","attachment;filename=" + fileName + ";");
byte[] buff = new byte[1024];
int length = 0;
while((length = inputStream.read()) != -1 ){
outputStream.write(buff, 0, length);
}
outputStream.flush();
}
Spring的RestTemplate
package com.hnu.scw.utils;
import okhttp3.*;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.core.io.Resource;
import org.springframework.http.*;
import org.springframework.http.MediaType;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
/**
* @ Author :scw
* @ Date :Created in 上午 10:34 2020/5/24 0024
* @ Description:请求URL的post方法工具类
* @ Modified By:
* @Version: $version$
*/
public class HttpPostUtils {
/**
* 执行 POST 请求 获取其返回的文件流
* @param url 请求URL
* @param parameter 请求参数
* @return 响应结果文件输入流
* @throws IOException
*/
public static InputStream doRequestPostByRestTemplateEntity(String url, Object parameter) throws IOException {
RestTemplate template = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
// 设置请求的格式为:json
MediaType type = MediaType.parseMediaType("application/json;charset=UTF-8");
headers.setContentType(type);
HttpEntity entity = new HttpEntity(parameter,headers);
// POST方式,如果要用GET,则为getForEntity即可
ResponseEntity<Resource> responseEntity = template.postForEntity(url, entity, Resource.class);
if(HttpStatus.OK.equals(responseEntity.getStatusCode())){
return responseEntity.getBody().getInputStream();
}
return null;
}
/**
* 请求URL并且下载其返回的文件流内容
* @param httpServletResponse http响应
* @throws Exception
*/
public void downLoadFile(HttpServletResponse httpResponse) throws Exception {
InputStream inputStream = doRequestPostByRestTemplateEntity("xxx", new HashMap<>());
// 采取缓存流的方式
OutputStream outputStream = new BufferedOutputStream(httpResponse.getOutputStream());
httpResponse.setContentType("application/octer-stream");
String fileName = URLEncoder.encode("测试","UTF-8");
httpResponse.setHeader("Content-disposition","attachment;filename=" + fileName + ";");
byte[] buff = new byte[1024];
int length = 0;
while((length = inputStream.read()) != -1 ){
outputStream.write(buff, 0, length);
}
outputStream.flush();
}
}
总结
- 本文讲述的方式各自有各自的应用场景,并不存在哪一种就是最好的形式(PS:当然不仅仅只有这些方式,比如还有apache的httpClient,CloseableHttpClient以及springCloud中常见的Fegin);
- 实际工作中发出http的请求是相对的多,对于其内部的执行流程和实现原理要去掌握(PS:网络协议+socket+netty都是需要掌握);
- 针对常用的场景和技术要学会去总结,分析各自的利弊,而不要只会用而不明白其内部;
- 通过阅读本文内容之后,建议将“输入一个URL地址,然后回车敲击显示结果”,这个链路的整个过程都去学习和掌握,你会发现更多的美哦!;
- 如果你想利用闲暇零散的学习技术,那么不妨关注我的公众号阅读你想要的文章哦!公众号搜索:Java菜鸟进阶之路
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/12376.html