如何跨域
Contents
跨域是个大问题
最近工作中经常需要在前端调用接口,则时常会碰到跨域的问题,积累一些思考,总结一下。另外也深刻感觉到,如果一个东西,不自己实践一下,仅看书是很难理解透彻的。
首先跨域就要区分什么是域。这个很容易理解,不再赘述,无非就是协议、域名、端口等都要一致。
而跨域就是在不同的域之间数据的通信。而且需要注意的是,跨域只发生在前端,后端是没有跨域这一说的。
比如a.com 中,想要获取b.com的内容,这个时候通常就是a.com中通过js发起一个http请求(也就是Ajax),去请求b.com的数据。 这样:
|
|
但是打开页面,这一段js代码执行之后,就会发现报了这样一个错:
|
|
大致是说,http://xxx.com/api 这里面的响应头(response header)里没有指定 Access-Control-Allow-Origin,因此我们的请求头 origin:null 不允许访问这个地址。
这是什么问题?这还要从浏览器的同源策略说起,浏览器为了安全起见,不允许去请求与当前页面不在同一个域的资源!所以当你请求其他域的资源,浏览器会阻断你,除非在响应头里指明了Access-Control-Allow-Origin,并且在这个Access-Control-Allow-Origin中有你当前所在的域,才会让你访问!
这就是为什么前面说的,跨域只发生在前端,后端没有跨域这一说法,正是浏览器不让我们访问! 但是有时候,正常的跨域请求又是很必须的,怎么办?
就按照上面说说,在Access-Control-Allow-Origin指明你的域,如Access-Control-Allow-Origin:a.com
或者Access-Control-Allow-Origin : *
。这也就是通常所说的,CROS跨域资源共享。使用这种方法,需要对方服务器允许我们跨域,也就是说,要么对方服务是开放的,要么就得提前与对方协商。麻烦是麻烦,但是增加安全性。
举个例子: a.com/index.html中需要去请求b.com/api.php的页面,想要跨域成功,就需要api.php中,设定响应头:
|
|
这样就能跨域成功,请求不会被浏览器阻断。
这是一种方式,但是还有另外更常用的【奇技淫巧】供我们使用。也就是jsonp。jsonp的原理:
虽然一般情况下不能请求其他域的数据,但是却有两个例外:script
标签,和img
标签,这两个标签可以随意地引用其他页面的数据也不会被阻断。例如:
|
|
那我们就可将需要请求的数据,交给这两个标签来请求就好了!
<script src="http://c.com/api.php"></script>
这样不就相当于给其他域发了一个请求吗?对的就是这样。 但是有两个问题:
只能发送get请求,对于需要通过post传递的数据,就无能为力了。
另外,如果此时打开一看,就发现还是报错了
Uncaught SyntaxError: Unexpected token :
//也可能是报其他的错,一般都是Unexpected token xxx
但是如果通过抓包能看到,请求正常发出,并且服务器也返回了数据。但是!我们这里是通过script标签引入的,也就是说引入的是一堆数据,而我们知道script需要引入的是js文件啊,当然出错了!
那如何解决呢?这时很多网上的断章取义文章都会给你说,指定一个回调函数,处理获取到的数据就行了,例如:
|
|
如果真的是这样的话,那就好了。但是想一下,如果http://c.com/api.php?callback=test
返回的是 ‘helloworld’,也就相当于:
|
|
那你能调用个鬼啊!
也就是说,这里我们期望的数据是这样的:
|
|
只有如此才能调用我们的test()函数。jsonp的重点就在这里,服务器返回的是一串以我们callback参数为函数名包裹着的参数,这时才能调用我们的回调函数。这才是重点,网上很多一知半解的人写的文章根本都没讲到这一点。
也就是说,如果服务器不特别为你返回这种形式的响应,那所谓的jsonp中的回调函数还是没法调用的。
如何返回的,还是举一个服务器端的例子:
|
|
首先先获取到 传过来的 callback 参数,当然不一定要叫callback 。然后再把json化后的result数组放入$callback($result)
。于是乎运行下来:响应的值就是:
test({"msg":"hello"})
也就相当于:<script src="test(%7B" msg></script>
。直到这里,才能调用我们我们预定义好的回调函数test。
也就是说,如果服务器不为你特别返回这种形式的响应,那所谓jsonp还是不能完成跨域的。
上面说到,img标签也能跨域请求,但是script标签相比,它不能操作获取到的响应数据,所以较jsonp相比,更加有限。
总结一下:
所谓的跨域方法,并非就能突破浏览器的同源策略限制,如果对方服务器不支持,还是没法跨域。 跨域发生在前端,因为有浏览器的同源策略限制,才会有跨域这种说法。后端请求数据的话,则无限制,也就说说,如果前端无法跨域获取数据,可以考虑在后端获取数据返回给前端使用。
Author JeelyWu
LastMod 2018-09-29