与后端接口跨域数据通信算是一个比较常见的情景,并且现在也已经有了非常多的解决方案,在此我就来整理下自己对于跨域问题的解决方案。

与后端接口数据通信主要分为HTTP GET和POST两种,说到它们之间的区别,表面上的区别就不讲了,太基础了,什么长度限制等等自己查下就清楚了。这里主要说下它们的语义:

对于GET,是用于获取指定URL上的资源,是读的操作,无论对某个资源进行多少次,它的状态是不会改变的,在这个意义上我们可以讲GET是安全的,因为GET是安全的,所以返回的内容可以被浏览器、Cache服务器缓存起来;

对于POST,语义是对指定资源添加或修改数据,所以是不安全的。每次提交POST,参与的代码都会认为这个操作会修改操作资源对象的状态,于是浏览器在按下F5时会弹出确认框,同时浏览器和Cache服务器不会缓存POST的内容。
安全的是指没有明显的对用户有影响的副作用(包括修改该资源的状态)。HTTP方法里的GET和HEAD都是安全的。
还有个概念叫幂等,是指一个方法不论多少次操作,结果都是一样。PUT(把内容放到指定URL),DELETE(删除某个URL代表的资源),虽然都修改了资源内容,但多次操作,结果是相同的,因此和HEAD,GET一样都是幂等的。
所以根据HTTP协议,GET是安全的,也是幂等的,而POST既不是安全的,也不是幂等的。

扯得有点远了,继续回到跨域数据通信的问题上来。
按上面说的,接口大体上会有读与写两种类型的需求,读数据用jsonp能解决掉跨域、跨浏览器的问题。但写数据时,用GET方式似乎不太稳妥,当然,如果后端能够很好的解决这样的安全问题,其实写用jsonp来与客户端通信也是很好的,这样的话,就能省不少的事。记得支付宝给第三方的快捷支付的接口,就全部是GET的方式处理的,但经过一系列的各种加密什么的处理,还是非常安全的。

在这里还是不想对写数据接口用jsonp进行,当然我也不喜欢用flash来跨域通信的方式,也不考虑CORS这些需要后端一些改动的方法,我想回归最简单、最纯粹的方式。要POST数据,我就构建一个form表单来post吧。直接把form表单的action指向后端接口,这样做太幼稚了,整个页面会刷新跳转到到action指定的接口那去的,所以在这里会把form的target的值设定为一个iframe的name,这样表单提交的目标成了iframe,form提交的结果也只会在那个iframe中出现,页面就不会跳转了。

当然这个iframe显然要隐藏掉,这种小勾当还是不要让用户看到的好,好好的页面中出现一个傻傻的空白iframe,会被骂死的,所以一定要记得iframe要display: none。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var iframe = $('<iframe '
+ 'frameborder="0"'
+ 'height="0"'
+ 'width="0"'
+ 'name="' + id + '"'
+ 'id="' + id + '"'
+ '/>')
.appendTo('body')
, form = $('<form />')
.attr({
action: '/openmobile/login',
method: 'POST',
target: id
})
.appendTo('body'), hiddens = {};

$.each({
loginType: 'meetstudio',
appId: 1,
account: 'amy@meet-future.com',
password: '111111'
}, function(_name, _value) {
hiddens[_name] = $('<input />').attr({
name: _name,
value: _value,
type: 'hidden'
}).appendTo(form);
});

这只是一个片段,很显然的功能就是构建一个iframe与一个form,插入body中。当然,可怜的form中还被插了许多的type=hidden的input,这些都是一些必要的参数。

为何iframe是用那么丑陋的方式来写呢?据前辈们介绍,因为这里有一个坑。在低版本的ie中,利用javascript动态插入的iframe,属性很容易broken,这算是一个bug。解决方式就是你最好把整个iframe一次性的插入页面中,或者需要的时候(比如在提交表单时)检查一下iframe的name是否与你预想中的一样,不同的话,再指定一次name。

从iframe中取到接口返回的结果

这个网络上有许多解决方案啦。从iframe中读取接口返回的数据,步骤说明如下:

  1. post 数据到接口时,开启 location.hash 变化的监听器
  2. 后端接口返回时把数据写到父页面的 location.hash 中
  3. 当发现hash有变化时,暂停监听
    好了,数据到手。

最后千万合理的使用那个监听器,一直在那循环着,对浏览器和用户的机器可不是什么好事。如果不小心把用户的电脑搞死机了,那一定就悲伤成河了。