你所不知道的跨域资源共享 (CORS)

本文收录在www.devsai.com个人博客

写在前面


有没有一看到讲跨域资源共享的就不想再看的了,网上的跨域资源共享的博文,三天两头的就出一篇。

既然你已经进来看了,还请你稍稍忍耐下,继续往下看,或许你会发现和之前看到的有不一样的收获。

其实,之前看过我写的文章的同学可能知道,我写过一篇关于《跨域及跨域资源共享》(没有看过的同学,可以从这进去)。比较全面的介绍了跨域的多种解决方案,以及说明了跨域资源共享.

你们会不会想:那既然已经写过了,为什么又写一篇? 是不是博主已经没啥东西可写了。

别急,接下来,让我跟你们慢慢道来。

你们所知道的

看过之前写的《跨域及跨域资源共享》或看过多篇CORS文章的同学可以选择性的跳过这一小段了。

就像你们看到过的相关的文章,讲跨域资源共享,一般讲其原理时,必定要讲到跨域资源共享的请求有两种(也有很多没有讲到):

简单请求 (Simple Request)

预检测请求 (Preflight Request)

然后就会进一步的讲到,什么时候发只发简单请求,又什么时候会在发真实的请求前,先发预检测请求,普遍的都是这么说的(包括我之前写的也是)

以下几种情况时都满足时是简单请求

request header 是简单的请求头

Accept

Accept-Language

Content-Language

Last-Event-ID

Content-Type

等等非自定义的请求头

request method 是下面的请求类型

HEAD

GET

POST

Content-Type 只限三个值

application/x-www-form-urlencoded、multipart/form-data、text/plain

如果不满足以上条件的都会先发送预检测请求,即为OPTIONS请求类型的请求

几乎都是这么说的,差别只是描述方式不同,比例下面的别人写的:

什么? 不信, 那你随便搜索几篇相关的文章看看。

那么,这有什么问题

先来做个例子吧。

假设要实现带进度条的上传功能,接口不是同域上的,服务端已经给配置了支持跨域资源共享的响应头,那我们直接用XmlHttpRequest就可以了

javaScript代码大概如下:

varxhr =newXmlHttpRequest();xhr.onreadystatechange =function(){// do something}xhr.upload.onprogress =function(){// do something}xhr.open('POST','http://127.0.0.1/upload');varfd =newFormData();fd.append('file',file);xhr.send(fd);

然后上传文件并查看下请求

What? 为什么会有两个请求啊。 是不是它不满足简单请求的要求(已不记得简单请求的同学往上再看看)

那么,我们来看看该真实请求的请求头

简单请求要求:

要求点实际内容是否满足

请求方式POST满足

请求头都是非自定义的请求头满足

Content-Typemultipart/form-data满足

不是都满足了吗?那为什么,会有两个请求,为什么在发送真实请求前还发了OPTIONS方式的请求。

为什么!!!整个人感觉都不好了!!!

变个魔术

把上面的javaScript代码改动下:

varxhr =newXmlHttpRequest();xhr.onreadystatechange =function(){// do something}xhr.open('POST','http://127.0.0.1/upload');varfd =newFormData();fd.append('file',file);xhr.send(fd);

去掉了xhr.upload.onprogress,上传后再来看下请求及请求头:

只有一个请求,请求头内容还都一样。 (这到底是怎么回事...有种再也不相信爱情的赶觉了!!!)

看到这里的同学,有木有觉得博主在坑你们,放了两张相同的请求头截图就想糊弄。

俗话说得好,不试不知道,一试吓一跳,要不,你们也亲自试试,一试便知真假。

再次双手奉上demo(喜欢的顺便点个赞哦~)。

分析问题

从上面的两小段JS看出,只是去除了上传的进度信息事件。也就是说加了进度事件就多发了个预检测请求。

那么,还有没有其他的事件了?添加其他的事件会不会也会发送预检测请求呢?

事件有onerror,onloadstart等等。经过博主的测试,上述答案是肯定的,添加其他事件后,确实也会发生预检测请求。

追求真理的同学们,在博主的demo里改改试试吧。

现在已经知道了问题的所在,由上传相关事件导致了跨域请求多发了预检测请求,说好的简单请求(Simple Request)呢~

博主抱着对问题刨根问底的精神,再次查看cors相关文档,找到了如下的内容:

If the following conditions are true, follow the simple cross-origin request algorithm:

The request method is a simple method and the force preflight flag is unset.

Each of the author request headers is a simple header or author request headers is empty.

通过这段,我们知道,原来除了我们所知道的简单请求的几大特征外,还提到了force preflight flag,这是什么鬼?

难道是因为设置了它? 那么什么时候设置了force preflight flag?

上面我们知道了因为上传事件导致了发送预检测请求,会不会是上传监听事件的时候给设置了force preflight flag,

然后在XmlHttpRequest level 2中的找到了相关的内容,以证实我的猜测是正确的。

有下面几段内容:

force preflight flag

The upload events flag.

从这段可以知道,force preflight flag与upload events flag是对应的,看到这里就知道了,只要upload events flag被设置true

那么就等于force preflight flag被设置了true,这时,不管请求的类型的是不是simple method,也不管请求头是不是simple header,都会先发送预检测请求。

接下来,我们再来看看upload events flag会在什么情况下被设置呢?

If the asynchronous flag is true and one or more event listeners are registered on the XMLHttpRequestUpload object set the upload events flag to true. Otherwise, set the upload events flag to false.

原来,当asynchronous flag为true并且XMLHttpRequestUpload(即示例中的xhr.upload)的一个或多个事件被监听的时候,upload listener flag就会被设置了。

这也正如之前测试的,当加了xhr.upload.onprogress后,出现了预检测请求。

到这里总算水落石出了。

这里还需要说明的一点是,the asynchronous flag就是xhr.open()的第三个参数,当未设置第三参数时,默认为异步,也就是the asynchronous flag为true

如果第三个参数设置为false,那么即使有上传的监听事件也不会发送预检测请求(Preflight Reuqest)

总结

以后还会不会理直气壮的在别人面前说,只要是满足几大条件(是非自定义的请求头,是GETorPOSTorHEAD,或Content-Type是那三种值的)就是简单请求 ,就不会发生预检测请求。

通过本文可知,并非满足这几大条件就一定是简单请求的,

应该要加个前置条件,是否是在上传请求中跨域,是否是异步的,是否监听了上传事件。

看到这,可能你想说,写这么多有啥用,对实际开发有帮助吗?或许没什么实际的帮助吧,又或许你也不会碰到吧。

但,最起码当你碰到的时候,你看到了两个请求,再看了下代码,你已经心里就有数了,知道这是怎么一回事了。

一直认为,做技术的对碰到的问题要知其然,更要知其所以然。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容