做有态度的前端团队

网易FEG前端团队

浅谈跨域的几种方式

跨域,在平常工作中经常遇到,也经常被一些人问到跨域的处理方式。虽然处理跨域的几种方式都有用过,但是每次提到跨域的时候经常晕乎一下先,所以在这里总结一下平时用过的几种跨域方式

1、通过jsonp跨域

在js中,我们直接用XMLHttpRequest请求不同域上的数据是不可以的,而在html中,能够实现跨域的就几种:

  1. link属性(css)
  2. href属性(a标签)
  3. src(img标签、script标签)

为了解决跨域的问题,结合script能够发起跨域请求的原理,产生了jsonp,即:动态的创建script标签,跨域的地址加上需要传送的数据和回调函数为script标签的src地址,执行完之后再动态的删除之前创建的script.
理解了原理之后,就很容易明白jsonp的缺点了:

  1. 只能用get方式请求,因为是使用src来传送数据
  2. 数据中出现中文需要编码。因为通过url传参数,可以在浏览器输入地址并带上中文参数试试
  3. src对请求的地址没有限制,会出现安全性的问题

2、document.domain

看到这个跨域方式,可能有些同学会觉得陌生,其实这个也很常用。
每次遇到有需要使用登录之类的项目,在开发的时候都需要将地址改成163的域名,为啥?在控制台打印一下document.domain试试,这篇文章的控制台输出的是feg.netease.com,而在有使用到163登录的项目中,输出的都是163.com。登录组件限制在163域名下才能用,但是163的域名有很多,避免不同域名之间出现通信问题,使用document.domain的方式将页面的主域名改成了163.com。这种方式主要用在主域名能设置成相同的页面之间的通信。如http://xyq.163.com/,它的主域名可以是xyq.163.com或163.com;http://xsd.163.com,它的主域名可以是xsd.163.com或163.com,所以这两个站点下的页面能够将主域名设置成163.com。

3、window.name的方式

这种方式利用了window对象name属性的特征:在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。
使用window.name还需要使用iframe来实现,下面是页面和投票系统使用window.name方式实现的例子(投票地址:http://cgi.mmog.163.com:8088/v4a/show_vote/1250/):
页面的js:

    var _cdkey = $("#enter-cdkey .code-input").val(),
    _urs = $("#enter-cdkey .mail-input").val(),
    _phone = $("#enter-cdkey .phone-input").val();
    
    var _windowName = _urs + "/" + _cdkey + "/" + _phone;

    var _iframe = $("iframe");
    _iframe.attr('src','');
    _iframe.attr('style','display:none');
    _iframe.appendTo('body');
    _iframe[0].contentWindow.name = _windowName;
    _iframe.attr('src', 'http://cgi.mmog.163.com:8088/v4a/show_vote/1250/');

投票系统页面中处理window.name传过来的数据:

    var _urs = window.name.split('/')[0],
      _cdkey = window.name.split('/')[1],
      _phone = window.name.split('/')[2];
    $('#voterinfo_10').val(_urs);
    $('#voterinfo_70').val(_cdkey);
    $('#voterinfo_41').val(_phone);

上面只是个简单的示例,大家有兴趣可以去尝试一下。
使用window.name方式对数据有一个限制:像上面例子示例的那样,数据是string。

4、Access-Control-Allow-Origin方式

正常情况下我们的接口都是jsonp的格式,可是万一遇到其它格式的接口怎么办?比如json格式,这个时候可以通过Access-Control-Allow-Origin来实现跨域。这种方式主要是后台同学配置,接口使用Access-Control-Allow-Origin设置对应项目的域名,前端同学这边像平常调用接口一样,不需要再做其他处理。如下面的例子:

    $.ajax({
        url: "http://api.ypw.163.com/api/app",
        type: "POST",
        data: JSON.stringify(postData),
        dataType: "application/json",
        headers: {"X-Ypw-Token": user_token, "Content-Type": "application/json"},
        success: function(result) {
            renderPage(result);
        }
    });

data为需要传输的数据,dataType是接口的格式。比平常接口调用多了的headers部分,是这个接口考虑到安全性的问题,接口开发同学需要我添加一个key,并且把user_token用base64加密之后传输给他,并不是跨域需要设置的东西,即这种方式跟平常调用接口一样。
好像这种方式挺好用的。确实,可是最大的问题是ie7、8、9都还不支持,所以目前也就能用在移动端了。

5、--allow-file-access-from-files --disable-web-security方式

这种方式是谷歌浏览器里面设置的,让浏览器允许跨域,如图
6612d581gba6d57e34795&690.png

除了上面的设置方式之外,还可以通过cmd命令来启动,像平常用fis启动项目一样,先进入谷歌的安装路径,然后输入--allow-file-access-from-files命令来启动谷歌。之后在谷歌浏览器下则不需要考虑接口这些跨域的问题,像4中代码展示差不多,直接调用接口就可以了。
这种方式好像也很方便,但实际上平常开发的时候很少用到,因为除了限制在谷歌浏览器之外,即使是谷歌用户都不会去设置这个属性。我也是因为之前开发手游管理系统的时候用到了,因为最后系统上线的时候,整个系统都在一个服务器上,没有跨域的问题,但是开发过程中接口都放在各位开发同学的电脑上,开发过程中跨域了,所以使用了这种方式来处理。

手机阅读请扫描下方二维码:

已有 1 条评论

  1. [...]查看原文》》[...]

添加新评论

ali-40.gifali-41.gifali-42.gifali-43.gifali-44.gifali-45.gifali-46.gifali-47.gifali-48.gifali-49.gifali-50.gifali-51.gifali-52.gifali-53.gifali-54.gifali-55.gifali-56.gifali-57.gifali-58.gifali-59.gifali-60.gifali-61.gif