HTML5 New Feature Series: Communication

Communication is one of the most important HTML5 Features, it provides an interactive mechanism based on the security policy of cross origins, and web applications can message each other even though they come from different sites, precisely benefits from this feature, it makes easier to integrate some resources from individually different web pages.

As we all know, in consideration of the security, browsers will properly limit the execution of JavaScript, it will forbid the script to access data service or DOM object of other sites, and in a certain extent, it eliminates the hidden security dangers, but meanwhile, it brings some troubles to Front-end developers, sometimes we must take the roundabout way to implement the functionality of cross-origin messaging. But with HTML5 is refining and being supported, if we try to resolve these problems by using Communication API, everything will be easier than before. And today let's get into the introduction of Communication API, we're going to talk about something related to Cross-origin Messaging.

Here we start from an example of Cross-origin Integration, assume that, we present to user some resources of different categories, these resources may not come from our own site, others instead. We can cooperate with other sites, and they provide us the needed resources, just like the following picture shows, there is a search input in current site page, and below the search input, there are two embedded pages that come from partners', one is news page, another is stocks page, they respectively show different information, and if user input the keyword to start searching, the news page and stocks page will receive a message, then they will send a request to their own server, lastly report the search results count to current site page.



We can easily perceive the change before and after the search operation, when user starts the search, it will trigger the operation of two sites separately at the same time, and when they get new search results, the results number will be reported to current site page, so the total number we saw in current page is exactly the sum of those two.

Now let's take a look at the page structure of current site:

<div>
	<input id="keyword" type="text"/>
	<button οnclick="search()">Search</button>
	<span id="results">
		<b id="resultsNum"></b> results.
	</span>
</div>

<!--news module-->
<iframe id='newsModule' src="http://test.news.com:8082/index.html"></iframe>

<!--stocks module-->
<iframe id="stocksModule" src="http://test.stocks.com:8083/index.html"></iframe>

As the codes show, we can easily see two iframe pages are embedded in the main page, and the http://test.integration.com:8081 is exactly our current site, it integrates two resources from different sites.

We all know that it will be considered as cross-origin if anyone of Protocol, Domain, Port is different between two URLs, so obviously, the pages we embedded above are cross-origin resources, therefore the main page can't access the embedded DOM object with JavaScript, and naturally, outer page can't trigger the script's execution in the inner page. If we try to do that, it will be like this:

Then how can we inform the embedded page to execute the corresponding code? Here it will refer to a very important API, the postMessage function, we use it to message other pages:

function search() {

	var keyword = document.getElementById('keyword').value;
	var newsOrigin = 'http://test.news.com:8082';
	var stocksOrigin = 'http://test.stocks.com:8083';

	document.getElementById('newsModule').contentWindow.postMessage({keyword: keyword}, newsOrigin);
	document.getElementById('stocksModule').contentWindow.postMessage({keyword: keyword}, stocksOrigin);
}

As we can see, when the search function is being executed, it will obtain the keyword from the input, and get the window object of each iframe, then call the postMessage function, which contains two important parameters, the first one is the data we want to pass, string type and object type are both ok, and the second one is the origin of the destination url, it consists of protocol, domain and port.

After the execution of postMessage function, the message has been sent to the embedded page. Then how to receive the message? Well, we can register the message event, for example, we can handle it like this:

var searchFromServer = function(keyword, callback) {
	//fake search logic
	
	var resultsNum = 3;
	document.getElementById('display').textContent = resultsNum + ' records by: ' + keyword;
	
	callback(resultsNum);
};

window.addEventListener('message', function(e) {
	var allowed = {
		'http://test.integration.com:8081': true
	};
	if (allowed[e.origin]) {
		var keyword = e.data.keyword;
		searchFromServer(keyword, function(resultsNum) {
			window.top.postMessage({resultsNum: resultsNum}, 'http://test.integration.com:8081');
		});
	}
}, false);
As the code shows, we registered the message event and assigned the handler function in the embedded news site, there're two important properties in the event, first one is the origin, as the name tells, it indicates the origin of message sender, and the data, is exactly the message data in this communication. Note that the origin is vital, because we can ignore some illegal requests from other unrelated sites and just handle the requests from our partner. And then, the searchFromServer function will be called, this function just return a mock data and as parameter pass to the callback function, and in the callback function we get the top window object by using window.top, and send a message to current site with the search result data.
And now let's have a look at the main site and how it handles with the message, here we've slightly modified the code in main site:

var resultsNum;

function search() {
	resultsNum = 0;

	var keyword = document.getElementById('keyword').value;
	var newsOrigin = 'http://test.news.com:8082';
	var stocksOrigin = 'http://test.stocks.com:8083';

	document.getElementById('newsModule').contentWindow.postMessage({keyword: keyword}, newsOrigin);
	document.getElementById('stocksModule').contentWindow.postMessage({keyword: keyword}, stocksOrigin);
}

window.addEventListener('message', function(e) {
	var allowed = {
		'http://test.news.com:8082': true,
		'http://test.stocks.com:8083': true
	};
	if (allowed[e.origin]) {
		resultsNum += e.data.resultsNum;

		document.getElementById('results').style.display = 'inLine';
		document.getElementById('resultsNum').textContent = resultsNum;
	}
}, false);
Similarly, we registered the message event and detected the message origin, in addition, we added a variable named 'resultNum', each time we start searching, it will be reset to 0, then show the new result number after receiving the result data from news or stocks. The following is the whole process:


It seems pretty simple, but it still needs some configurations if we want to run it locally, now let's figure it out how to config and run this example.

In brief, we need the following few steps:

1. modify hosts, configure the domain mappings.

2. install an http-server package on Node.js platform for running a simple server.

3. start these three applications separately with different ports.

First of all, we should modify our hosts file and append three configs to ensure the system be able to locate our server when visiting the specified domain.

127.0.0.1 test.news.com
127.0.0.1 test.stocks.com
127.0.0.1 test.integration.com

And next, we need to config our web server, make sure there has a Node.js platform on our local machine, and then execute the following command:

npm install http-server -g
At last, we need to start three servers, here is our folder structure:

What we need to do is to get into these three folders and start the server respectively, just quite simple commands below:

cd integration
http-server -p 8081

cd news
http-sever -p 8082

cd stocks
http-server -p 8083
It's done, now we can see the page presented above by visiting test.integration.com:8081. But note that, we'd better use the modern browser to make sure this feature is supported very well, and surely, we can also use the following approach to detect the supportive in real development.
var isMessageSupported = 'postMessage' in window;
Basically that's all about this topic, hope we can set up the environment and test the example in person, and finally, understand and master this feature.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章