在解释复杂请求和简单请求之前,我们先说说http的请求类型
。
根据 HTTP
标准,HTTP
请求可以使用多种请求方法。
HTTP1.0
定义了三种请求方法: GET
, POST
和 HEAD
方法。
HTTP1.1
新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE
和 CONNECT
方法。
GET:请求指定的页面信息,并返回实体主体
POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。通常导致在服务器上的状态变化或副作用(该请求可能会导致新的资源的建立和/或已有资源的修改)
HEAD:类似于GET请求,但没有响应体,用于获取报头
OPTIONS:用于描述目标资源的通信选项
PUT:从客户端向服务器传送的数据取代指定的文档的内容
PATCH:是对 PUT 方法的补充,用来对已知资源进行局部更新 ,也就是对资源应用部分修改
DELETE:删除指定的资源
TRACE:回显服务器收到的请求,主要用于测试或诊断
CONNECT:建立一个到由目标资源标识的服务器的隧道(HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。)是不是很懵逼?那就说直白点!connect就是将服务器作为代理,让服务器代替用户去访问其他网页,翻墙都懂吧?不用再多说了吧
说完了请求方法,家人们有了一点铺垫,我们再来是步入正轨,是时候聊聊复杂请求和简单请求了。相信大家在开发过程中都会遇到跨域的问题吧?(说没有的在直接叉出去!)也就是这个鬼!
跨域是面试八股文老演员了,没必要再说了,如果家人们想再巩固下,请自行百度,实在不行我也可以后面写篇文章(留言才写哈)
cors
的时候,http请求会被划分为两类,即 简单请求和复杂请求。而这两种请求的区别主要在于是否会触发cors
预检请求。
摘自MDN
:
跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是GET以外的 HTTP 请求,或者搭配某些 MIME 类型的POST请求),浏览器必须首先使用 OPTIONS方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
看到这儿应该大概有一个印象了吧。接下来再详细说说:
简单请求:
1.请求方法:GET
、POST
、HEAD
2.除了以下的请求头字段之外,没有自定义的请求头(也就是不能自定义请求头header),是可以有其它标准请求头的
Accept
Accept-Language
Content-Language
Content-Type
range
(只允许简单的范围标头值 如bytes=256-
或 bytes=127-255
,Firefox 还没有将Range
实现为安全的请求标头。)Content-Type
的值只有以下三种(Content-Type
一般是指在post请求中,get请求中设置没有实际意义)text/plain
multipart/form-data
application/x-www-form-urlencoded
3.如果请求是使用 XMLHttpRequest
对象发出的,在返回的 XMLHttpRequest.upload
对象属性上没有注册任何事件监听器;也就是说,给定一个 XMLHttpRequest
实例 xhr,没有调用 xhr.upload.addEventListener()
,以监听该上传请求。
4.请求中没有使用 ReadableStream
对象。
复杂请求: 排除法嘛,除了简单请求,那剩下的自然就是复杂请求了。还是给大家举个例子嘛,比方说
- 用到了
PUT、DELETE
等请求方法 Content-Type
的值是上述三种以外的- 用到了自定义
header
这几种情况都会触发预检请求,看看服务端是否支持 复杂请求表面上看起来和简单请求使用上差不多,但实际上浏览器发送了不止一个请求。其中最先发送的是一种"预请求",此时作为服务端,也需要返回"预回应"作为响应。预请求实际上是对服务端的一种权限请求,只有当预请求成功返回,实际请求才开始执行(这也就是为啥有的同学们会发现自己方法只调用了一次,请求却有两个,原因就是在这儿)
ps1:
什么是预检请求
浏览器的同源策略(出来混,总得注意安全啦),浏览器会限制从脚本发起的跨域HTTP
请求,像XMLHttpRequest
和Fetch
都遵循同源策略。
OPTIONS
是一种“预检请求”,浏览器在处理跨域访问的请求时如果判断请求为复杂请求,则会先向服务器发送一条预检请求,根据服务器返回的内容浏览器判断服务器是否允许该请求访问。
在发送真正的请求前, 会先发送一个方法为OPTIONS
的预请求(preflight request), 用于试探服务端是否能接受真正的请求,如果options
获得的回应是拒绝性质的,比如404\403\500等http
状态,就会停止post、put
等请求的发出。
ps2:预检请求的特点
OPTIONS
不会携带请求参数和cookie
,也不会对服务器数据产生副作用Access-Control-Request-Method
和Access-Control-Request-Headers
Access-Control-Request-Method
:内容是实际请求的种类,告诉服务器实际请求使用的方法
Access-Control-Request-Headers
:内容是一个以逗号分隔的列表,告诉服务器实际请求复杂请求所使用的头部
这个时候服务端也很热情啊,会告诉浏览器小老弟一些信息
Access-Control-Allow-Origin
:域,这个是肯定会返回的Access-Control-Allow-Methods
:服务器允许客户端使用那些方法发起请求。这个也是肯定会返回的Access-Control-Allow-Headers
:当预请求中包含Access-Control-Request-Headers
时一定会有)这是对预请求当中Access-Control-Request-Headers
的回复,也是以逗号分隔的列表,可以返回所有支持的头部表明服务器允许请求中携带字段
现在基本都搞懂了吧,可能有些同学就要问了,那我一言不合就发两次,有没有办法优化啊???在线等,挺急的
别急 我会出手!
OPTIONS
预检请求的结果可以被缓存!!!
摘自MDN
:
返回结果可以被缓存的最长时间(秒)。 在 Firefox 中,上限是 24 小时 (即 86400 秒)。 在 Chromium v76 之前, 上限是 10 分钟(即 600 秒)。 从 Chromium v76 开始,上限是 2 小时(即 7200 秒)。 Chromium 同时规定了一个默认值 5 秒。 如果值为 -1,表示禁用缓存,则每次请求前都需要使用 OPTIONS 预检请求。
举个栗子
将预检请求的结果缓存 10 分钟:Access-Control-Max-Age: 600
现在基本都搞懂了吧,下面再拓展一下 简单请求与复杂请求的跨域设置:
简单请求,在进行CORS
设置的时候,只需要设置
Access-Control-Allow-Origin:* // 如果只是针对某一个请求源进行设置的话,可以设置为具体的值 Access-Control-Allow-Origin: // 例如 'http://www.***.com'
复杂请求,则需要设置不同的响应头来回应预检请求时携带的相应请求头信息
假如请求的时候是这样的
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-CUSTOMER-HEADER, Content-Type
那么相应的响应头信息就是
Access-Control-Allow-Origin: http://xxx
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
重定向:
如果发送的预检请求被进行了重定向,大多数的浏览器都不支持对预检请求的重定向。我们可以通过先发送一个简单请求的方式,获取到重定向的url
,然后再去请求这个url
。
如果请求是由于存在 Authorization
字段而引发了预检请求,则这一方法将无法使用。这种情况只能由服务端进行更改。
附带身份凭证的请求(这儿很多人踩过坑):
一般情况下,跨域 XMLHttpRequest
或 Fetch
请求,浏览器不会发送身份凭证信息。如果要发送凭证信息,需要去设置 withCredentials
,别以为到这儿就完了,这才刚刚开始呢!如果前端配置了这个 withCredentials
为 true
,服务端也需要向客户端发送 cookie
的话,需要服务器端也返回 Access-Control-Allow-Credentials : true
响应头信息(服务器端的响应中未携带 Access-Control-Allow-Credentials: true
,浏览器将不会把响应内容返回给请求的发送者),且
- 服务器不能将 A
ccess-Control-Allow-Origin
的值设为通配符“*”
,而应将其设置为特定的域,如:Access-Control-Allow-Origin: https://xxx
- 服务器不能将
Access-Control-Allow-Headers
的值设为通配符“*”
,而应将其设置为标头名称的列表,如:Access-Control-Allow-Headers: X-PINGOTHER
,Content-Type
- 服务器不能将
Access-Control-Allow-Methods
的值设为通配符“*”
,而应将其设置为特定请求方法名称的列表,如:Access-Control-Allow-Methods: POST, GET
请求的首部中携带了Cookie
信息,如果 Access-Control-Allow-Origin
的值为“*”
,请求将会失败,将Access-Control-Allow-Origin
的值设置为 http://xxx
(请求源),请求才会成功执行。
参考资料: