在前后端交互的过程当中,跨域是一个老生常谈的问题了,说说自己的一些理解。
什么时候会发生跨域现象?
跨域的产生,是由于同源策略的限制,xhr或者fetch这样的API都遵循这个策略,所以当我们在某个域的页面,比如 http://kasol.com,他通过xhr发起一个请求,指向http://aenvgiell.com,那么这个时候就会产生跨域现象,得不到想要的内容。需要注意的是,其实对于服务端来说,此时的通信是正常的,服务端的逻辑照样执行,也会正常发送响应,但是在浏览器端却被阻断了
怎么来解决跨域?
- 最通用的方案要属jsonp了,实际上这不是一个官方的解决方法,但是却应用十分广泛。刚刚说,使用xhr的方式跨域访问资源会被拦截,但是其实在前端要发起请求是有很多方法的,比如style标签,script标签,img标签,他们都可以发起一个请求,那么我们用一个script标签,把src指向一个跨域的路由
<script src="http://aenvgiell.com/list/get?callback=printList"></script>
此时这个请求就可以正常返回内容了,由于一般情况下,我们会在script标签里面指向一个静态JS文件,所以这种用法不太常用,但是这个就是jsonp的原理,假如后端的这个路由是这样处理的
// 伪代码
var list = getListFromDB();
var callback = ctx.query.callback;
response.write( callback(list));
大概的意思,就是获取到此次目标的资源,也就是list,然后把前端带过来的参数,当做一个函数名,然后返回一个函数的调用给前端。所以这段代码到了前端之后,应该是这样的
<script>
printList(list)
</script>
所以我们需要在这个脚本执行前,先定义一个名称为printList的函数
function printList(list){
console.log(list);
}
这样一来,跨域的资源就可以被我们使用了,很多库比如Jquery已经统一封装了这类请求。注意,这种请求回来的内容浏览器不会拦截掉,所以可以通过这种方式顺利的拿到非同源的资源。但是实际上, 从上面可以看到,这种方式需要前后端同学一起配合,但是比如前后端都是一个人来开发的话,就会省去一些沟通成本。并且这种方式只支持GET请求,原因也很明显。
我们刚才说的这个方法,是基于script标签发出的跨域请求不会被拦截而实现的,但是有时候我们也有方式让浏览器可以拦截这种请求内容,详细可以了解一下CORB,关于X-Content-Type-Options这个响应头的信息。
-
还有一种比较常用的方式,即CORS。实际上,资源是放在跨域的服务端上,假如我要申请访问那个资源,只要那个跨域的服务端同意即可,所以服务端需要在响应头上设置好响应头: access-control-allow-origin
可以给他设置为通配符*,也可以多个域名,需要注意的是,假如需要验证用户信息比如cookie时,即前端指定了withCredentials为true,那么此时不能使用通配符,要指定对应域名。这种方式也要服务端的配合。可以看看MDN上关于CORS的详细说明 。
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS -
通过服务端代理的方式来获取跨域的资源,上面提到过,跨域其实是浏览器拦截的请求内容,但是在服务端通过http或者rpc的方式是完全可以访问到的,所以可以这么做
xhr.open("http://kasol.com/list/proxy","GET");
xhr.send();
然后服务端处理
var ret = httpRequest.get("http://aenvgiell.com/list/get");
ctx.body = ret;
这样一来,也可以拿到这个资源。只不过服务端要改动的多一些
其他
其实最主流的就是上面提到的这些方式,不过还有一些比较少见的方法,这里提一下,不详细说明,有兴趣的话可以自行搜索。
更改浏览器的打开方式,上面提到, 跨域是浏览器的限制,但是我们实际上在运行浏览器时,可以用其他方式打开,平常我们是用点击图标这种方法打开,实际上可以通过cmd的方式,附带一些设置信息来打开浏览器,这些设置的意图就是告诉浏览器,不要去拦截跨域的请求内容,平常我们在开发调试的时候这招有些用武之地,但是对于一般用户来说,不太适用。
通过Adobe Flash,运行在浏览器的脚本除了JavaScript之外,还有一个叫ActionScript,这东西依赖flash player,运行在fvm这个容器里面,类似于jvm,他也有类似于xhr一样的API,被称为HTTPService,他可以通过配置文件的方式,来成功访问到跨域的资源,并且它提供了对应的类来和JavaScript通信,所以这种方式也能解决跨域问题,但是看得出来,非常繁琐,而且Adobe FLex也渐渐的开始退出历史舞台。