本文主要探讨用于构建实时跨源通信的两个模块:跨文档消息通信(Cross Document Messaging)和XMLHttpRequestLevel2。通过这两个模块,我们可以构建不同域间进行安全通信的Web应用。

一、跨文档消息通信

出于安全方面的看的考虑,运行在同一浏览器中的框架、标签页、窗口间的通信一直多受到严格的限制。但是现实中还存在一些合理的让不同站点的内容能在浏览器内进行交互的需求,其中Mashup就是一个典型的例子,它是各种不同应用的结合体。为了满足上述需求,引入了一种新的功能:跨文档消息通信。其可以确保iframe、标签页、窗口间安全地进行跨源通信。

  发送消息使用postMessage API,其示例代码如下:

chatFrame.contentWindow.postMessage(content,url);

  接受消息时,需要在页面中添加一个事件处理函数,当消息到达时,通过检查消息的来源来决定如何对这条消息如何处理,示例代码如下:

window.addEventListener("message",messageHandler,true);
function messageHandler(e){
switch(e.origin){//表示数据发送源
case "friend.example.com":
//处理消息
processMessage(e.data);//发送方实际传送的消息
break;
default:
//其他消息来源
//消息被忽略。
}
}

  postMessage API提供了一种交互方式,使得不同源的iframe之间可以进行消息通信。

  HTML5 通过引入源的感念对域安全进行了阐明和改进。源是网络上用来建立信任关系的地址的子集。源由规则(scheme)、主机(host)、端口(port)组成,例如由于scheme(https、http)不同,则源不同。

跨源通信通过 源来确定发送者,这就使得接收方可以忽略或者拒绝来自不可信源的消息。同时需要通过添加监听事件来接受消息,以避免被不可信应用程序的信息所干扰。但是在使用外来消息时,即便是可靠的数据源,也同样要谨慎,以防止内容注入。

在使用postMessage API时,需要遵循以下步骤:

1、检查浏览器是否支持

if(typeof window.postMessage === undefined){
//浏览器不支持postMessage
}

2、发送消息

window.postMessage("Hello","xx.example.com");

第一个参数包含要发送的数据,第二个参数时消息传递的目的地。

如果要发送消息给iframe,则使用如下代码:

document.getElementById("iframe")[0].contentWindow.postMessage("Hello","xx.example.com");

3、监听消息事件

window.addEventListener("message",messageHandler,true);
var originWhiteList = ["a.example.com","b.example.com","c.example.com"];
function messageHandler(e){
if(checkWhiteList(e.origin)){
processMessage(e.data);//发送方实际传送的消息
}else{
//忽略发送的消息
}
} function checkWhiteList(origin){
for(var i = 0; i<originWhiteList.length; i++){
if(origin === originWhiteList[i]){
return true;
}
}
return false;
}

二、XMLHttpRequestLevel2

  XMLHttpRequestLevel2是XMLHttpRequest的改进版本,主要涉及:跨源XMLHttpRequess和进度事件(Progress events)。

  XMLHttpRequest仅限于同源通信,XMLHttpRequestLevel2通过跨资源共享实现(Cross Origin Resource Sharing)跨源XMLHttpRequests。

  在XMLHttpRequest中通过readystatechange事件来响应进度,但是其在某些浏览器中不被兼容。XMLHttpRequestLevel2用了一个有意义的名字Progress进度来命名进度事件。其进度事件的名称主要有loadstart、progress、abort、error、load、loadend。通过对程序属性设置回调函数,可以实现对这些事件的监听。

  在使用XMLHttpRequestLevel2时,需要遵循以下步骤:

1、检查浏览器是否支持

var xhr = new XMLHttpRequest();
if(typeof xhr.withXredentials === undefined){
    //浏览器不支持XMLHttpRequest
}

2、构建跨源请求

var crossOriginRequest = new XMLHttpRequest();
crossOriginRequest.open("GET","http://www.example.com",true);

  

  在请求过程中,务必确保能够监听到错误,以找出出错原因,解决问题。

3、使用进度事件

crossOriginRequest.onprogress = function(e){
var total = e.total;
var loaded = e.loaded;
if(e.lengthComputable){
//处理其他事情
}
} crossOriginRequest.upload..onprogress = function(e){
var total = e.total;
var loaded = e.loaded;
if(e.lengthComputable){
//处理其他事情
}
}

三、postMessage API示例应用

以跨源聊天应用为例,来演示门户页面和聊天部件之间的交互。

1、创建postMessagePortal.html页面

<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>跨源通信-WebChat</title>
<link rel="stylesheet" href="styles.css">
</head> <h1>跨源通信门户</h1>
<p><b>源</b>: http://portal.example.com:8080</p>
状态 <input type="text" id="statusText" value="Online">
<button id="sendButton">更改状态</button>
<p>
使用postMessage发送一个状态,以更新包含在此页面中的widgetiframe。
</p>
<iframe id="widget" src="http://chat.example.net:8080/communication/postMessageWidget.html"></iframe>
<script>
var targetOrigin = "http://chat.example.net:8080";
var notificationTimer = null;
function messageHandler(e){
if(e.origin == targetOrigin){
notify(e.data);
}else{
//忽略
}
} function sendString(s){
document.getElementById("widget").contentWindow.postMessage(s,targetOrigin);
}
function notify(message){
alert(message);
}
function sendStatus(){
var statusText = document.getElementById("statusText").value;
sendString(statusText);
}
function loadDemo(){
document.getElementById("sendButton").addEventListener("click",sendStatus,true);
sendStatus();
}
window.addEventListener("load",loadDemo,true);
window.addEventListener("message",messageHandler,true);
</script>

  

2、创建postMessageWidget.html

<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>widget</title>
<link rel="stylesheet" href="styles.css">
</head>
<h1>Widget iframe</h1>
<p><b>源</b>: http://chat.example.net:8080</p>
<p>通过门户设置状态为: <strong id="status"></strong> <p> <div>
<input type="text" id="messageText" value="Widget notification.">
<button id="actionButton">发送通知</button>
</div> <script>
var targetOrigin = "http://portal.example.com:8080";
window.addEventListener("load",loadDemo,true);
window.addEventListener("message",messageHandler,true); function loadDemo(){
document.getElementById("actionButton").addEventListener("click",
function() {
var messageText = document.getElementById("messageText").value;
sendString(messageText);
}, true);
} function messageHandler(e) {
if (e.origin === "http://portal.example.com:8080") {
document.getElementById("status").textContent = e.data;
} else {
// ignore messages from other origins
}
} function sendString(s) {
window.top.postMessage(s, targetOrigin);
}
</script>

  注意:第一、上述的两个页面需要部署到web服务器;第二、两个页面必须来自不同的域。如果要在本机部署,则需要更改hosts文件,增加:

127.0.0.1 portal.example.com
127.0.0.1 chat.example.net

  修改完成后,需要关闭浏览器,再次重新打开。

四、XMLHttpRequestLevel2示例应用

  1、创建crossOriginUpload.html页面:

<!DOCTYPE html>
<head>
header(“Access-Control-Allow-Origin: *”);
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>上传地理数据</title>
<link rel="stylesheet" href="styles.css">
</head>
<script> function loadDemo() {
var dataElement = document.getElementById("geodata");
dataElement.textContent = JSON.stringify(geoData).replace(",", ", ", "g"); var xhr = new XMLHttpRequest()
if (typeof xhr.withCredentials === undefined) {
document.getElementById("support").innerHTML = "浏览器不支持跨源XMLHttpRequest";
} else {
document.getElementById("support").innerHTML = "浏览器支持跨源XMLHttpRequest";
} var targetLocation = "http://geodata.example.net:8080/upload"; function setProgress(s) {
document.getElementById("progress").innerHTML = s;
} document.getElementById("sendButton").addEventListener("click",
function() {
xhr.upload.onprogress = function(e) {
var ratio = e.loaded / e.total;
setProgress("已上传" + ratio + "%");
}
xhr.onprogress = function(e) {
var ratio = e.loaded / e.total;
setProgress("已下载" + ratio + "%");
} xhr.onload = function(e) {
setProgress("完成");
} xhr.onerror = function(e) {
setProgress("错误");
} xhr.open("POST", targetLocation, true);
geoDataString = dataElement.textContent;
xhr.send(geoDataString);
}, true); }
window.addEventListener("load", loadDemo, true); </script> <h1>XMLHttpRequest Level 2</h1>
<p id="support"></p> <h4>上传地理数据:</h4>
<textarea id="geodata">
</textarea>
</div> <button id="sendButton">上传</button> <script>
geoData = [[39.080018000000003, 39.112557000000002, 39.135261, 39.150458, 39.170653000000001, 39.190128000000001, 39.204510999999997, 39.226759000000001, 39.238483000000002, 39.228154000000004, 39.249400000000001, 39.249533, 39.225276999999998, 39.191253000000003, 39.167993000000003, 39.145685999999998, 39.121620999999998, 39.095761000000003, 39.080593, 39.053131999999998, 39.02619, 39.002929000000002, 38.982886000000001, 38.954034999999998, 38.944926000000002, 38.919960000000003, 38.925261999999996, 38.934922999999998, 38.949373000000001, 38.950133999999998, 38.952649000000001, 38.969692000000002, 38.988512999999998, 39.010652, 39.033088999999997, 39.053493000000003, 39.072752999999999], [-120.15724399999999, -120.15818299999999, -120.15600400000001, -120.14564599999999, -120.141285, -120.10889900000001, -120.09528500000002, -120.077596, -120.045428, -120.0119, -119.98897100000002, -119.95124099999998, -119.93270099999998, -119.927131, -119.92685999999999, -119.92636200000001, -119.92844600000001, -119.911036, -119.942834, -119.94413000000002, -119.94555200000001, -119.95411000000001, -119.941327, -119.94605900000001, -119.97527599999999, -119.99445, -120.028998, -120.066335, -120.07867300000001, -120.089985, -120.112227, -120.09790700000001, -120.10881000000001, -120.116692, -120.117847, -120.11727899999998, -120.14398199999999]];
</script> <p>
<b>状态: </b> <span id="progress">准备</span>
</p>

  注意:部署web应用,运行crossOriginUpload.html页面时,可能会才出现如下的提示错误:

  这是因为访问一页面的域与所请求的域非同源造成的。且浏览器是根据响应头的规则来确定这个域是否同源可以接收。

  因此我们需要http://geodata.example.net:8080/upload在返回内容时,设置Header Access-Control-Allow-Origin,即:

  Response.AddHeader("Access-Control-Allow-Origin","*") ;

  浏览器在接收到服务器返回信息时,会检查响应头的Access-Control-Allow-Origin,它的值标识请求内容所允许的域。如果将服务器设置Access-Control-Allow-Origin为*,表明该返回信息允许所有源访问。如果设置为具体的域,如http://xx.com,就表明除了同源外,只允许域来自xx.com的访问。

  本文链接:http://www.cnblogs.com/oooweb/p/html5-communication.html

  via oschina

《HTML5编程之旅》系列二:Communication 技术初探的更多相关文章

  1. HTML5编程之旅系列一:HTML5 Geolocation 初探

    让我们假设这样一个场景,有一个web应用程序,它可以向用户提供附近不远处某商场的打折优惠信息.使用HTML5 Geolocation API(地理定位 API),可以请求用户共享他们的位置信息. HT ...

  2. VSTO之旅系列(二):创建Excel解决方案

    原文:VSTO之旅系列(二):创建Excel解决方案 本专题概要 引言 创建VSTO项目 Excel对象模型 创建Excel外接程序 创建Excel文档级自定义项 小结 一.引言 也许很多朋友都没有听 ...

  3. 《HTML5编程之旅》系列三:WebSockets 技术解析

    本文主要研究HTML5 WebSockets的使用方法,它是HTML5中最强大的通信功能,定义了一个全双工的通信信道,只需Web上的一个Socket即可进行通信,能减少不必要的网络流量并降低网络延迟. ...

  4. 【Swift学习】Swift编程之旅(二)

    在本节将介绍一些最基础的知识 swift提供自己版本的类型,下面说明几种简单的类型 Int 整型 Double和float 浮点型 String 字符串型 Bool 布尔型 它也提供了3种主要的强大的 ...

  5. Web 开发精华文章集锦(jQuery、HTML5、CSS3)【系列二十七】

    <Web 前端开发精华文章推荐>2014年第6期(总第27期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML5 ...

  6. Web 前端开发精华文章推荐(HTML5、CSS3、jQuery)【系列二十三】

    <Web 前端开发精华文章推荐>2014年第2期(总第23期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML5 ...

  7. Web 前端开发精华文章推荐(HTML5、CSS3、jQuery)【系列二十二】

    <Web 前端开发精华文章推荐>2014年第一期(总第二十二期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML ...

  8. Web 前端开发精华文章集锦(jQuery、HTML5、CSS3)【系列二十】

    <Web 前端开发精华文章推荐>2013年第八期(总第二十期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各种增强网站用户体验的 jQuery 插件,展示前沿的 HTML5 和 C ...

  9. WCF编程系列(二)了解WCF

    WCF编程系列(二)了解WCF   面向服务     服务是复用进化的结果,起初的复用是函数,面向对象编程的出现使复用从函数上升到对象,随后面向组件编程又将复用从对象上升到组件,现在面向服务编程将复用 ...

随机推荐

  1. 《剑指offer》--- 两个链表的第一个公共结点

    本文算法使用python3实现 1. 问题   输入两个链表,找出它们的第一个公共结点.   时间限制:1s:空间限制:32768K 2 思路描述   使用两个指针 $ p1,p2 $ 分别指向两个链 ...

  2. lintcode-414-两个整数相除

    414-两个整数相除 将两个整数相除,要求不使用乘法.除法和 mod 运算符. 如果溢出,返回 2147483647 . 样例 给定被除数 = 100 ,除数 = 9,返回 11. 标签 二分法 思路 ...

  3. 使用coding.net上传项目

    鉴于上一次上传托管代码的惨烈教训,痛定思痛,决定把这次使用cooding.net上传的过程记录下来.也算是一篇简单的cooding初级使用教程了. 1.首先在cooding上新建项目 (1)填写项目名 ...

  4. DB2 9.5 数据库分区管理及应用实践

    DB2 数据库分区是 DB2 企业版 DPF(Data Partitioning Feature)选件提供的,它主要用来为大规模数据处理.高并发数据访问提供支持.DB2 数据库分区采用 Share-n ...

  5. iOS- 如何建立索引实现本地文本搜索引擎,允许容错搜索?

    1.前言 实现一个本地搜索引擎,允许容错搜索,也就是搜索结果不需要和搜索的关键字完全精准匹配.比如,搜索”eric wang“,搜索结果可以包括Erica Watts等等.搜索效率十分高. 这里我们需 ...

  6. bash循环语句

    1  )单分支if语句 if 测试条件 :then 如果满足条件就执行这里的代码 f 2)双分支的if语句 if  测试条件:then 如果满足条件就执行这里的代码 else 如果不满足条件就执行这里 ...

  7. Java String简单知识点总结

    1.字符串的比较 public void run(){ //str1在池中 String str1 = new String("String"); //str2,str3 存在于堆 ...

  8. 【week12】psp

    psp 项目 内容 开始时间 结束时间 被打断 净时间 12月2日 写博客 对各小组评价 11:20 12:05 0 45 写博客 final评价1 23:40 23:57 0 17 12月5日 看论 ...

  9. oracle & 的用法!

    /*select * from emp_bak where deptno = &"Department number" order by ename; select * f ...

  10. 【c】线性表

    数据对象集:线性表是N(>=0)个元素构成的有序序列,a1,a2,a3.....a(N-1),aN,a(N+1) 线性表上的基本操作有: ⑴ 线性表初始化:Init_List(L)初始条件:表L ...