看了这还分不清简单请求和复杂请求,就来捶我

898 阅读8分钟

在解释复杂请求简单请求之前,我们先说说http的请求类型

根据 HTTP 标准,HTTP 请求可以使用多种请求方法。

HTTP1.0 定义了三种请求方法: GET, POSTHEAD 方法。

HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACECONNECT 方法。

GET:请求指定的页面信息,并返回实体主体

POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。通常导致在服务器上的状态变化或副作用(该请求可能会导致新的资源的建立和/或已有资源的修改)

HEAD:类似于GET请求,但没有响应体,用于获取报头

OPTIONS:用于描述目标资源的通信选项

PUT:从客户端向服务器传送的数据取代指定的文档的内容

PATCH:是对 PUT 方法的补充,用来对已知资源进行局部更新 ,也就是对资源应用部分修改

DELETE:删除指定的资源

TRACE:回显服务器收到的请求,主要用于测试或诊断

CONNECT:建立一个到由目标资源标识的服务器的隧道(HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。)是不是很懵逼?那就说直白点!connect就是将服务器作为代理,让服务器代替用户去访问其他网页,翻墙都懂吧?不用再多说了吧

说完了请求方法,家人们有了一点铺垫,我们再来是步入正轨,是时候聊聊复杂请求和简单请求了。相信大家在开发过程中都会遇到跨域的问题吧?(说没有的在直接叉出去!)也就是这个鬼!

img.jpg

跨域是面试八股文老演员了,没必要再说了,如果家人们想再巩固下,请自行百度,实在不行我也可以后面写篇文章(留言才写哈)

cors的时候,http请求会被划分为两类,即 简单请求和复杂请求。而这两种请求的区别主要在于是否会触发cors预检请求。

摘自MDN

跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是GET以外的 HTTP 请求,或者搭配某些 MIME 类型的POST请求),浏览器必须首先使用 OPTIONS方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

看到这儿应该大概有一个印象了吧。接下来再详细说说: 简单请求: 1.请求方法:GETPOSTHEAD 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请求,像XMLHttpRequestFetch都遵循同源策略。 OPTIONS是一种“预检请求”,浏览器在处理跨域访问的请求时如果判断请求为复杂请求,则会先向服务器发送一条预检请求,根据服务器返回的内容浏览器判断服务器是否允许该请求访问。

在发送真正的请求前, 会先发送一个方法为OPTIONS的预请求(preflight request), 用于试探服务端是否能接受真正的请求,如果options获得的回应是拒绝性质的,比如404\403\500等http状态,就会停止post、put等请求的发出。

ps2:预检请求的特点

  • OPTIONS不会携带请求参数和cookie,也不会对服务器数据产生副作用
  • Access-Control-Request-MethodAccess-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预检请求的结果可以被缓存!!!

image.png

摘自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 字段而引发了预检请求,则这一方法将无法使用。这种情况只能由服务端进行更改。

附带身份凭证的请求(这儿很多人踩过坑): 一般情况下,跨域 XMLHttpRequestFetch 请求,浏览器不会发送身份凭证信息。如果要发送凭证信息,需要去设置 withCredentials ,别以为到这儿就完了,这才刚刚开始呢!如果前端配置了这个 withCredentialstrue ,服务端也需要向客户端发送 cookie 的话,需要服务器端也返回 Access-Control-Allow-Credentials : true响应头信息(服务器端的响应中未携带 Access-Control-Allow-Credentials: true,浏览器将不会把响应内容返回给请求的发送者),且

  • 服务器不能将 Access-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(请求源),请求才会成功执行。

参考资料:

developer.mozilla.org/zh-CN/docs/…

developer.mozilla.org/zh-CN/docs/…