作为一个苦逼前端开发工程师,不得不面对各种cross,比如面对五花八门的浏览器我们必须cross browser,面对各种终端,我们必须cross device,在这么多年的前端开发经历中,在不同的域之间穿越中,遭受各种折磨,所以这次和大家分享的是cross domain。

这次分享的cross domain,是包括所有跨域调用,无论是跨域获取数据,还是跨域跨frame调用,所以会分为两部分,这次会先分享跨域获取数据,跨域获取数据大概有以下方式。

1.jsonp方式
这是最简单,也是最实用的跨域获取数据方式,原理是在浏览器端通过生成script标签,并通过js callback的形式实现跨域访问,比如一个jsonp接口是这样:
http://mail.163.com/someapp/jsonp?somequery=xxx&callback=fSomeMethod
然后服务器端会通过以下方式返回数据:
fSomeMethod({返回的json数据对象})
优点:实现简单,使用灵活,并且原生cross all browser
缺点:只能get方式获取数据,某些浏览器会显示加载中提示

2.表单post方式
这个方式会复杂一点点,原理是post表单到一个隐藏的iframe,然后iframe将数据post回来同域的一个url,这时候就可以直接调用同域的回调:
首先post表单到如:http://other.domain.com/someapp?somequery=xxx&callback=fSomeMethod&backurl=http://mail.163.com/proxy
这个接口返回的内容:
<form action="${backurl}" method="post">
<input type="hidden" name="data" value="返回的数据">
<input type="hidden" name="callback" value="${callback}">
</form>
<script>document.getElementsByTagName("FORM")[0].submit();</script>

然后post到http://mail.163.com/proxy后,返回:
<script>
parent.${param.callback}("${param.data}")
</script>
这样就完成整个跨域获取数据的过程
优点:支持post方式,并且原生cross all browser
缺点:实现有点复杂,并且流程有点曲折,需要两次请求,而且表单post方式会引起刷新提示的问题

3.服务器代理
服务器代理方式跨域调用也是使用的比较广泛的方式,原理就是在服务器端来获取跨域数据,然后在同域里ajax方式或者其他方式返回给浏览器。
优点:客户端实现简单,没有cross browser问题
缺点:需要在服务器端实现模拟请求获取数据

4.html5的XDomainRequest
终于在html5迎来了对跨域的ajax,泪牛满脸啊,这下完全可以通过浏览器的原生方式跨域ajax获取数据,这里有个对浏览器的各种跨域ajax的测试:
http://www.debugtheweb.com/test/teststreaming.aspx
优点:浏览器原生支持跨域方式ajax请求
缺点:只能在支持html5的高级浏览器中支持

5.flash request
这个是目前跨域请求最好的一个解决方案,建议在不能用jsonp的方式时候,都可以使用flash request方式,而且flash request可以配置一个安全策略,可以允许哪些域可以调用,然后被跨域的调用需要配置一个crossdomain.xml,允许可以被那个域的flash跨域调用,内容如下:
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="www.xxx.com" />
</cross-domain-policy>
这种方式的局限就是必须依赖flash,我们在邮箱曾经做过一个统计,99%的用户可以通过这种方式获取到数据,另外1%的用户可能是没有安装flash或者flash版本有问题。
优点:依赖flash下可以无视各个浏览器兼容问题
缺点:正所谓成也萧何败也萧何,缺点也是因为要依赖flash

以上每个方式都有优点和缺点,具体使用那种方式来跨域获取数据,需要具体使用时候来决定,没有那种方式是完美的。

二:

苦逼的前端开发工程师,刚才解决了一个cross domain的问题,还没有来得及沉浸在其中喜悦之际,又迎来了另外一个cross domain的问题:邮箱出现双滚动啦。

在页面中当需要加载外域app的iframe时候,最容易出现跨frame的cross domain问题,比如刚才那前端开发工程师遇到的双滚动的问题,在页面中放入一个外域的iframe,当iframe的高度大于iframe的页面的body高度时,就会出现滚动,这时候加上本身页面已经有一条滚动,那就出现经典的双滚动问题,如果iframe里面又嵌套另一个外域iframe,那可能会出现三滚动,继续嵌套..继续滚动..继续套..继续滚...,之所以出现这个问题,是因为外域的iframe不能直接调用:

parent.document.getElementById("iframe_id").style.height = document.body.offsetHeight + "px"

这里就需要跨iframe进行cross domain,有以下提到的两个方式。
以下的例子会以这几个页面作为例子:
页面a:http://www.a.com/a.htm
页面b:http://www.b.com/b.htm
a的内容:<iframe src="http://www.b.com/b.htm" id="ifrm_b"></iframe>

1.html5的postMessage方式
首先介绍一个浏览器原生的跨域调用方式,在支持html5的高级浏览器,支持这种方式:
oWin.postMessage(oMessage, sTargetDomain);
oWin为需要跨域调用的window对象
oMessage为传送的数据
sTargetDomain是跨域的frame的域
假如页面a跨域页面b,那么在页面a上调用以下进行跨域:

try{
document.getElementById("ifrm_b").contentWindow.postMessage(JSON.stringify({value:"this is cross call by PostMessage."}), "http://www.b.com");
}catch(e){
alert("请使用以下或者更高版本浏览器:\nIE8+, Firefox 3, Opera 9, Chrome 3和 Safari 4!");
}

然后在b,需要加一个window的message事件监听
fAddEvent(window, "message", function (o) {
var oMessage = o.data;// data属性就是postMessage的oMessage参数
alert(oMessage.value); // 这时候会显示:this is cross call by PostMessage.
});

这样就完成了跨frame的跨域通讯
优点:完美的跨frame跨域调用
缺点:只支持html5的浏览器
2.代理iframe方式
使用代理iframe的方式有两种,一种通过window.name方式跨域调用,一种是通过url参数的方式传递调用,不过两种方式的调用原理都是创建一个隐藏的iframe,iframe的url指向需要跨域的域名的一个代理页面,然后通过这个代理的iframe,和跨域的iframe通讯,因为这时代理的iframe和跨域的iframe完全同域,就可以畅通无阻进行。
在上面的例子加多一个代理页面c:
代理iframe c:http://www.b.com/c.htm
在页面a加入以下函数:

function fCrossByName(sDomain, oData) {
var oIframe = document.getElementById("ifrmCross");
if(oIframe){
oIframe.parentNode.removeChild(oIframe);
}
oIframe = document.createElement("IFRAME");
oIframe.style.display = "none";
oIframe.id = "ifrmCross";
oIframe.name = JSON.stringify(oData);
document.body.appendChild(oIframe);
oIframe.src = "http://" + sDomain + "/c.htm";
}

然后在页面a就可以进行跨域:
fCrossByName("www.b.com", {
value:'this is cross call by iframe',
func : "fCrossByNameCall", // 要跨域调用的函数
win : "ifrm_b"
});

然后在代理iframe加入以下代码:
var oData = !window.name?null:(new Function('return '+window.name))();
if(oData.win && oData.func){
var oResult = (oData.win == "top" ? top : parent[oData.win])[oData.func](oData);
}

这样就可以实现整个的跨域调用,通过url参数方式跨域调用,和这个类似,只是需要将数据放到代理iframe的url参数上,而不是name。
下面这个页面demo显示这三种方式的调用(因为没加JSON的转换js包,需要使用支持内置JSON对象的浏览器运行..):
http://mimg.163.com/demo/crossdomain_test.htm
优点:没有浏览器兼容问题
缺点:因为要部署代理iframe的文件,所以比较麻烦,整个流程也比较复杂,如果要实现和html5的postMessage兼容的接口,需要做大量封装,就会更加造成更加复杂

总结:在邮箱实际使用过程中,当只是简单的跨域调用比如前面提到的解决双滚动问题,一般可以简单的使用第二种方式,但是如果需要复杂的双向互相调用,就需要支持html5的浏览器调用postMessage,不支持的需要第二种方法实现兼容。

原文链接:http://ntesmailfetc.blog.163.com/blog/static/206287061201241011546581/

前端开发各种cross之cross domain的更多相关文章

  1. js/jquery/html前端开发常用到代码片段

    1.IE条件注释 条件注释简介 IE中的条件注释(Conditional comments)对IE的版本和IE非IE有优秀的区分能力,是WEB设计中常用的hack方法.条件注释只能用于IE5以上,IE ...

  2. 高逼格前端开发工具-FIDDLER

    1.Fiddler相对其他调试工具的优势 HttpWatch 和 Firebug绝大多数前端开发人员都比较熟悉,但是HttpWatch虽然可以抓到每个 HTTP 请求的全部数据,但无法修改返回的数据: ...

  3. 使用 mock.js 让前端开发与后端独立

    直接上代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...

  4. 前端开发利器—FIDDLER 转

    http://www.cnblogs.com/yuzhongwusan/archive/2012/07/20/2601306.html 前端开发利器—FIDDLER 1.Fiddler相对其他调试工具 ...

  5. 前端开发工具—fiddle

  6. 利用 Grunt (几乎)无痛地做前端开发 (一)之单元测试

    前言 如果你想开发一个js应用,甭管多简单,都要先创建整个宇宙 来看看我们的Javascript小宇宙: 确定如何根据需求.功能划分模块,如何将代码分成多个文件开发,合成一个发布 保证上一条的同时,使 ...

  7. 前端开发工程师 - 03.DOM编程艺术 - 第1章.基础篇(下)

    第1章.基础篇(下) Abstract: 数据通信.数据存储.动画.音频与视频.canvas.BOM.表单操作.列表操作 数据通信(HTTP协议) HTTP事务: 客户端向服务器端发送HTTP请求报文 ...

  8. 1+x 证书 Web 前端开发中级理论考试(试卷 8 )含答案

    1+x 证书 Web 前端开发中级理论考试(试卷 8 ) 官方QQ群 转载请注明来源:妙笔生花个人博客http://blog.zh66.club/index.php/archives/438/ 一.单 ...

  9. 认识web前端开发

    web前端开发 1.web即web系统,是以网站的形式呈现,通过浏览器的访问来实现一定的功能的系统. 2.什么是前端开发? 前端开发是创建web页面或app等前端界面呈现给用户的过程.通过html.c ...

随机推荐

  1. OpenCV入门:(三:图片Mask operations)

    Mask operations 翻译为中文应该是掩模操作,具体操作步骤就是根据一个操作矩阵(又名kernel)处理图片中的每一个像素点,操作矩阵会根据当前像素点的周围像素来调整当前像素值. 1.示例 ...

  2. 「题目代码」P1029~P1033(Java)

    1029 C基础-求解方程 import java.util.*; import java.io.*; import java.math.BigInteger; public class Main { ...

  3. pta数组作业

    7-2 设计思路:本题要求处理数据并输出最大值及其对应的最小下标,首先输入n,然后定义一个长度为n的数组用于存储数据,定义m=a[0],n=0,从a[1]开始与m进行比较,若某项大于m,就把该项的值赋 ...

  4. lintcode-95-验证二叉查找树

    95-验证二叉查找树 给定一个二叉树,判断它是否是合法的二叉查找树(BST) 一棵BST定义为: 节点的左子树中的值要严格小于该节点的值. 节点的右子树中的值要严格大于该节点的值. 左右子树也必须是二 ...

  5. CentOS7 最小化安装vmware-tools

    花了一上午的时间在1611上安装vmware-tool,总不能全部顺利安装成功 结合网上资料,整理出正确流程 下载最新的CentOS-7-x86_64-Minimal-1708 安装之后 联网 yum ...

  6. BZOJ4448 SCOI2015情报传递(离线+树链剖分+树状数组)

    即滋磁单点修改,询问路径上小于某数的值有多少个.暴力树剖套个主席树(或者直接树上主席树,似乎就1个log了?感觉不一定比两个log快)即可,然而不太优美. 开始觉得可以cdq,然而就变成log^3了. ...

  7. Eclipse打不开,闪退

    自己编写了个程序,运行巨慢..无语,输出太多,后来冒出一个错误,不知什么原因啊,再后来Eclipse就打不开了,到workbench闪退... 百度后解决方案: 进入目录:workspace/.met ...

  8. (转载)Java中如何遍历Map对象的4种方法

    在Java中如何遍历Map对象 How to Iterate Over a Map in Java 在java中遍历Map有不少的方法.我们看一下最常用的方法及其优缺点. 既然java中的所有map都 ...

  9. BZOJ4869 [Shoi2017]相逢是问候 【扩展欧拉定理 + 线段树】

    题目链接 BZOJ4869 题解 这题调得我怀疑人生,,结果就是因为某些地方\(sb\)地忘了取模 前置题目:BZOJ3884 扩展欧拉定理: \[c^a \equiv c^{a \mod \varp ...

  10. 洛谷 P2893 [USACO08FEB]修路Making the Grade 解题报告

    P2893 [USACO08FEB]修路Making the Grade 题目描述 A straight dirt road connects two fields on FJ's farm, but ...