credit:https://medium.com/@baphemot前端
译自:https://medium.com/@baphemot/understanding-cors-18ad6b478e2bnode
“呃。。还行, 但不够”
webpack
若是你常常跟AJAX call打交道,那么你确定遇到过下面这个错误。web
若是你看到这条消息,意味着响应失败了,但你仍是能在Console里的Network标签下,看到返回的数据。chrome
那么,这里究竟是怎么一回事呢?express
你所遇到的这种行为就是浏览器跨域的实现。npm
考虑到安全问题,在跨域标准化以前,若是你想调用一个节点在不一样域的API, 是不存在的。这种调用,会由于 Same-Origin 政策被阻止。后端
设计CORS这种机制是为了,第一,使你所发出的请求能表明你自身; 第二, 阻止那些流氓JS发出的请求; 第三,这种机制会被激活,不管什么时候你发送请求到:api
1). 不一样的域名。(eg. 应用在 example.com 调用 api.com)跨域
2). 不一样的子域名。(eg. 应用在 example.com 调用 api.com)
3). 不一样的端口。(eg. 应用在 example.com 调用 example.com:3001)
4). 不一样的协议。(eg. 应用在 example.com 调用 example.com)
经过这种机制,咱们可以阻止黑客的脚本攻击,以防当你登录,好比银行网站,的时候替换你的验证信息。
若是你的浏览器尝试发起一个‘不简单’的请求(好比: 一个请求包含了cookies, 或者 Content-type
不是application/x-ww-form-urlencoded
, multipart/form-data
或者 text-plain
)这时候会调用有一种叫作 预检(preflight)的机制,而后会发送一个options
请求到服务器。若是服务器的响应,没有携带特定的headers, 随后的‘简单‘get
或post
请求仍是会发送,可是浏览器不会容许JS去访问的收到的数据。
若是你明确想要添加cookies,自定义headers和其余一些features,那这个请求就再也不是一个‘简单’请求。那么服务器就不会合理地响应,请求也不会被发送。
CORS使用不多的HTTP请求头(在请求和响应里都是),可是有一点你必须明白,并且有能力去在工做中应用:
这个请求头通常会被服务器端返回,它的值表明了哪些域名你有权能够访问。 它的值能够为:
*
容许访问任何域若是你请求客户端传一些用做验证的请求头,好比cookies, 那么你就不能将Access-Control-Allow-Origin的值设定为*
—必须是安全验证过的域名才能够!
若是一个服务器支持经过cookies来验证,那么必需要在响应里带上这个请求头。
True是其惟一有有效的值。
一个逗号分隔的list,存放表明服务器愿意支持的请求头。(eg. 好比x-authentication-token
, 你须要将其包含在ACA header里, 返回给前面提到的options
请求, 不然你的请求会被blocked)
跟上面类似,这个请求头包含一系列用户可用的headers,这些headers会出如今真实响应里,并且客户端是可使用的。其余的全部header会被blocked。
这个比较简单,存放全部服务端支持的HTTP请求类型(好比get
,post
)。
客户端请求头的一部分,其值包含客户端app启动处的域名。 出于安全考虑,浏览器将不容许你去重写这个值。
你不得不认可CORS并非一种‘错误’。它是一种预期的机制为了去保护用户,你,还有你发送请求的目标网站。
有时候缺少合理的请求头是客户端的一种错误的行为(eg. 缺乏验证信息好比API key)。
这里我将给你一些方法去“解决错误”,选择哪一种方法,这取决于你所应用的场景:
A - 我开发前端,后端我认识,听个人 ;)
嗯这固然是最好的状况, 你就能够去实现合理的CORS响应在你所请求的服务器端。若是一个API正在使用node的express
框架,你只要用一下cors
的包就好了。若是你想使你的网站更加合理安全,你可能要考虑使用一个白名单给Access-Control-Allow-Origin
请求头。
B - 我开发前端,后端我不熟,暂时须要一个临时的解决方案 :)
这是第二好的状况,由于这就是A状况,只不过有一些时间限制。若是你想临时解决这个问题,你可让你的浏览器忽略CORS机制,举个栗子,使用ACAO Chrome插件,或者在用Chome的时候跑一下下面的flags:
chrome --disable-web-security --user-data-dir复制代码
重要:请记住这条命令会应用于全部网站,而且存在于整个浏览器会话中。请当心使用。
另外一个路子就是,你可使用devServer.proxy(假设你使用webpack去serve你的app)或者使用一个 CORS-as-a-service 解决方案,好比cors-anywhere.herokuapp.com/
C - 我开发前端,我想要调后端? 不存在的 :`(
好吧,如今事情就变得复杂了。首先,你应该可能须要搞清,为何服务器端没有发送一个正确的请求头。
可能它们不容许使用第三方的库的app去访问?可能它们的API只给服务器端的应用使用, 而不是浏览器?可能你在请求时没有发送用于验证的token?
若是你仍然认为你有可以经过浏览器获得数据,你应该去写一个本身的代理,存在于浏览器应用和API之间,就像咱们在方案B中所作的同样。
Adding a proxy in the middle
这个代理服务器,不是必须和你的应用跑在相同的域上。只要使得这个代理服务器,在与客户端交流时支持CORS就能够。在与API交流时不是必需要支持CORS。
你能够写一个本身的平台,或者使用一个已有的解决方案,好比
记住,这种方法可能存在安全风险,若是你想要支持验证的话。
若是你想学更多关于CORS的细节,我推荐你去查看更加细节化的MDN article.