文章目录
- 前言
- 如何构造HTTP请求?
- 1、通过form表单构造HTTP请求
- ① form发送GET请求
- ② form发送POST请求
- 2、通过Ajax构造HTTP请求
- 同步等待与异步等待的感性理解
- 3、使用postman构造HTTP请求
- 4、通过 Java socket 构造 HTTP 请求
- 如何实现前端给后端传参
- 1、query string获取前端发起的GET请求
- 2、form表单获取前端发起的POST请求【⭐】
- 重点:『图解前后端交互全过程』
- 3、json格式解析获取前端发起的POST请求
- ① postman发送HTTP请求
- ② Ajax构造HTTP请求
- 拓展:Jackson第三方库解析json格式
- 总结与提炼
前言
很多同学在学习到Servlet的时候,需要通过Tomcat去接收HTTP的响应以实现这个前后端交互的场景,因此理解起来就比较困难,之前在写Java代码的时候,我们只是在一个程序里面通过方法1调用方法2以达成基本的代码逻辑。不过现在呢,我们是去进行一个前后端的交互,本质实现的是两个程序之间的相互通信
- 这就需要去理解一个接口的思想,后端Servlet通过注解的形式去为前端提供一个访问的接口,前端只需要通过这个找到对应的服务器接口,Tomcat服务器就会将HTTP请求解析为
request
对象,以此很好地在内部通过Servlet来处理这个对象,再构造出response
对象由返回给Tomcat,最后由Tomcat继续解析为HTTP响应返回给浏览器
还是有点不同的同学可以看看下面这个动图,解释得很好,出自JavaWeb——Servlet
如何构造HTTP请求?
当然,前端在给后端发送HTTP请求之前,它要先构造出这个请求才行,下面我介绍三种构造HTTP请求的方式
1、通过form表单构造HTTP请求
① form发送GET请求
- 那首先我们要去写一个表单
- 【action】属性为
https://www.sogou.com/
,也就是指定表单要提交到的地址处(URL),这里是交给搜狗的服务器来处理我们所提交的表单内容 - 【method】属性为
get
,表示在提交表单时所需要用到的HTTP方法,这里的话是使用get(默认),不过对于表单来说我们用的都是post,下一小节会介绍
- 【action】属性为
- 写好form表单的属性后,里面就是供用户操作的一些控件,分别为两个输入框和一个提交按钮,可以看到两个输入文本框我不仅设置了必要的【type】属性,而且还分别设置了【name】属性,为的就是实现的header(请求头)中所呈现键值对
<form action="https://www.sogou.com/" method="get">
<input type="text" name="UserId">
<input type="text" name="ClassId">
<!-- 点击这个按钮会触发form表单的提交操作,也就是构造HTTP请求发给服务器 -->
<input type="submit" value="提交">
</form>
- 可以看到,当我点击提交的时候,就来到了搜狗的主页
- 接着我们通过Fiddler抓包工具就可以去查看这个HTTP请求,对于GET请求来说,我们主要看这个第一行就可以了
下面是我们对form表单的设置与HTTP请求报文发生的对应变化
② form发送POST请求
- 当然,form表单除了向服务器发送GET请求外,还可以发送POST请求,只需要修改一下这个【method】属性即可
<form action="https://www.sogou.com/" method="post">
<input type="text" name="UserId">
<input type="text" name="ClassId">
<!-- 点击这个按钮会触发form表单的提交操作,也就是构造HTTP请求发给服务器 -->
<input type="submit" value="提交">
</form>
- 然后我们看到form表单所发送过来的HTTP请求的原生文件,可以观察到与GET请求相比,里面的内容出现了差距
- 然后我们来分析一下这个报文格式的变化,可以看到对于form构造的post请求来说,body里面的数据格式就和query string是非常相似的,也是【键值对结构
- 键值对之间用
&
来分割 - 键和值之间用
=
来分割
- 键值对之间用
2、通过Ajax构造HTTP请求
当然,除了form表单之外,我们还有一种功能更强的构造HTTP请求的方式,那就是通过ajax来实现
同步等待与异步等待的感性理解
【概念】:ajax 全称 Asynchronous Javascript And XML
, 是 2005 年提出的一种 JavaScript 给服务器发送HTTP 请求的方式.
- 其最大的特点就是:可以不需要刷新页面/页面跳转 就能进行数据传输.
- 其实ajax最需要关注的点就是
Asynchronous
,也就是【异步】的意思,【同步】相信学习过多线程的同学都有听说过,使用关键字synchronized
就可以去实现了,那我们就要来谈谈这个【同步】和【异步】的区别了
我在这里举一个形象点的例子
- 比如说你现在和你和女朋友在谈恋爱,你呢想约她出去玩,于是来到她的宿舍楼下,打电话问她什么时候出来,此时她说:“等一下”,但是呢一般女生说的等一下通常不是三五分钟,起码是半个小时起步⏰于是呢你就开始了漫长的等待~~
- 如果你只是戴戴地望着宿舍大门等她出来,等到她露头就马上迎上去,此时就叫做【同步的等待】
- 如果你觉得这么等着比较无聊,然后来到了宿舍楼下周围的树荫下打开了你心爱的《Java编程思想》,那么此时你也是在等待,不过这叫做【异步的等待】
相信通过上述这个案例你一定理解了什么是【同步等待】和【异步等待】了
A 等待 B
👉 A始终盯着B,A负责关注B啥时候就绪
👉 A不盯着B,B就绪之后主动通知A
- HTML中,通过ajax 发起http请求,就属于是 “异步” 方式,现在我们就来说说如何使用ajax去发起http请求
- 不过呢在实现ajax前我们要先引入一个外部的jQuery,它是一种流行的 JavaScript 库,通过封装常见任务和跨浏览器兼容性问题来使 JavaScript 更易于使用
- 首先进入bootcdn的官网,把这段链接复制下来即可,然后将其粘贴到
<script>
的src属性中,表示引入一个外部的js文件
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
- 接下去我们就可以去构造ajax请求了,需要构造的内容其实和form表单类似,只是属性有所不同罢了
- 在form表单中我们所写的
method
,这里变成了type
; - 在form表单中我们所写的
action
,这里变成了url
;
- 在form表单中我们所写的
- 其实它们本质还是相同的,只是换了个说法而已。然后接下去呢就可以看到有一个
success
属性,它的内部似乎是一个function函数。这里要重点说一下,它就是我们在C语言中所学习的回调函数,这个底层的逻辑是类似的,当我们通过ajax构造的HTTP请求到达服务器后,服务器再进行处理将其返回给浏览器,此时浏览器就会触发该回调函数,去进行执行相应的代码 - 上述的这个回调逻辑其实就体现了【异步】的思想,ajax不需要等待浏览器给它响应,就可以先把自己的代码先跑完,所以我们可以预期一下console控制台中的第一句便是这个【浏览器立即往下执行后续代码】
<script>
$.ajax({
type: 'get',
url: "https://www.sogou.com/",
// 此处的success就声明了一个回调函数,就会在服务器响应,回到浏览器的时候触发该回调
// 正是此处的回调体现了 “异步”
success: function(data){
console.log("当服务器返回的响应到达浏览器后,浏览器触发该回调,通知到咱们的代码中");
}
})
console.log("浏览器立即往下执行后续代码");
</script>
- 通过去console控制台进行观察我们可以发现打印出来的结果是【浏览器立即往下执行后续代码】,这就意味着它是在回调函数之前执行的,但回调函数没有执行只是因为搜狗那里的服务器并没有处理我们呢发送过去的HTTP请求,所以便报出了异常,后面我们自己通过Tomcat去模拟服务器调用Servlet来进行处理即可
【小结一下】:
- 和form相比,ajax功能更强
- 支持put、delete等方法
- ajax发送的请求可以灵活设置header
- ajax发送的请求body也可以灵活设置
3、使用postman构造HTTP请求
对于postman而言,它是专门用来进行http请求测试的工具,如有不懂可以看看这篇文章链接
- 有关如何去构造HTTP请求,我下面做一个演示,很简单,只需要点点鼠标即可🖱
- 此时搜狗那边就可以收到我们的HTTP请求了,但是人家不给出回应也没办法查看,下面我们自己去实现前后端交互的时候我会继续做演示
4、通过 Java socket 构造 HTTP 请求
上面三种是我们在平常工作中用得最多的,还有一种就是通过 Java socket 来进行构造,可以了解一下~
public class HttpClient {
private Socket socket;
private String ip;
private int port;
public HttpClient(String ip, int port) throws IOException {
this.ip = ip;
this.port = port;
socket = new Socket(ip, port);
}
public String get(String url) throws IOException {
StringBuilder request = new StringBuilder();
// 构造首行
request.append("GET " + url + " HTTP/1.1\n");
// 构造 header
request.append("Host: " + ip + ":" + port + "\n");
// 构造 空行
request.append("\n");
// 发送数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write(request.toString().getBytes());
// 读取响应数据
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[1024 * 1024];
int n = inputStream.read(buffer);
return new String(buffer, 0, n, "utf-8");
}
public String post(String url, String body) throws IOException {
StringBuilder request = new StringBuilder();
// 构造首行
request.append("POST " + url + " HTTP/1.1\n");
// 构造 header
request.append("Host: " + ip + ":" + port + "\n");
request.append("Content-Length: " + body.getBytes().length + "\n");
request.append("Content-Type: text/plain\n");
// 构造 空行
request.append("\n");
// 构造 body
request.append(body);
// 发送数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write(request.toString().getBytes());
// 读取响应数据
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[1024 * 1024];
int n = inputStream.read(buffer);
return new String(buffer, 0, n, "utf-8");
}
public static void main(String[] args) throws IOException {
HttpClient httpClient = new HttpClient("42.192.83.143", 8080);
String getResp = httpClient.get("/AjaxMockServer/info");
System.out.println(getResp);
String postResp = httpClient.post("/AjaxMockServer/info", "this is
body");
System.out.println(postResp);
}
}
如何实现前端给后端传参
在学会了如何去自己手动构造HTTP请求后,我们就要去实现服务器去接收这个HTTP请求然后处理它并且处理后的内容返回,此时就需要使用到我们之前说到故的Tomcat + Servlet来实现了
1、query string获取前端发起的GET请求
首先第一种,后端通过query string的方式去获取前段的GET请求,对于query string 我们在讲解HTTP协议的时候有说起过,就是?
后面加上键值对的形式去构成
那现在的话我们就要通过后端的代码来处理这个请求了
- 首先最重要的就是加要上这个
@WebServlet()
的注解,这个其实就相当于服务端提供给外部的一个标识,前段便可通过这个注解的标识来发送对应的HTTP请求 - 当然在Tomcat服务器接收到这个HTTP请求后,还要去将其解析为【HttpServletRequest 】对象,接着便将其交给Servlet做处理,这里我们使用
getParameter()
就可以通过找寻对应的key来获取相关的value了,最后呢通过resp对象再给到浏览器做响应
@WebServlet("/getParameter")
public class GetParameterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html charset=utf-8");
String userId = req.getParameter("userId");
String classId = req.getParameter("classId");
resp.getWriter().write("userId = " + userId + " classId = " + classId);
}
}
- 最后我们就可以看到浏览器上显示出了对应的内容
2、form表单获取前端发起的POST请求【⭐】
GET请求发送完后,我们再来试试POST请求,主要是通过form表单来实现
- 这里的HTML代码我们前面在通过form表单构造HTTP请求的时候有写过
<form action="postParameter" method="post">
<input type="text" name="userId">
<input type="text" name="classId">
<input type="submit" value="提交">
</form>
- 那后端这块的逻辑还是和query string差不多,就是重写的方法要为
doPost()
而已,然后的话注解标记记得也做个修改
@WebServlet("/postParameter")
public class PostParameterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html charset=utf-8");
String userId = req.getParameter("userId");
String classId = req.getParameter("classId");
resp.getWriter().write("userId = " + userId + " classId = " + classId);
}
}
- 看到最后返回的结果还是这样,通过无论是GET还是POST的请求一样都可以进行处理
重点:『图解前后端交互全过程』
对于form表单这种请求来说,可能对于一些初学者不是很好理解,这里我画了两种图,希望可以帮助到你
网页端与服务端交互请求全过程
浏览器与tomcat的两次交互过程
3、json格式解析获取前端发起的POST请求
如果 POST 请求中的 body 是按照 JSON 的格式来传递,那我们在前后端交互的过程中又会发生怎么微妙的变化呢?
① postman发送HTTP请求
- 首先我们简单一点,使用现有的工具postman去构造一个body为【json】格式的POST请求
- 然后再来到后端这里写Servlet的代码逻辑处理这个POST请求。不过呢,这和我们上面缩写的可不太一样。因为在body中是【json】格式的数据,因此我们无法直接通过某种方法来精准获取里面的内容,那就只有一个办法:全部读出来!
- 那在讲解网络socket的时候,我们有说到过可以用
getInputStream()
去读取管道中的字节流,倒入到容器中,取多少倒多少,最后再转换为String的对象,打印出来的内容即为我们读取到的对象,代码如下👇
@WebServlet("/postParameterP")
public class PostParameterPostman extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//在流对象中读取的字节数取决于ContentLength
int length = req.getContentLength();
byte[] buffer = new byte[length];
InputStream inputStream = req.getInputStream();
inputStream.read(buffer); //以字节形式读取HTTP报文格式中的数据
String body = new String(buffer, 0, length, "utf-8");
resp.getWriter().write(body);
System.out.println(body);
}
}
最后我们来看一下运行结果吧
② Ajax构造HTTP请求
说完了使用postman来发送HTTP请求,然后我们再用Ajax来试试,前面我们有介绍过了,但是人家搜狗的服务器不处理我们的请求,因此就报出了错误,但现在我们学习了Tomcat和Servlet后,就就可以自己去实现服务端的代码逻辑了
@WebServlet("/postParameterA")
public class PostParameterAjax extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("applicaiton/json;charset=utf-8");
String body = readBody(req);
resp.getWriter().write(body);
}
private String readBody(HttpServletRequest req) throws IOException {
//首先获取到body主体的内容长度
int length = req.getContentLength();
byte[] body = new byte[length];
InputStream inputStream = req.getInputStream();
inputStream.read(body); //把管道中的数据读入桶中
return new String(body, 0, length, "utf-8"); //再构造String对象返回
}
}
- 对于前端的代码,我们要做一些修改,改用按钮点击事件
<button onclick="sendJson()">发送 Json 格式POST请求</button>
- JS的话把Ajax的请求包在
sendJson()
的按钮点击事件中,并且写好对应的5个属性,
<script>
function sendJson(){
ajax({
method: 'POST',
url: 'postParameterA',
contentType: 'application/json;charset=uft-8',
body: JSON.stringify({
"userId": 10,
"classId": 20
}),
callback: function(body, status){
console.log("回调函数会在之后调用");
console.log(body);
}
})
}
console.log("浏览器立即往下执行后续代码");
function ajax(args) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
// 0: 请求未初始化
// 1: 服务器连接已建立
// 2: 请求已接收
// 3: 请求处理中
// 4: 请求已完成,且响应已就绪
if (xhr.readyState == 4) {
args.callback(xhr.responseText, xhr.status)
}
}
xhr.open(args.method, args.url);
if (args.contentType) {
xhr.setRequestHeader('Content-type', args.contentType);
}
if (args.body) {
xhr.send(args.body);
} else {
xhr.send();
}
}
</script>
接下去我们来看看运行结果
- 首先是一开始进入浏览器的样子,可以看到就打印出了我们在最后所写这句话,然后并没有去调用这个
callback()
回调函数,这是因为我们在前端的页面中什么都没有做,即没有向服务器发送HTTP请求,因此也不会触发这个回调函数的调用机制
- 不过在我们点击了这个按钮后,网页端便发送了一个POST请求给到服务端这里,服务端在接收并处理这个HTTP报文后,返回给浏览器做响应,此时这个回调函数便被调用了,打印出了语句和【json】格式的body主体
拓展:Jackson第三方库解析json格式
就上面两种构造json格式的方法,我们在服务器这边只是将整个body给读了出来,但是,但是并没有按照键值对的方式来处理,即无法根据key获取value
- 在前面的【query string】和【form表单】中,我们可以使用
getParameter()
这个方法来获取对应的value值,对于【json】格式的数据,我们可以使用第三方库来解决,这个库有很多,例如gson、fastjson,但是在这里我推荐使用 Jackson - 为什么推荐这个呢?相信学习过Spring MVC的同学都应该清楚,它里面天然就支持处理json格式的数据,就是因为内置了Jackson这个库来解析json格式
第一步: 在中央仓库中搜索 Jackson, 选择 JackSon Databind
第二步: 选择2.12.3版本,找到Maven依赖
第三步: 在pom.xml
文件中添加这个Maven依赖
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
第四步:重新创建一个Java类,继承自HttpServlet类,然后使用Jackson中的核心对象ObjectMapper去完成json格式的解析
class Student{
public String userId;
public String classId;
}
@WebServlet("/postParameterJaskson")
public class PostParameterJaskson extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.使用Jackson使用到的核心对象
ObjectMapper objectMapper = new ObjectMapper();
// 2.使用里面的readValue()方法把里面具有json格式的数据转换为Java对象
Student student = objectMapper.readValue(req.getInputStream(), Student.class);
// 3.显示出解析后的数据
System.out.println("userId = " + student.userId + " classId = " + student.classId);
resp.getWriter().write("userId = " + student.userId + " classId = " + student.classId);
}
}
第五步: 在postman中修改对应的WebServer注解,给对应的接口发送HTTP请求,最后获取到服务器和浏览器都观察到解析后的结果
这里讲解一下最后的这段代码,可能有的同学不太理解
- JsonData 这个类用来表示解析之后生成的 JSON 对象. 这个类的属性的名字和类型要和 JSON 字符串的 key 相对应.
- Jackson 库的核心类为
ObjectMapper
. 其中的 readValue() 方法把一个 JSON 字符串转成 Java 对象. 其中的 writeValueAsString() 方法把一个 Java 对象转成 JSON 格式字符串. - readValue 的第二个参数为
JsonData
的 类对象. 通过这个类对象, 在 readValue 的内部就可以借助【反射机制】来构造出 JsonData 对象, 并且根据 JSON 中key 的名字, 把对应的 value 赋值给 JsonData 的对应字段. - 可以来看看这个Jackson库解析【json】格式的全过程,就会很清楚了
总结与提炼
最后来总结一下本文所介绍的内容📖
- 本文的开端,我们首先了解了如何去构造一个HTTP请求,可以通过form表单、Ajax、postman以及Java Socket的去进行构造,我们主要还是掌握前三种,对于form表单而言最为常见,我介绍了GET请求和POST请求两种,不过对于form表单来说一般发送的还是POST请求居多。
- 知道了如何构造HTTP请求,此时前端就可以在HTTP请求中放置一些内容,后端从这个请求中获取到内容然后处理,那这个前后端的交互就需要有一个接口,它就是【Servlet注解】,前端通过这个
@WebServlet()
里面的参数,决定自己的URL是怎样的;后端呢则通过这个URL来进行精准地接收并做出相应的处理,而且还需根据前端发起的不同请求在不同的方法里做处理(doGet、doPost) - 对于这个前后端的交互逻辑,如果还有不理解的同学可以多去画画图,通过postman去发送各种的HTTP请求,然后通过Fidder抓包进行观察,去清楚双方是如何进行一发一收的这么一个交互逻辑
以上就是本文要介绍的所有内容,感谢您的阅读:rose
: