从何说起
Access to XMLHttpRequest at 'http://127.0.0.1:3000/' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
如果你在控制台中看到类似以上的输出信息,这就是跨域访问的拦截提示;那到底什么叫跨域呢?简单一点说,你网页是xxx.com/xxx.html
,这个网页中的元素不允许访问除这个域名以外的其他域名下的资源(也许不严谨,化繁为简能快速理解其本质)。再具体一点就是这个页面下的Ajax请求,不能去调类似http://abc.com/xxxx
这样的接口,必须是xxx.com
下的接口。
出浅入深
- 什么是域?简单一点,就是域名,
http://www.abc.com
下的网页只能调http://www.abc.com/
开头的接口,否则就是跨域了,跨域就会报错,上面那个错误提示; - 为什么不允许跨域?为了安全!废话,到底哪里有风险?简单描述一下, 我们的浏览器可以同时打开多个网站,也就是说浏览器是一个公共场所。不同的站点请求下来的数据可能会被互相“看到”,就像公共澡堂,大家坦诚相见,难免会有非份之想。所以要“隔开”,每个站点下的Ajax只能请求自己站点下的数据,不能请求其他站点的数据,即不允许跨域访问。
- 到了这里或许你已经有了一点点概念了,原来跨域访问的限制就A站点下的脚本只能从A拿数据,B站点下的脚本只能从B拿数据。如果我A站点确实想拿B站点的数据怎么办,有没有办法呢?肯定是有的,你在B站点上做相关设置,允许别人拿咯。
- 误区来了,由于在服务器上做相关匹配就可以允许跨域,
所以很多同学认为只所以不能跨域访问,是服务器做了相关限制?!这是不对的。
请注意:跨域访问限制是浏览器的行为。 阻止你跨域进行Ajax请求的是浏览器,不是服务端,并且事实上服务端已经返回了数据到本地,被浏览器拦截下来了,没有呈现到页面上来还抛出了异常。 - 还有一点疑惑,如果跨域限制是浏览器行为,为什么是在服务器上做相关配置?我们可以理解为
“服务器授权”
,默认情况下浏览器是不允许跨域的,这样可以为各站点数据增加一点安全保护,但如果你的站点在响应请求的时候,带回信息告诉浏览器:“没事,你尽管让他们都来请求好了,我有什么给什么。”,这样一来跨域的限制就解除了。 - 再多说两句,为什么App调接口没有跨域的问题?因为跨域的限制是浏览器行为,服务器本身并没有限制访问的源是谁。
简单总结
跨域访问限制是浏览器为了数据安全而设计的一种保护机制,跨域指的是不两只域名(简单一点就这么认为吧,不要纠结)下的站点数据不能互相访问,换句话说页面中的Ajax请求只能当前站点下的接口,否则就会被抛出异常。
值得注意的是,这种禁止跨域访问的行为是浏览器的“规矩”,服务端本身没并有这种限制。但可以在服务端做相关配置来允许跨域访问,这是服务端在“授权”,让浏览器解除跨域访问本站的限制。
扩展与思考
前面一直提到的是Ajax请求,是不是跨域针对的就是Ajax?我的理解是从该页面发出的所有网络请求都在其限制范围内。然而浏览器又并没有对所有的网络请求都做了跨域的限制。
什么意思呢,比如说img标签中的src属性,当前页面引入一张其他站点的图片资源,这其实是跨域的,可这个是却是合法的,类似的现象还有css资源外链与javascript资源。换句话说,我可以通过script
标签的src属性从别的域中拿到数据,浏览器没拦截,然后再想办法将数据取出来不就可以了吗?想得挺美,事实上还真可以。jsonp
就是依据这个原理实现的,相关细节不在这里讨论,点到为止。