AJAXが注目されてから数年が経過しました。
XMLHttpRequestを利用した非同期通信は新しい技術ではありませんが、私を含め衝撃を受けた方は多いのではないでしょうか
ところで、このXMLHttpRequestを利用した非同期通信ですがセキュリティへの配慮から、異なるドメインへの通信は許されていません。
つまり通信を行う元サイトのドメイン(XMLHttpRequestを実装したスクリプトを実行するドメイン)とXMLHttpRequestの接続先のドメインが異なる場合は通信ができないということです。
小規模な業務システムであれば同一ドメイン内で処理が完結する事が多いと思いますが、大規模システム或いはインターネットサービスのようなグローバルに展開するサービスの場合、異なるドメインと接続したくなります。
解決方法の一つとして、サーバサイドの処理(非同期通信で実行する処理)をWebサービスで実装し、Webサービスメソッドをコールするaspx等のサーバファイル(XMLHttpRequestを実装したJavaScriptと同一のドメインに配置)を用意し、XMLHttpRequestを用いてコールします。(以下、例です)
<script type="text/javascript" charset="utf-8" src="./GetData.aspx"></script>
GetData.aspxを経由し、Webサービスをコールします。
GetData.aspxファイルはXMLHttpRequestを実装したスクリプトと同一のドメインに配置します。
この方法も利用環境が限定されています。つまりJavaScript(XMLHttpRequest実装ファイル)実行ドメインとGetData.aspxドメインが同じである必要があるのです。
つまりJavaScriptを任意のドメインに配置されたHTMLファイルに記述して非同期通信を行うという要件には適さないのです。
このような利用方法は、ブログへのブログパーツの設置などのようにHTMLのみの記述だけで、機能を配置するという要件では利用できないということです。
XMLHttpRequestを利用せずにクロスドメイン環境で非同期通信を行う方法としてJSONP(JSON with Padding)という方法があります
私はこの方法をネット巡回中に遭遇した際に本当に衝撃を受けました。
JSONPという表現をされていますが、基本的なデータ通信の考え方はJSONデータ形式に依存している訳ではありません。
JSONPの処理フローは以下の図のようになります。(クリックで拡大します)
図を解説すると、まず①の処理にて非同期通信を実装するHTMLにスクリプトを配置します。
①にてロード(実行)されるスクリプトにて、動的に<script>タグを生成し、src にサーバサイドの処理(データ取得処理等)を実装したaspxファイル(④の処理を行うaspx)を指定します。
次に生成したscript要素を<head>タグの子要素として登録します。
headタグ要素の読み込み処理は、bodyタグ内のデータ読み込み処理とは異なるスレッドで実行されるので、body要素はサーバとの通信処理を待つことなく描画処理が行われます。
※上記のようにheadタグにスクリプト(aspx)読み込み処理を実装せずに、bodyタグ内に実装した場合はサーバサイドの処理が終了するまで、bodyタグ内の次の要素(画像やテキスト等)の読み込みが行われないので画面表示は完了しません。(つまり非同期通信にはなりません)
④のサーバサイドの処理にてデータ取得処理を実行します。
ここからが肝なのですが、サーバサイドにてJavaScriptを動的に生成し、予めjsonp.js(②のスクリプト)に定義(上図の吹き出し部)しておいたJavaScript関数をコールするようにスクリプトを出力します。
静的に定義されているJavaScriptファイル(jsonp.js)とサーバサイドより動的に出力されたスクリプトをインタフェースする為に用意されているJsonpDataインスタンスがポイントです。
動作確認の為、以下のスクリプトを埋め込み、サーバと非同期通信を行い(サーバ側で10秒Sleepし、時間を取得します)、結果を下の白いエリアに出力します。
<script type="text/javascript" charset="utf-8" src="http://www.squabu.com/js/jsonp.js"></script>
上記のスクリプトタグをこの行の次の位置に定義します。(Bodyタグ内に設定しているという意味です)
画面描画完了後、10秒ぐらいで結果が出力される事を確認して下さい。(画面をリロードすれば動作確認できると思います)
サーバよりデータを取得しています・・・
先ほども書きましたが、結果として出力するデータ形式はJSONでなくても問題はありません。XML形式でも動作します。
ただ、JavaScriptでデータを扱うなら、XMLよりJSON形式の方が親和性があります。(取り扱いが非常に楽です)
ASP.NET 2.0 AJAX環境であれば、JavaScriptSerializerクラスを利用すれば、JSONデータの生成も一発で生成できます。
以下にサンプルコードを掲載します。(※サーバサイドで返却するデータ1つしかありませんが、は配列形式にしています。)
非同期通信処理を実装するHTML(aspx等)へのスクリプト定義(bodyタグ内に配置)
<script type="text/javascript" charset="utf-8" src="jsonp.js"></script>
サーバサイドとの非同期通信の仕組みを実装したJavaScript(jsonp.js)
var host='http://www.squabu.com/';//非同期通信処理を実装したホスト名(利用環境に応じて変更)
/********サーバサイドで出力するJavaScriptと当スクリプトのインタフェース変数*******/
var JsonpData = {};
//コールバック処理(サーバ側処理で動的に出力されるスクリプトよりコールされます)
JsonpData.onload = function(data){
JSONPman.onCallback(data);
};
/********************非同期データ通信処理実装クラス*******************/
function JSONPManager(){
this.REQUEST_URL = host + "CodeSamples/Samples/Json/getdata.aspx";//データ取得サイトのurl
};
JSONPManager.prototype={
/****************データ取得後にコールバックされるメソッド*******************/
onCallback : function(data){
if(data!=null){
var outputArea = document.getElementById("JsnpGetArea");
if(null!=outputArea){
outputArea.innerHTML = data[0].CurrentTime;
}
}
},
//初期処理
//データ取得用aspx読み込みスクリプトをHeaderタグに追加する
initialize : function(){
//データ取得用aspx読み込みスクリプトをHeaderタグに追加しデータ取得処理を実行する
var scriptElm = document.createElement("script");
scriptElm.setAttribute("type", "text/javascript");
scriptElm.setAttribute("charset", "utf-8");
scriptElm.setAttribute("src", this.REQUEST_URL);
var head = document.getElementsByTagName("head")[0];
head.appendChild(scriptElm);
}
};
var JSONPman = new (JSONPManager);
JSONPman.initialize();
サーバサイド処理(getdata.aspx):非同期で実行するサーバサイド処理
protected void Page_Load(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(10000);
//サンプルの為、手動でJSONデータを作成
string jsondata = "[{\"CurrentTime\":\"" + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + "\"}]";
//以下JavaScriptコードを出力
//※取得したJSONデータを配列に格納し、他のJavaScriptからアクセスできるようにする
//JSONP対応
Response.ContentType = "text/plain";
Response.Write("if (typeof(JsonpData) == 'undefined') JsonpData = {};\n");
Response.Write("JsonpData.data=" + jsondata + ";\n");
Response.Write("if (typeof(JsonpData.onload) == 'function') JsonpData.onload(JsonpData.data);");
}
上記のサンプルではコールバック関数は静的に設定していますが(getdata.aspxより出力するJavaScript部)、getdata.aspxコール時にGetパラメータにコールバック関数名を与えてあげれば、コールバック関数も任意に設定できるように変更できます。
最後にこのサンプルの方法でクロスドメイン環境でも非同期通信ができますが、デメリット(XMLHttpRequestに劣る部分)もあります。
- Get通信のみに対応(Postでは利用できません)
- Http Statusの取得ができません。
- Http ヘッダーの送信ができません。
利用できる範囲は限定されるかもしれませんが、画面起動時の非同期通信であれば利用用途も多いのではないでしょうか?
このサンプルサイトの左に配置されている携帯サイト無料作成ツールで作成された携帯サイトのプレビュー機能もこのサンプルの手法を採用しています。