目次


Ajax ベースの Web アプリケーションに使用される多様なクライアント - サーバー間通信メカニズム

Comments

はじめに

最近の Web アプリケーションはすべて、Ajax 関連のさまざまな概念をベースにしています。Ajax の手法が使われるようになったことから、対話型のインターフェースや動的なインターフェースが Web ページ上で使われることが多くなりました。Ajax 革命の起源となった発想は、Web アプリケーションはバックグラウンドでサーバーから非同期にデータを取得することができ、Web ページとサーバーとのやり取りはページが取得される時のみに限定されるわけではない、というものです。Web ページの概念は、長期間存続する Web アプリケーションへと拡張されました。こうした Web アプリケーションは、そのアプリケーションのバックエンドと継続的に通信しながらユーザーとやり取りします。こうした継続的な通信の内容には、次のようなものがあります。

  • 情報の送受信
  • 随時行われる入力の検証 (例えばパスワードの強度など)
  • サーバーで実行されたルールと分析に基づく、ユーザー入力の自動補完

クライアントとサーバーとのやり取りに関連するタスクを実行するためには、各通信タスク用に適切な通信メカニズムを提供する最適な通信レイヤーを、アプリケーションが持つ必要があります。

この記事では、通信レイヤーを作成する際に考慮すべき問題について、またそうした通信レイヤーに組み込む必要のあるメカニズムについて学びます。

従来どおりの方法で行う

かつて、Web ページとサーバーとの間の通信は不正侵入と見なされていました。そうした通信は、さまざまな HTML 要素を本来の目的とは異なる方法で使わない限り不可能でした。そのような使い方 (つまり不適切な使い方) が可能な HTML 要素の重要な側面は、それらの HTML 要素は本来、サーバーからファイルを取得するためのものであるということです。そうした HTML 要素を使用すると、ブラウザーは HTML 要素のタイプに基づいてファイルを解釈します。これらの要素には、以下のようなものがあります。

img
画像ファイルを取得します。
script
JavaScript™ ファイルを取得します。
iframe
HTML ファイルを表示し、また場合によっては HTML ファイルを取得する場合もあります。

これらの要素は本来、Web サイトのマークアップとして使われるものです。しかし JavaScript を使って DOM を操作すると、ページのライフサイクルの一部として、これらの要素を動的に注入することができ、またサーバーとやり取りすることができます。以下の 3 つのセクションでは、これらの要素の使い方について説明します。

起動した後は忘れる: <img> 要素を別の用途に使用する

img 要素の主な用途はサーバーから画像を取得して表示することです。ブラウザーは画像を取得するために、image 要素の src 属性の値である URL を使用して GET リクエストを作成します。この値はブラウザーの「同一生成元ポリシー」の制約は受けません。つまりその画像の URL は、その画像を表示するページと同じドメインに属するものである必要はありません。

可能なことと不可能なこと

img 要素を使うと、任意の URL から GET 呼び出しを行うことができます。つまり論理的には、別のサーバー上のサービスを呼び出すことができます。

ただしブラウザーは、その GET 呼び出しに対する応答は画像ファイルだと想定し、応答を画像ファイルとして処理します (画面上に画像を表示します)。応答が実際には画像ファイルではない場合には、その img 要素を DOM ツリーに追加しません。その GET リクエストは (その img 要素がページに追加されたかどうかとは無関係に)、src 属性が設定された時に作成されたものだからです。

img 要素は主に、「起動した後は忘れる」タイプのサービスに使用します。この場合、クライアントはサーバーから返される応答には関心がありません。img 要素は、「サーバーで発生したことはサーバー上にとどまる」というルールに従うサービスに適しています。

リスト 1 は「起動した後は忘れる」タイプの呼び出しを実行する方法を示しています。

リスト 1. 「起動した後は忘れる」タイプのサービスを呼び出す
<script type=”text/javascript”>
function fireAndForgetService(targetUrl){
  // first we create an img object
  var imgNode = document.createElement(“img”);
  // then we set its src attribute to the url of the service we’d like to invoke
  // when the next code line is executed the browser creates a GET request
  // and sends it to targetURL
  imgNode.src = targetUrl;
  
}
// calling the function with any url – cross site scripting is possible here
  fireAndForgetService(“http://www.theTargetUrl.com/doSomething?param1Name:param1Value”);
</script>

取得して実行する: <script> 要素を使用する

img 要素に関して上で説明した内容はほとんどすべて、script 要素を使う場合にも適用することができます。script 要素による通信メカニズムも、ブラウザーの「同一生成元ポリシー」の制約を受けません。

ただし <img> 要素と <script> 要素が異なる 1 つの点は、script 要素が使われる場合には、ブラウザーは実行可能な JavaScript コードを受信するものと想定することです。これは非常に強力なメカニズムであるため、安易に使うべきではありません。取得されたスクリプトは呼び出され、そのページからアクセス可能なすべてのもの (クッキー、DOM、履歴など) にアクセスすることができます。

可能なことと不可能なこと

ブラウザーで実行されるコードと取得されたスクリプトとの間には、何らかのプロトコルを定義する必要があります。皆さんがお使いのドメインからスクリプトを取得する場合には、ブラウザーで実行されるコードは大抵、アプリケーションのコードに含まれるさまざまな関数やユーティリティー、制約などを認識しているはずです。この場合のプロトコルは基本的に、同じアプリケーションの 2 つのコンポーネントを統合したものにすぎません。サーバーはブラウザーで実行すべきコード部分を、リクエストの一部として受信し、そのコード部分にさまざまな変更を加える場合もあります (国際化に関する制約の処理や、そのユーザーに特有の変更など)。

別のサーバーからコードを取得しなければならなくなると、興味深いことが起こります。この場合 (そしてそのドメインが信頼に足る場合) には、取得されたスクリプトはアプリケーションの構成体を認識していないため、静的なデータのみが取得されます。このデータをアプリケーションで処理するためには、取得された内容を何らかの方法でアプリケーションのコードと統合する必要があります。この場合に JSONP の概念を使用することができます。つまり基本的に、取得されたコードは JSON オブジェクトを引数として受け付ける関数呼び出しによってラップされたJSON オブジェクト (つまりこのような関数呼び出しを JSON オブジェクトに対して適用したもの) であるということです (この呼び出しはコールバックとして知られています)。このコールバック関数の名前は、取得されたスクリプトの URL に含まれる URL パラメーターとして送信されます。このコールバック関数の名前でラップされた JSON オブジェクトを提供するかどうかは、受信側のドメインに依存します。

リスト 2 はクライアント - サーバー間の通信を JSONP を使って行う方法を示しています。

リスト 2. JSONP による方法
<script type=”text/javascript”>
// the next function receives the following arguments:
// targetUrl – the url from which data is fetched and handled later as 
//             jsonp
// jsonpName – the name of the url parameter that the target url accepts and
//             knows to read from the callback name 
// callbackName – the name of the function that will handle the returned
//             json object
function invokeJSONP(targetUrl, jsonpName ,callbackName){
  // first we create a script object
  var scriptNode = document.createElement(“script”);
  // set its type so upon return it would be executed
  scriptNode.type = “text/javascript”;
  // set its src attribute to the url of the fetched script
  // and add to the url the callback name 
  scriptNode.src = targetUrl+”?”+ targetDomainJsonpName+”=”+callbackName;
  // adding the script to the page to get it up and running
  document.getElementsByTagName(“head”)[0].appendChild(scriptNode);
  
}
// calling the function with any url – cross site scripting is possible here

function handleJsonp(infoObject){
  validateInfoObject(infoObject);
  handleInfoObject(infoObject);
}

  invokeJSONP (“http://targetUrl.com/provideJsonpData”,“jsonpCallback”, “handleJsonp”);
</script>

ここで、<img> 要素を使用する場合と <script> 要素を使用する場合の小さな違いについて、1 つだけ触れておきます。<img> 要素を使用する場合には、その <img> 要素を DOM ツリーに追加する必要はありません。それとは対照的に、スクリプトを取得する方法を使う場合には、DOM ツリーに <script> 要素が追加されない限り GET リクエストは作成されません。

JSONP 呼び出し用の API (特に、アプリケーションに含まれるパラメーター名) を公開するかどうかは、ターゲット・ドメインに依存します。この API では、コールバック関数の名前を指定するだけでなく、レスポンス本体に入れて送信される JSON オブジェクトも API に含める必要があります。

リスト 2 で、ターゲット・ドメインは jsonpCallback (この名前はドメインによって異なります) という URL パラメーターを受け付け、handleJsonp を呼び出すスクリプトがターゲット・ドメインから返されるものと想定します。このスクリプトはリスト 3 のようなものです。

リスト 3. ターゲット・ドメインから返される JSONP
handleJsonp( {
  ‘height’:185,
  ‘units’:’cm’,
  ‘age’: 30,
  ‘favoriteFruit’:’apple’,
  ‘likesDogs’: true
  }
  );

仲介者に助けを求める: <iframe> 要素を使う

iframe 要素を使用すると、ページの中にページを埋め込むことができます。2 つのページが同じドメインのものである場合には、2 つのページの間で通信を行い、情報を交換することができます。

Ajax アプリケーションではこの仕組みを利用し、メイン・ページ内にある iframe 要素を使用して、ユーザーによる対話動作とクライアント - サーバー間の通信とを分離することが一般的です。iframe 要素はユーザーからは見えないように隠されており、ユーザーとアプリケーションとの対話動作にはまったく関与しません。

Ajax アプリケーションと iframe 要素を組み合わせることで、サーバーから任意のコンテンツを取得することができ、またページを更新せずにフォームを送信することができます。しかもそうしたことをすべて、背後で行うことができます。

可能なことと不可能なこと

これまでに説明したメカニズムでは、どの要素もページを構成する要素でしたが、そうしたメカニズムとは異なり、iframe 要素は要素そのものがページになっています。iframe 要素で指定できるのは、特定の種類のコンテンツ (画像など) やプロセス (受信したコンテンツの実行) に限定されません。従って、あらゆる種類のデータを任意のサーバーから取得し、また任意のサーバーに送信することができます。あらゆる種類のコンテンツを取得することができるため、サーバーとのやりとりをどのようなフォーマットのデータを使って行うことも可能であり、サーバー・サイドには柔軟性がもたらされます。サーバーへのデータの送信は、フォームの送信として行うことができ、GET リクエストに加えて POST リクエストも使うことができます。マルチパートのリクエストも送信できるため、クライアントのマシンからファイルをサーバーにアップロードすることもできます (ページ全体の更新を行うと、ユーザーとのやり取りがブロックされてしまうことを忘れないでください)。

ただし iframe を使う方法に欠点がないわけではありません。クロスサイトの呼び出しが可能なため、アプリケーションのセキュリティーが脆弱になるかもしれません。また、このメカニズムによるやり取りはページの履歴オブジェクトの中に入れられます。そのため、「進む/戻る」アクションを使ってナビゲートしているユーザーは混乱するかもしれません。

iframe を使う

iframe 要素を使用してサーバーからプログラムでコンテンツを取得する方法は script 要素を使う方法と似ていますが、重要な違いが 1 つあります。script 要素を作成してページに接続した後は、画面上に script 要素があることでユーザーが混乱しないように、その script 要素を隠す必要があります。

iframe 要素はフォーム送信の対象として使うことができ、それにより、フォーム送信の結果として行われるページ更新を防ぐことができます。

リスト 4 は、クライアント - サーバー間の通信をプログラムで行う代わりに、アプリケーションのマークアップの一部として iframe 要素を使用してファイルをアップロードする方法を示しています。

リスト 4. <iframe> を使ってファイルをアップロードする
<!—a hidden iframe that is the target of the form that would be used to upload a file -->
	 
<iframe id="IFrame" name="IFrame"
	  style="width:0px; height:0px; border:0px"
	  src="blank.html">
</iframe>

<!—the form is connected to the previous iframe by the target attribute, thus basically 
reloading that hidden frame upon form submission -->

<form name="UploadFile"  target="IFrame" method="POST"
    action="http://myServer/fileUploadServiceURL"
    enctype="multipart/form-data">
<input type="file" name="uploadFileNameId"/>
<input type="submit" value="Upload" name="submit"/>
</form>

Ajax の方法で行う

Ajax ベースの Web アプリケーションは通常、サーバー上で実行されるアプリケーションに対するクライアントとして動作するため、アプリケーションの通信負荷は高くなり、サーバーとの間で頻繁にデータが送受信されます。それに対処するには、データ・トランスポートを行うためのメカニズムが必要です。データ・トランスポート・メカニズムはアプリケーションの重要コンポーネントとして、可能な限り軽量でセキュアでなければなりません。幸いなことに、最近のあらゆるブラウザーには、まさにそのためのオブジェクト、XMLHttpRequest (XHR) が用意されています。XHR は実際に軽量であり、ページの取得元のサーバーに限定したリクエストを作成します。このため、クロスサイト・スクリプティングの問題はすべてなくなり、XHR ではテキストのみを送信することができます。

可能なことと不可能なこと

XHR オブジェクトを使用すると、アプリケーションはそのアプリケーション用のサーバーに情報を送信することができ、またそのアプリケーション用のサーバーからデータを取得することができます。このメカニズムは、これまで説明したメカニズムに比べ、以下のようにいくつかのメリットがあります。

あらゆる種類の HTTP メソッドを使用することができます
最近の一般的なサーバー・アーキテクチャーの大半は REST の原則をベースにしているため、リクエストとして GET、PUT、POST、DELETE を使用する必要があります。クライアント - サーバー間の通信の中核に XHR オブジェクトを使用することで、RESTful なサーバー・アーキテクチャーを実現することができます。
完了した時点で通知を受けることができます
XHR オブジェクトは、作成直後の状態から応答が完全にロードされた状態に至るまで、いくつかの状態で存在することができます。それぞれの状態が変化するとイベントが起動されるので、状態が変化した時に呼び出されるコールバックを定義しておくことができます。

このイベント・ハンドラーにより、サーバーから取得するデータに依存するコードは、そのデータが取得されると必ず実行されます。

ページ履歴に影響しません
XHR を呼び出してもページの履歴オブジェクトには反映されません。そのため、ブラウザーの「進む/戻る」アクションの通常の使い方から外れることはありません。
クロスサイト・スクリプティング (XSS) は許可されません
アプリケーションはクロスサイト・スクリプティングを実行できないため、よりセキュアになります。
ブロックするか否かを設定することができます
リクエストとレスポンスのサイクルを、下記のいずれか一方として定義することができます。
  • 同期型。ブラウザーがレスポンスを待つ間、どのコードも実行されません。
  • 非同期型。レスポンスを受信すると、コールバックが実行されます。
XML フォーマットも他の任意のフォーマットも使用することができます
レスポンスが XML フォーマットの場合には、そのレスポンス・データに対する完全な DOM ツリーが作成されます。また、レスポンスに含まれるテキストの内容をそのまま利用することもできます (そして JSON などの別のフォーマットでレスポンスを受信した場合には、その内容を処理することもできます)。

とは言え、XHR オブジェクトに関してすべてが素晴らしいわけではありません。送信できるのはテキストのみのため、ファイルをサーバーにアップロードする場合には相変わらず iframe を使う必要があります。また XHR オブジェクトの作成方法はブラウザーによって異なります。

まとめ

最近ではどのような Web アプリケーションでも、中心となるのはクライアントとサーバーとのやり取りになっています。この記事では、アプリケーションがクライアント - サーバー間でのやり取りをするための一般的なメカニズムについて学びました。この記事で説明したメカニズムがすべてのアプリケーションに必要なわけではありません。どのメカニズムが必要か、それらのメカニズムをどう使用するかは、皆さん次第です。

多くの JavaScript フレームワークには、こうした一連の Ajax 通信メカニズムの部分的な実装、または完全な実装が用意されています。どのフレームワークを使用し、どのメカニズムを実装するかを決定する際には、この記事の内容を検討してみてください。


ダウンロード可能なリソース


関連トピック

  • 軽量のデータ交換フォーマット、JSON について学んでください。
  • JSON スキーマを利用すると JSON オブジェクトの構造を構成することができます。
  • W3C の XMLHttpRequest 仕様を読んでください。この仕様には、クライアントとサーバー間でのデータ転送のためのクライアント関数をスクリプトで提供する API が定義されています。
  • W3C の Cross-Origin Resource Sharing 仕様にはクライアント・サイドでクロスオリジン・リクエストを行うためのメカニズムが定義されています。リソースに対してクロスオリジン・リクエストを行うための API の仕様には、この Cross-Origin Resource Sharing 仕様で定義されたアルゴリズムを使用することができます。
  • Cross-domain Ajax with Cross-Origin Resource Sharing は CORS に関する優れたブログ記事です。
  • ウィキペディアで Ajax について学んでください。
  • developerWorks の Web development ゾーンには、Web ベースの多様なソリューションに特化した記事が豊富に用意されています。
  • 皆さんの目的に最適な方法で IBM 製品を評価してください。製品の試用版をダウンロードする方法、オンラインで製品を試す方法、クラウド環境で製品を使う方法、あるいは SOA Sandbox で数時間を費やし、サービス指向アーキテクチャーの効率的な実装方法を学ぶ方法などがあります。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=500840
ArticleTitle=Ajax ベースの Web アプリケーションに使用される多様なクライアント - サーバー間通信メカニズム
publish-date=06222010