CORS是一种常见的跨域机制,一般由服务端提供一个Access-Control-Allow-Origin头来解决问题,但是这仅对一些“简单请求”有效。那么何谓“简单请求”呢?根据MDN的介绍,一个请求如果同时满足:

  1. 请求方法为GET HEAD POST中任意一种
  2. 请求头仅包含浏览器(代理)添加的头字段(User-Agent, Connection)、描述资源的头字段(Accept, Accept-Encoding, Accept-Language)等
  3. 如果请求头有Content-Type,其值仅为application/x-www-form-urlencoded, multipart/form-data, text/plain其中之一

那么它就是一个“简单请求”。只要有一条不满足,则不是“简单请求”,在CORS请求时需要先进行预检(preflight)。

预检指的是,浏览器先发一个OPTIONS请求到服务器,并使用Access-Control-Request-Method头标识即将发起请求的方法,使用Access-Control-Request-Headers标识不在上文第2条标识范围内的头字段名。服务器接收到预检请求之后,需要返回Access-Control-Allow-OriginAccess-Control-Allow-Methods标识支持预检的方法,Access-Control-Allow-Headers标识能够新增的请求字段名,同时返回Access-Control-Max-Age表明客户端可以缓存预检成功状态多少秒,在有效期内再次发送请求时不需预检。

预检请求成功返回后,浏览器会检验即将发送的请求方法、请求头是否符合服务端需要,如果满足则发送实际的请求,否则会在控制台报一个CORS错误。

下面来模拟一下整个过程:

客户端

客户端代码比较简单,我们构建一个XMLHttpRequest对象,然后发送一个非"简单请求",代码如下:

var req = new XMLHttpRequest()

req.onreadystatechange = function (e) {
if (req.readyState === 4 && req.status === 200) {
console.log('请求成功')
}
} req.open('GET', 'http://127.0.0.1:3003/static/pic', true)
req.setRequestHeader('X-Pong', 'ping')
req.send()

我们使用GET方法,但是新增了X-Pong头,这不是一个常规的头部,所以浏览器会发预检请求。

服务端

使用koa搭建一个简单的响应服务器

const fs = require('fs')
const path = require('path')
const util = require('util')
const Koa = require('koa2')
const Router = require('koa-router') const app = new Koa()
const router = new Router() router
.get('/', (ctx, next) => {
ctx.body = 'index'
})
.options('/static/pic', (ctx, next) => {
let reqMethod = ctx.headers['access-control-request-method']
let origin = ctx.headers['origin'] || '*'
// 打印预检请求头
console.log(util.inspect(ctx.headers)) if (/get/i.test(reqMethod)) {
ctx.set({
// 只支持GET方法
'Access-Control-Allow-Method': 'GET',
// 支持额外的X-Pong头部字段
'Access-Control-Allow-Headers': 'X-Pong',
'Access-Control-Allow-Origin': origin,
// 预检有效缓存10分钟
'Access-Control-Max-Age': '600'
})
ctx.status = 204
} else {
ctx.status = 405
ctx.body = '<p>This resource can only be preflight by GET.</p>'
}
})
.get('/static/pic', (ctx, next) => {
// 这是正常的跨域CORS发送文件
let origin = ctx.headers['origin'] || '*'
let filePath = path.resolve(__dirname, '../mime/pic.jpg')
ctx.set('Access-Control-Allow-Origin', origin)
ctx.type = path.extname(filePath)
ctx.body = fs.createReadStream(filePath)
}) app.use(router.routes())
app.use(router.allowedMethods()) app.listen(3003, function () {
console.log('server running on port 3003...')
})

实验结果

启动服务器,然后在Chrome浏览器的Sources-Snippets面板中运行客户端代码,即可在node控制台看到浏览器发送的预检请求头:



打开浏览器的NetWork面板可以发现请求发送了两次,第一次是OPTIONS预检请求,第二次是GET方法获取图片。通过检视我们发现了预检响应头。



当然这个预检是满足要求的。我们有一些方法使得预检不能被满足:

  1. Access-Control-Allow-Headers中的X-Pong去掉,此时客户端要发送X-Pong但不被预检允许;
  2. Access-Control-Allow-Method改成其他方法,此时客户端发送GET请求获取资源不被允许;

这两种情况下浏览器控制台都会报错,感兴趣的不妨试下。

最后Access-Control-Max-Age表示预检成功结果缓存的时间,它受浏览器缓存控制。如果浏览器运行在disable cache模式,请求头默认携带cache-control: no-cachecache-control: max-age=0,此时预检缓存也将失效,OPTIONS请求会重新发送。

CORS预检的更多相关文章

  1. CORS预检请求详谈

    引言 最近在项目中因前后端部署不同地方,前端在请求后端api时发生了跨域请求,我们采用CORS(跨域资源共享)来解决跨域请求,这需要前后端的配合来完成.在这一过程中,后端支持了CORS跨域请求后,前端 ...

  2. ASP Net Core – CORS 预检请求

    CORS(跨源资源共享)是一种机制,它允许同一个来源运行的Web应用程序从在另一个来源运行的服务器访问资源.同源策略是一种非常严格的措施,因为它只允许与服务器起源于同一源的应用程序访问其资源.很多时候 ...

  3. 来自 CORS 预检通道的 CORS 头 'Access-Control-Allow-Headers' 的令牌 'appkey' 无效)。

    1.服务端: web.config文件中: <system.webServer> <httpProtocol> <customHeaders> <add na ...

  4. Cross-origin resource sharing JSON with Padding 同源策略 JSONP 为什么form表单提交没有跨域问题,但ajax提交有跨域问题? XMLHttpRequest and the Fetch API follow the same-origin policy 预检请求(preflight request)

    https://zh.wikipedia.org/wiki/跨来源资源共享 跨来源资源共享(CORS)是一份浏览器技术的规范,提供了 Web 服务从不同域传来沙盒脚本的方法,以避开浏览器的同源策略[1 ...

  5. 在fetch方法中添加header后遇到的预检请求问题

    今天在使用fetch方法 fetch('xxx.com',{header:{bbbbbbb:111}}) 浏览器返回的请求信息中,header变成了 :authority:koss.nocorp.me ...

  6. 跨域请求中预检请求options之坑

    一.前言 因为跨域请求,浏览器可能(后面讲)会发送一次options请求,如果处理不好,跨域还是会gg的. 之前很少涉及跨域,涉及也是简单请求(下面阮老师文章中区别热简单请求和复杂请求),所以基本不会 ...

  7. Http跨域时候预检没通过的几种原因

    网上大多数涉及的原因(直接复制粘帖): CORS把HTTP请求分成两类,不同类别按不同的策略进行跨域资源共享协商. 1. 简单跨域请求. 当HTTP请求出现以下两种情况时,浏览器认为是简单跨域请求: ...

  8. DRF 中 解决跨域 与 预检

    DRF 中 解决跨域 与 预检 1 跨域 浏览器的同源策略: 对ajax请求进行阻拦 ps: 对href src属性 不限制 只有浏览器会阻止,requests模块不会存在跨域 (1)解决方案1 JS ...

  9. cors 预请求

    1.CORS的其他限制 默认允许的方法只有:GET.HEAD.POST默认允许的Content-Type:text/plain.multipart/form-data.applicaton/x-www ...

  10. 对CROS OPTIONS预检请求的一些思考

    前后端分离模大势所趋,跨域问题更是老生常谈. 问题背景: 浏览器最基本的安全规范-同源策略.所谓同源是指域名.协议.端口相同.不同源的浏览器脚本(javascript.ActionScript.can ...

随机推荐

  1. srcrpy手机投屏软件

    1,先在pc上下载和压缩投屏软件 2,在安卓手机上设置打开开发者模式 (usb是需要插线的,如果要无线连接就用adb)

  2. Java 查找Panel 里的某个组件 比如 按钮

    遇到到一个需求,需要获取界面里的一个按钮,但是这个按钮是封装的父类嵌入的,知道label 的值. 写了一个递归获取它 1 private JButton LookupTheButton(Compone ...

  3. 分布式事务seata

    1.事务的4大基本特征.   1)原子性   2)一致性   3)隔离性   4)持久性 2.什么是分布式事务? 本地事务:单服务进程,单数据库资源,同一个连接conn多个事务操作. 分布式事务:多服 ...

  4. python之路3:文件操作和函数基础

    文件操作 字符编码解码 函数基础 内置函数 一.文件操作 对文件操作流程 打开文件,得到文件句柄并赋值给一个变量 通过句柄对文件进行操作 关闭文件 打开文件的模式有: r,只读模式(默认). w,只写 ...

  5. 2022年JMUCTF WP

    2022年JMUCTF WP crypto 2,Are you ok Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. ...

  6. Fortran笔记之过程重载,多态,泛型编程

    参考自Introduction to Modern Fortran for the Earth System Sciences 过程重载 OOP中的另一个重要技术是过程重载(Procedure Ove ...

  7. select标签如何实现 每个option传递多个值

    设计项目时 我们有时候会利用 <select>  <option  value="值1" > </option>  </select> ...

  8. Ubuntu网络重置

    Ubuntu网络重置 >重置网络 sudo service network-manager stop sudo rm /var/lib/NetworkManager/NetworkManager ...

  9. Java 日期类 处理

    原始方案:SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-DD"); //线程不安全类,最好不要用了,替代方案: Date ...

  10. pytorch 独热编码报错的解决办法:one_hot is only applicable to index tensor

    首先,报错原因,我认为是数据类型错误, 在文档中表示,第一个tensor参数的数据类型为LongTensor,也就是torch.int64类型的,如果你有报这个错:"one_hot is o ...