1.前后应用从浏览器端向服务器发送 HTTP 请求(请求报文)
2.后台服务器接收到请求后,调度服务器应用处理请求,向浏览器端返回HTTP响应(响应报文)
3.浏览器端接收到响应,解析显示响应体/调用监视回调
HTTP 请求报文由请求行、请求头、空行和请求包体(body)组成。
- 请求行
methodurl:GET/product_detail?id=2POST/login- 多个请求头
Host:www.baidu.com
Cookie:BAIDUID=AD3B0FA706E;BIDUPSID=AD3B0FA706;
Content-Type:application/x-www-form-urlencoded
或者application/json- 请求体
username=tom&pwd=123
{“username”:“tom”,“pwd”:123}
HTTP 响应报文由状态行、响应头部、空行和响应包体(body)组成
- 响应状态行:statusstatusText
- 多个响应头
Content-Type:text/html;charset=utf-8
Set-Cookie:BD_CK_SAM=1;path=/- 响应体html文本/json文本/js/css/图片…
100,接受的请求正在处理,信息类状态码
200
OK 请求成功。一般用于GET与POST请求
201
Created 已创建。成功请求并创建了新的资源
301,永久性重定向,表示资源已被分配了新的 URL
比如,我们访问 http://www.baidu.com会跳转到 https://www.baidu.com,发送请求之后,就会返回301状态码,然后返回一个location,提示新的地址,浏览器就会拿着这个新的地址去访问。
302,临时性重定向,表示资源临时被分配了新的 URL
比如未登陆的用户访问用户中心重定向到登录页面
303,表示资源存在另一个URL,用GET方法获取资源
304,(未修改)自从上次请求后,请求网页未修改过。服务器返回此响应时,不会返回网页内容
401
Unauthorized 未授权/请求要求用户的身份认证
404
NotFound 服务器无法根据客户端的请求找到资源
500
InternalServerError 服务器内部错误,无法完成请求
1.请求方式
- GET:从服务器端读取数据 查 (get请求不能发请求体参数)
- POST:向服务器端添加新数据 增
- PUT:更新服务器端已经数据 改
- 4DELETE:删除服务器端数据 删
2.请求参数
1. query参数(查询字符串参数)
- 参数包含在请求地址中,格式是:
/xxxx?name=tom&age=18
- 敏感数据不要使用query参数,因为参数是地址的一部分,比较危险
- 备注:query参数又称查询字符串参数,编码方式为
urlencoded
2. params参数
- 参数包含在请求地址中,格式是:
http://localhost:3000/add_person/tom/18
- 敏感数据不要使用query参数,因为参数是地址的一部分,比较危险
3. 请求体参数
- 参数包含在请求体中,可以通过浏览器开发工具查看
- 常用两种格式:
格式一:urlencoded
格式
例如:name=tom&age=18
对应请求头:Content-Type:application/x-www-form-urlencoded
格式二:json格式
例如{“name”:“Tom”,“age”:18}
对应请求头:Content-Type:application/json
特别注意
- GET请求不能携带请求体参数,因为GET请求没有请求体
- 理论上,一次请求可以随意使用上述三种类型参数中的任何一种,甚至一次请求3个参数可以用三种形式携带,但一般不这么做。
- 一般来说,有一些约定俗成的规矩:
(1)例如form表单发送POST请求时:自动使用请求体参数,用urlencoded编码。
(2)例如jQuery发送ajax-post请求时:自动使用请求体参数,用urlencoded编码。- 开发中请求到底发给谁?用什么请求?携带什么参数?。。。参考项目的API接口文档
三次握手
)不允许断点续传
,缓存处理,
在HTTP1.0中主要使用header里的If-Modified-Since
,Expires来作为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略如Entity tag
,If-None-Match
,If-Unmodified-Since
,If-Match
、Last-Modify
等更多可供选择的缓存头来控制缓存策略。
带宽优化及网络连接的使用
HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
错误通知的管理:
在HTTP1.1中新增了24个错误状态响应码,如409(conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上某个资源被永久性删除。
Host头处理:
在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机,并且他们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
长连接(Connection: keep-alive)
:
HTTP1.1支持长连接和请求的流水线处理,在一个TCP连接上可以传送多个HTTP请求和相应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection:keep-alive
,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。(串行)
多路复用
HTTP2.0使用了多路复用的技术,做到同一个连接并发处理多个请求
,而且并发请求的数量比HTTP1.1打了好几个数量级。HTTP1.1也可以多建立几个TCP连接,来处理更多并发的请求,但是创建TCP连接本身也是有开销的。(并行)
头部数据压缩
在HTTP1.1中,HTTP请求和相应都是由状态行、请求/响应头部、消息主体三部分组成。一般而言,消息主体都会经过gzip压缩,或者本身传输的就是压缩过后的二进制文件,但状态行和头部却没有经过任何压缩,直接以纯文本传输。随着Web功能越来越复杂,每个页面产生的请求数也越来越多,导致消耗在头部的流量越来越多,尤其是每次都要传输UserAgent、Cookie这类不会频繁变动的内容,完全是一种浪费。
HTTP1.1不支持header数据的压缩,HTTP2.0使用HPACK算法对header的数据进行压缩,这样数据体积小了,在网络上传输就会更快。
(http 2.0使用encoder减少需要传输的header大小,通讯双方各自cache一份header field表,避免了header的重复传输,又减少了传输的大小)
服务端推送
服务端推送是一种在客户端请求之前发送数据的机制。网页使用了许多资源:HTML、样式表、脚本、图片等等。在HTTP1.1中这些资源每一个都必须明确地请求。这是一个很慢的过程。浏览器从获取HTML开始,然后在它解析和评估页面地时候,增量地获取更多的资源。因为服务器必须等待浏览器做每一个请求,网络经常是空闲和未充分使用地。
为了改善延迟,HTTP2.0引入server push,它允许服务端推送资源给浏览器,在浏览器明确的请求之前,免得客户端再次创建连接发送请求到服务器端获取。这样客户端可以直接从本地加载这些资源,不用再通过网络。
数据流优先级
对数据流可以设置优先值,这个优先值决定了客户端和服务端处理不同的流采用不同的优先级策略。(例如浏览器在等待关键的 CSS 或者 JS 文件完成对页面的渲染时,服务器却在专注于加载图片)
使用二进制分帧层
在应用层与传输层之间增加一个二进制分帧层,在不改动 HTTP 的语义(HTTP 方法、状态码、URI 及首部字段)的情况下,将所有传输的信息分割为更小的消息和帧,并对它们采用二进制格式编码,改进传输性能,实现低延迟和高吞吐量。
其中 HTTP1.x 的首部信息会被封装到 Headers 帧,而我们的 request body 则封装到 Data 帧里面。
基于UDP减少了TCP三次握手
及TLS握手时间:
HTTP1.0/1.1/2.0/HTTPS都使用了TCP
进行传输,HTTP2/HTTPS还需要使用TLS
协议来进行安全传输。这就存在两个握手延迟,而基于无连接UDP协议的QUIC,连接建立时只需要一次交互,相当于半个握手的时间。
解决多路复用丢包时的线头阻塞问题:
QUIC中一个连接上的多个stream之间没有依赖。所以当发生丢包时,只会影响当前的stream,从而避免线头阻塞问题。
优化重传策略:
TCP中重传序号一致
问题而导致的发送端不能明确ACK是初始包的确认还是对重传包的确认。为了避免这一问题,QUIC发送端设置初始与重传的每一个封包都改用一个新
的编号,unique packet number,每一个编号都唯一而且严格递增,这样每次在收到ACK时,就可以依据编号明确的判断这个ACK是来自初始封包或者是重传封包。
流量控制:
QUIC为了避免出现流量极慢的stream,采用了连线层 (connection flow control) 和 Stream 层的 (stream flow control) 流量控制,限制单一 Stream 可以占用的最大buffer size。
连接迁移:
QUIC 连接不以四元组作为标识(源 IP、源端口、目的 IP、目的端口)(连接变换时就需要切换TCP连接),而是使用一个 64 位的随机数Connection ID,对应每个stream,即使 IP 或者端口发生变化,只要 Connection ID 没有变化,那么连接依然可以维持。
- REST API: restful (Representational State Transfer (资源)表现层状态转化)
(1) 发送请求进行CRUD 哪个操作由请求方式来决定
(2) 同一个请求路径可以进行多个操作
(3) 请求方式会用到GET/POST/PUT/DELETE- 非REST API: restless
(1) 请求方式不决定请求的CRUD 操作
(2) 一个请求路径只对应一个操作
(3) 一般只有GET/POST
用来快速搭建REST API 的工具包
10秒快速建一个服务器
在线文档: https://github.com/typicode/json-server
npm install -g json-server
db.json
json-server --watch db.json
#db.json 文件
{
"posts": [
{ "id": 1, "title": "json-server", "author": "typicode" },
{ "id": 2, "title": "json-server2", "author": "typicode" }
],
"comments": [
{ "id": 1, "body": "some comment", "postId": 1 }
],
"profile": { "name": "typicode" }
}
http://localhost:3000/posts
http://localhost:3000/posts/1
测试 GET/POST/PUT/DELETE
json-server 模拟的服务器,id必须用params带,其他的可以用请求体:
http://localhost:3000/students/3
GET query参数
get Params参数
请求体 urlencoded形式
请求体 json形式
ajax
(xhr)请求是一种特别的http
请求XHR
或fetch
发出的才是ajax
请求,其它所有的都是非ajax请求接口文档是:由 工具 api-doc 写一些注释,就能生成
拿到文档 先用postman测试
引入
- axios调用的返回值是Promise实例
- 成功的值叫response,失败的叫error
- axios 成功的值是一个axios封装的response对象,服务器返回的真实数据在response.data中
- 携带query参数的配置项叫做params
- 携带parmas 需要自己拼接
axios(config): 通用/最本质的发任意类型请求的方式
axios(url[, config]):
可以只指定url 发get 请求
axios.request(config):
等同于axios(config)
axios.get(url[, config]):
发get 请求
axios.delete(url[, config]):
发delete 请求
axios.post(url[, data, config])
: 发post 请求
axios.put(url[, data, config]):
发put 请求
axios.defaults.xxx:
请求的默认全局配置(method\baseURL\params\timeout…)
axios.interceptors.request.use():
添加请求拦截器
axios.interceptors.response.use():
添加响应拦截器
axios.create([config]):
创建一个新的axios(它没有下面的功能)
axios.Cancel():
用于创建取消请求的错误对象
axios.CancelToken():
用于创建取消请求的 token 对象
axios.isCancel(): 是否是一个取消请求的错误
axios.all(promises)
: 用于批量执行多个异步请求
axios.spread(): 用来指定接收所有成功数据的回调函数的方法
————————————————
<script type = "text/javascript" src="./js/axios.min.js"></script>
<button id="btn1"> 点我获取所有人</button>
<button id="btn1"> 点我获取某个人</button>
<input id="person_id" type="test" placeholder="请输入一个人的id"> </input>
<button id="btn3"> 点我添加一个人</button>
<input id="person_name" type="test" placeholder="请输入名字"> </input>
<input id="person_age" type="test" placeholder="请输入年龄"> </input>
<button id="btn4"> 点我更新一个人</button>
<input id="person_update_id" type="test" placeholder="请输入id"> </input>
<input id="person_update_name" type="test" placeholder="请输入名字"> </input>
<input id="person_update_age" type="test" placeholder="请输入年龄"> </input>
<button id="btn5"> 点我删除一个人</button>
<input id="person_delete_id" type="test" placeholder="请输入闪删除的的id"> </input>
const btn1 = document.getELementById('btn1')
const btn2 = document.getELementById('btn2')
const btn3 = document.getELementById('btn3')
const btn4 = document.getELementById('btn4')
const btn4 = document.getELementById('btn4')
const btn5 = document.getELementById('btn5')
const personId = document.getElementByid('person_id')
const personName = document.getElementByid('person_name')
const personAge = document.getElementByid('person_age')
const personUpdateId = document.getElementByid('person_update_id')
const personUpdateName = document.getElementByid('person_update_name')
const personUpdateAge = document.getElementByid('person_update_age')
const personDeleteId = document.getElementByid('person_delete_id')
# 获取所有人信息-发送GET请求-不携带参数
btn1.onclick=()=>{
# 完整版
axios({
url:'http://localhost:5000/test1?delay=5000',//请求地址,延迟5秒
method:'GET',
timeout:2000,
}).then(
response=>{ console.log('请求成功了!',response.data);},
error=>{console.log('请求失败了!',error);}
)
# 精简版
axios.get('http://localhost:5000/person').then(
response=>{ console.log('请求成功了!',response.data); },
error=>{console.log('请求失败了!',error);}
)
}
#获取某个人的信息-发送GET请求-携带query参数
btn2.onclick=()=>{
# 完整版
axios({
url:'http://localhost:5000/person',
method:'GET',
params:{id:personId.value}
# 此处写的是params,但是携带的是query参数
}).then(
response=>{ console.log('请求成功了!',response.data);},
error=>{console.log('请求失败了!',error);}
)
# 精简版
axios.get('http://localhost:5000/person',{params:{id:personId.value} }).then(
response=>{ console.log('请求成功了!',response.data); },
error=>{console.log('请求失败了!',error);}
)
}
# 添加一个人,年龄和名字--发送POST请求--携带json编码参数或urlencoded编码
btn3.onclick=()=>{
# 完整版
axios({
url:'http://localhost:5000/person',
method:'POST',
# 1. 携带请求体参数,json编码
data:{name:personName.value,age:personAge}
# 2. 携带请求体参数,urlencoded编码
data:`name=${personName.value}&age=${personAge.value}`
}).then(
response=>{ console.log('请求成功了!',response.data);},
error=>{console.log('请求失败了!',error);}
)
# 精简版
# 1. 携带请求体参数,json编码
axios.post('http://localhost:5000/person',
{name:personName.value,age:personAge} ).then(
# 2. 携带请求体参数,urlencoded编码
axios.post('http://localhost:5000/person',
`name=${personName.value}&age=${personAge.value}`).then(
response=>{ console.log('请求成功了!',response.data); },
error=>{console.log('请求失败了!',error);}
)
}
# 更新某个人--发送PUT请求--携带json编码参数或urlencoded编码
btn4.onclick=()=>{
# 完整版
axios({
url:'http://localhost:5000/person',
method:'PUT',
data:{id:personUpdateId.value,
name:personUpdateName.value,
age:personUpdateAge.value,
}
}).then(
response=>{ console.log('请求成功了!',response.data);},
error=>{console.log('请求失败了!',error);}
)
# 精简版
axios.put('http://localhost:5000/person',{id:personUpdateId.value,
name:personUpdateName.value,
age:personUpdateAge.value}).then(
response=>{ console.log('请求成功了!',response.data); },
error=>{console.log('请求失败了!',error);}
)
}
# 删除某个人--发送DELETE请求--携带parmas
btn5.onclick=()=>{
# 完整版
axios({
url:`http://localhost:5000/person/${personDeleteId.value}`,
method:'DELETE',
}).then(
response=>{ console.log('请求成功了!',response.data);},
error=>{console.log('请求失败了!',error);}
)
}
axios.defaults.xxx:
请求的默认全局配置
<script type = "text/javascript" src="./js/axios.min.js"></script>
# 给 axios配置默认配置
axios.defaults.timeout = 2000
axios.defaults.headers = {name:atjm}
axios.defualts.baseURL='http://localhost:5000'
<button id="btn"> 点我获取所有人</button>
const btn1 = document.getELementById('btn')
btn.onclick=()=>{
# 完整版
axios({
url:'http://localhost:5000/person',//请求地址,
method:'GET', //请求方式
//params:{a:1,b:2},//配置query
//data:{a:3,d:3}, //配置请求体 参数 (json)
//data:'e=5&f=6' //配置请求体参数(urlencoded)
timeout:2000, //配置超时时间
header:{demo:123} //配置请求头
responseType:'json' //配置相应数据格式(默认值)
}).then(
response=>{ console.log('请求成功了!',response.data);},
error=>{console.log('请求失败了!',error);}
)
}
axios.create
方法axios.create([config])
:创建一个新的axios(它没有下面的功能)
axios.create(config)
- 根据指定配置创建一个新的
axios
,也就就每个新axios
都有自己的配置- 新
axios
只是没有取消请求和批量发请求的方法,其它所有语法都是一致的- 为什么要设计这个语法?
(1)需求:项目中有部分接口需要的配置与另一部分接口需要的配置不太一样,
(2)解决:创建2个新axios,每个都有自己特有的配置,分别应用到不同要求的接口请求中
const axios2 =axios.create({
timeout: 2000
//headers: {name:atjm}
baseURL:'http://localhost:5000'
})
# 需要放在defaluts前方
btn.onclick=()=>{
# 完整版
axios2({
url:'http://localhost:5000/person',//请求地址,
method:'GET', //请求方式
//params:{a:1,b:2},//配置query
//data:{a:3,d:3}, //配置请求体 参数 (json)
//data:'e=5&f=6' //配置请求体参数(urlencoded)
timeout:2000, //配置超时时间
header:{demo:123} //配置请求头
responseType:'json' //配置相应数据格式(默认值)
}).then(
response=>{ console.log('请求成功了!',response.data);},
error=>{console.log('请求失败了!',error);}
)
}
axios
中的拦截器
axios
请求拦截器
- 是什么?
在真正发送请求前执行的一个回掉函数- 作用:对所有请求做统一的处理:追加请求头、追加参数、界面loading提示等等
axios
响应拦截器
- 是什么?
得到相应之后执行的一组回掉函数
2.作用:若请求成功,对成功的数据进行处理,
若请求失败,对失败进行进一步操作
拦截器函数/
ajax
请求/请求的回调函数的调用顺序
说明: 调用axios()
并不是立即发送ajax
请求, 而是需要经历一个较长的流程
流程: 请求拦截器2 => 请求拦截器1 => 发ajax
请求 => 响应拦截器1 => 响应拦截器2 => 请求的回调
注意: 此流程是通过promise
串连起来的, 请求拦截器传递的是config
, 响应拦截器传递的是response
————————————————
#请求拦截器
axios.interceptors.request.use((config)=>{
if(Date.now()%2===0){
config.headers.token='atuge'
}
return config;
})
# 响应拦截器
axios.interceptors.response.use(
response=>{
console.log('相应拦截器成功的回掉执行了',response)
if(Date.now()%2===0) return response.data
else return '时间戳不是偶数,不能给你数据'
},
error=>{
console.log('相应拦截器失败的回掉执行了')
alert(error)
return new Promise(()=>{})
}
)
btn.onclick=() async() =>{
const result =await axios.get('http://localhost:5000/person')
console.log(result);
}
axios
中取消请求
- 基本流程
配置cancelToken
对象
缓存用于取消请求的cancel函数
在后面特定时机调用cancel函数取消请求
在错误回调中判断如果error是cancel,做相应处理- 实现功能
点击按钮,取消某个正在请求中的请求
在请求一个接口前,取消前面一个未完成的请求
取消请求1
let cancel;
btn1.onclick=() async() =>{
# 完整版
axios({
url:'http://localhost:5000/person',
cancelToken:new CancelToken((c)=>{
console.log(c) //c是一个函数,调用c可以关闭本次请求
cancel=c
})
}).then(
response=>{ console.log('请求成功了!',response.data);},
error=>{console.log('请求失败了!',error);}
)
}
btn2.onclick=()=>{
cancel('任性,就是不要了')
}
取消请求2
#CancelToken 能为一次请求“打标注"
const {CancelToken,isCancel}= axios
let cancel;
btn1.onclick=() async() =>{
if(cancel) cancel()
axios({
url:'http://localhost:5000/test?delay=3000',
cancelToken:new CancelToken((c)=>{
console.log(c) //c是一个函数,调用c可以关闭本次请求
cancel=c
})
}).then(
response=>{ console.log('请求成功了!',response.data);},
error=>{
if(isCancel(error))
console.log('用户取消了请求,原因是',error,message)
else
console.log('请求失败了!',error);}
)
}
btn2.onclick=()=>{
cancel('任性,就是不要了')
}
取消请求3 取消请求+拦截器
#CancelToken 能为一次请求“打标注"
const {CancelToken,isCancel}= axios
let cancel;
#请求拦截器
axios.interceptors.request.use((config)=>{
if(cancel) cancel('取消了')
config.CancelToken = new CancelToke((c)=> cancel=c)
return config;
})
# 响应拦截器
axios.interceptors.response.use(
response=>{ return response.data },
error=>{
if(isCancel(error))
console.log('用户取消了请求,原因是',error.message)
else
console.log('失败了',error)
return new Promise(()=>{})
}
)
btn1.onclick=() async() =>{
const result= await axios.get('http://localhost:5000/test?delay=3000')
}
btn2.onclick=()=>{
cancel('任性,就是不要了')
}
axios
批量发请求btn1.onclick=() async() =>{
axios.all([
axios.get('http://localhost:5000/test'),
axios.get('http://localhost:5000/test2?delay=3000'),
axios.get('http://localhost:5000/test3')
]).then(
response=>{ console.log('请求成功了!',response.data);},
error=>{console.log('请求失败了!',error);}
)
}
Fetch
fetch是一个底层API,浏览器原生支持的;
fetch()
的功能与 XMLHttpRequest
基本相同。
fetch()
使用 Promise
,不使用回调函数,因此大大简化了写法,写起来更简洁。
它的基本用法如下
fetch('https://api.github.com/users/ruanyf')
.then(response => response.json())
.then(json => console.log(json))
.catch(err => console.log('Request Failed', err));
Fetch优点
存在问题
fetch
只对网络请求报错,对400,500
都当作成功的请求,服务器返回400,500
错误码时并不会reject
只有网络错误这些导致请求不能完成fetch 才会被 reject
。cookie
,需要添加配置项 fetch(url,{credenials:"include"})
about
,不支持超时控制,使用setTimeout及Promise.reject
的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费。XHR
可以,axios
也可以Axios
axios 是 社区封装的组件 axios是一个基于Promise
用于浏览器
和node.js
的HTTP客户端,本质上是对原生XHR的封装,只不过他是Promise的实现版本,符合最新的ES规范
优点:
XMLHttpRequest
Promise Api
csrf
并发
请求的接口,(重要,方便了很多操作)拦截请求和响应
取消
请求JSON
数据总结:axios 既提供了并发的封装
,也没有fetch的各种问题,而且体积较小,首选
面试回答:
1. fetch 是一个底层的API,浏览器原生支持,Axios是一个封装好的框架;
2. axios (1)支持浏览器和nodejs发送请求,前后端发请求;
(2)支持promise语法
(3)支持自动解析JSON
(4)支持中断请求
(5)支持拦截请求
(6)支持请求进度检测
(7)支持客户端防止CSRF
总之: 封装比较好
3.fetch
优点:1.浏览器原生支持的API
2.原生支持promise api
3.语法简洁, 符合es标准规范
4.是whatwa组织提出,现在也是w3c规范
缺点:1.不支持文件上传进度检测
2.使用不完美,需要封装
3.不支持请求终止
4.默认不带cookie
总之 缺点是需要封装,优点:底层原生支持