面试中几乎必考题get与post的区别,网上都是千篇一律的回答,但是每次说出来心里都没底,生怕问点啥深入的,反正都不会,于是决定自己玩一玩这东西。
搭建前后端
起一个express服务,同时为了方便操作,把html文件返回到浏览器。
const express = require("express");
const app = express();
const port = 3000;
app.get("/", (req, res) => {
res.sendFile(__dirname + "/httpclient.html");
});
app.get("/get", (req, res) => {
res.send();
});
app.post("/post", (req, res) => {
res.send();
});
app.listen(port, () => {
console.log("runing in port" + port);
});
html页面里面简单放两个操作按钮,然后定义一下请求操作
<!--httpclient.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<button id="btn1">get请求</button>
</div>
<div>
<button id="btn2">post请求</button>
</div>
</body>
<script>
document.querySelector('#btn1').onclick = () => {
const xhr = new XMLHttpRequest()
xhr.open('get', 'get')
xhr.send()
xhr.onreadystatechange = () => {
if(xhr.readyState === 4 && xhr.status === 200){
console.log(xhr.responseText)
}
}
}
document.querySelector('#btn2').onclick = () => {
const xhr = new XMLHttpRequest()
xhr.open('post', 'post')
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
xhr.send()
xhr.onreadystatechange = () => {
if(xhr.readyState === 4 && xhr.status === 200){
console.log(xhr.responseText)
}
}
}
</script>
</html>
此时的页面
get/post的大小
啥也不返回的时候
- 分别点击两个按钮发送最简单的get和post请求,发现两个请求的大小是一样的,都是145B
此时的get和post请求的响应头都是:
但是get的请求头为
post的请求头为:
post请求的请求头比get的请求头多了三个字段: Content-Length, Content-type, Origin
发送空消息的时候
- 接着,我在get和post的接口中都返回一个空字符串: res.send(''),两个请求的大小都变成了226B
这时候的get和post的响应头都多了一点东西:
多了一个Content-Type,一个Etag
但是请求头没有变
这时候再次点击get和post请求,get命中了浏览器缓存返回的304,post还是发的请求:
这个时候再看看get请求的请求头和响应头:
请求头里面多了一个If-None-Math,所以命中缓存
设置请求头的时候
- 再接着,我们只在post请求里面,响应头里面设置cookie:
res.cookie("name",'zhangsan',{maxAge: 900000, httpOnly: true});
- 然后先点击get请求,再点击post请求
这时候get请求还是226B,post请求的响应头里面多了一个Set-Cookie字段,大小变为了323B,再次点击get和post请求,get请求直接304,post请求这次的请求头里面多了一个cookie
总结: 由此可见,get请求和post请求在大小上是一样的,大小和返回的数据,响应头的大小有关系,和get post本身没有关系。
但是我们本来只想探究一下get post的大小问题,但是在对这几个请求头响应头的对比分析中,发现一个小秘密,那就是post请求不会被浏览器缓存命中,没有缓存这一说,但是get请求,第一次发过去的响应头里面就会带上ETag,下一次就会走协商缓存,命中的话就直接返回304,而不是200
这里有时候可能会遇到一个Chrome的小bug,那就是,明明命中了协商缓存,但是返回的还是200,其实返回的应该是304
浏览器缓存
前面不小心遇到了浏览器缓存的事情,可以看见,get请求会被浏览器主动cache,那么接下来,我们就对着强缓存和协商缓存一探究竟
强缓存的触发
我们先在请求头里面都设置一下Cache-Control: max-age=3600,然后发送两次请求
并没有触发强缓存
那么我们看一下在响应头里面设置
可以发现,第二次的get请求直接走的disk cache,但是post没有走缓存
那么请求头对是否走缓存真的一点都控制不了吗,虽然他控制不了走缓存,但是他可以控制不走缓存
我们在请求头里面设置'Cache-Control':'no-cache',v这时,虽然响应头设置了强缓存,但是这时候的get请求就永远都不会命中强缓存,而是发送正常的请求
协商缓存的触发
看过了强缓存,那我们再看一下协商缓存 这里不贴图了,发现无论是在请求头还是响应头里面设置,都无法触发post的缓存
options
对于options请求,一直不太清楚,这次刚好一并弄清楚 首先看面经,知道触发options请求的条件有:
- 必须是跨域请求
- 使用了PUT/DELETE/CONNECT/OPTIONS/TRACE/PATCH任一方法
- 自定义了请求头
- Content-Type的值不等于application/x-www-form-urlencoded、multipart/form-data、text/plain options请求可以主动发,能够检测服务器支持哪些HTTP方法,但是遇到以上情况时,浏览器会自动发一个预检请求,获知服务端是否允许改请求。
没有跨域的时候
首先,来验证一下不跨域行不行:
- httpclient.html中加一个发options请求的按钮
document.querySelector('#btn3').onclick = () => {
const xhr = new XMLHttpRequest()
// xhr.open('options', 'http://localhost:8000/options')
xhr.open('options', 'options')
xhr.send()
xhr.onreadystatechange = () => {
if(xhr.readyState === 4 && xhr.status === 200){
console.log(xhr.responseText)
}
}
}
- httpserve.js中新加一个options的接口
app.get("/options", (req, res) => {
res.send('');
});
- 浏览器工具这里不能只显示fetch/chr,要切换到全部,不然就算有options也看不见
- 依次点击三个请求
get和post请求和之前一样,options请求我们写的虽然是返回的空字符串,但是实际上返回的是支持的http方法
这里的options是有返回的,也有大小
options的请求头和get也是一样的,但是响应头里面多了一个allow: get, head
可见不跨域的时候无法触发自动的options请求
简单跨域的时候
- 新建一个serve2.js, 里面开启一个8000端口的服务
- 利用cors中间件处理一下跨域
- 把页面三个按钮对应请求的地址都改为断口为8000的接口
- 依次发送请求 结果如下:
get和post没有引起options请求,这是因为我们虽然跨域了,但是没有符合上面提到的那些要求,所以是简单跨域,不会引起额外的options预检,但是下面的options本身报了一个错误,同时引发了一次options预检,同时注意到,两个options请求的类型是不一样的,一个是XHR,一个是preflight。
看一下options请求的响应和信息
返回的204,204的意思是请求成功执行,但是没有数据。 响应头里面多了一个Access-Content-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
这个字段的意思是: 响应首部 Access-Control-Allow-Methods 在对 preflight request.(预检请求)的应答中明确了客户端所要访问的资源允许使用的方法或方法列表。
可以看见里面并没有options方法,所以会报错。
复杂跨域请求
下面我们触发一下复杂跨域请求
- get请求里面设置额外的请求头xhr.setRequestHeader('token', 'abc')
- post请求里面设置Content-Type为application/json 再次尝试:
可以看见get,post都触发了一次options请求。options报错的原因也和简单跨域时候一样,不支持这个方法。
总结
经过这么一折腾,get,post的区别,以及options的相关就更熟悉了,比死记硬背效果要好,看别人的文章背下来的,终究不如自己试验一下来的清楚!!!