前提条件:首先,页面需要部署在web服务器上;其次,两个页面必须来自不同的域。
postMessage API提供了一种交互方式,使得不同源的ifarme可以与其父页面进行通信。这个案例,小部件被包含在一个ifarme中,因此不能直接访问其父窗口。当聊天部件接收到聊天消息时,它可以用postMessage向父页面发送一个消息,以便父页面提醒聊天部件的用户接收到了新消息。同理,父页面也能将用户的状态发送给聊天部件,父页面和部件通过把彼此的源加到可信源的白名单中,就都能收到来自对方的消息。
127.0.0.1文件postMessagePortal.html
<!DOCTYPE html>
<title>门户页面标题</title>
<link rel="stylesheet" href="styles.css">
<script>
var trustedOrigin = "http://www.vserve365.com";
var defaultTitle = "门户页面标题";
var notificationTimer = null;
/**
* 消息事件是一个拥有data(数据)和origin(源)属性的DOM事件。data属性是发送方传递的时间消息,而origin属性是发送来源。有了origin属性,接收方能轻易地忽略掉来自不可信源的消息:根据可信源的列表能方便地判断来源是否可靠。
*/
function messageHandler(e) {
if (e.origin == trustedOrigin) {
notify(e.data);
} else {
// 忽视来自其他源的消息
}
}
/**
* 添加一个与聊天部件通信的函数。它用postMessage发送一个状态,更新新门户页面中的部件ifarme。对于在线的聊天应用,可以用这种方式来更新用户在线状态(在线、离开等)
*/
function sendString(s) {
document.getElementById("widget").contentWindow.postMessage(s, trustedOrigin);
}
/**
* 通知函数
*/
function notify(message) {
stopBlinking();
blinkTitle('[新消息提醒]' + message, defaultTitle);//闪烁标题信息
}
/**
* 停止闪烁标题函数
*/
function stopBlinking() {
if (notificationTimer !== null) {
clearTimeout(notificationTimer);
}
document.title = defaultTitle;
}
/**
* 闪烁标题
* @param {[string]} m1 闪烁的信息
* @param {[type]} m2 默认的标题信息
*/
function blinkTitle(m1, m2) {
document.title = m1;
notificationTimer = setTimeout(blinkTitle, 1000, m2, m1)
}
/**
* 发送状态信息
*/
function sendStatus() {
var statusText = document.getElementById("statusText").value;
sendString(statusText);
}
/**
* 封装一个向聊天小部件页面发送通知的函数,绑定点击事件。
*/
function loadDemo() {
document.getElementById("sendButton").addEventListener("click", sendStatus, true);
document.getElementById("stopButton").addEventListener("click", stopBlinking, true);
sendStatus();//页面加载立即调用,马上发送
}
/**
* 为window绑定load加载事件和message消息事件监听器
*/
window.addEventListener("load", loadDemo, true);
window.addEventListener("message", messageHandler, true);
</script>
<h1>跨域门户</h1>
<p><b>源</b>: http://127.0.0.1</p>
状态 <input type="text" id="statusText" value="Online">
<button id="sendButton">改变状态</button>
<p>
这用PostMessage发送状态更新包含在门户页面控件iframe。
</p>
<iframe id="widget" src="http://www.vserve365.com/postMessageWidget.html"></iframe>
<p>
<button id="stopButton">停止闪烁标题</button>
</p>
远程服务器文件postMessageWidget.html
<!DOCTYPE html>
<title>聊天小部件页面</title>
<link rel="stylesheet" href="styles.css">
<script>
var trustedOrigin = "http://127.0.0.1";
// TODO 可以添加多个白名单[数组]
/**
* 消息事件是一个拥有data(数据)和origin(源)属性的DOM事件。data属性是发送方传递的时间消息,而origin属性是发送来源。有了origin属性,接收方能轻易地忽略掉来自不可信源的消息:根据可信源的列表能方便地判断来源是否可靠。
*/
function messageHandler(e) {
if (e.origin === "http://127.0.0.1") {
document.getElementById("status").textContent = e.data;
} else {
// 忽视来自其他源的消息
}
}
/**
* 编写用于与门户通信的函数。在接收到新消息时,部件将请求门户页面通知用户,并且用postMessage()函数向门户页面发送消息
*/
function sendString(s) {
window.top.postMessage(s, trustedOrigin);//最外层的窗口
}
/**
* 封装一个向门户页面发送通知的函数,绑定点击事件。
*/
function loadDemo() {
document.getElementById("actionButton").addEventListener("click",
function() {
var messageText = document.getElementById("messageText").value;
sendString(messageText);
}, true);
}
/**
* 为window绑定load加载事件和message消息事件监听器
*/
window.addEventListener("load", loadDemo, true);
window.addEventListener("message", messageHandler, true);
</script>
<h1>小部件iframe</h1>
<p><b>源</b>: http://127.0.0.1</p>
<p>状态设置: <strong id="status"></strong>(来自于门户).<p>
<div>
<input type="text" id="messageText" value="聊天小部件的通知信息">
<button id="actionButton">发送通知</button>
</div>
<p>
这将要求门户通知用户。门户通过闪烁标题来实现这一点。如果消息来自于HTTP之外的源代码,例如.NET 9999,门户页面将忽略它。
</p>
运行效果截图:
结束语:在处理跨源通信的消息时,一定要验证每个消息的源。此外,处理消息中的数据时也应该谨慎。即使消息来源可信源,也应该像对待外部输入时一样仔细。