Ajax をマスターする: 第 3 回 Ajax での高度な要求と応答

HTTP ステータス・コード、Ready 状態、そして XMLHttpRequest オブジェクトを完全に理解する

多くの Web 開発者たちにとって、シンプルな要求を行って、シンプルな応答を受け取ることができれば、それですべての必要は満たせます。しかし Ajax をマスターしたいのであれば、HTTP ステータス・コード、Ready 状態、そして XMLHttpRequest オブジェクトを完全に理解することが必須です。今回の記事では、Brett McLaughlin が各種のステータス・コードを紹介し、そのそれぞれをブラウザーがどのように処理するかを説明します。そして最後に、使用されることは少ないものの Ajax を使用して実行できる HTTP 要求を紹介します。

Brett McLaughlin, Author and Editor, O'Reilly Media Inc.

Photo of Brett McLaughlinBrett McLaughlin は、Logo の時代からコンピューター業界に携わっています (あの小さな三角形を覚えていますか)。近年、Java および XML コミュニティーでもっとも著名な作成者兼プログラマーの 1 人です。彼の功績には Nextel Communications での複合エンタープライズ・システムのインプリメンテーション、Lutris Technologies でのアプリケーション・サーバーの作成があり、最近では O'Reilly Media, Inc. で関連本の著作および編集に従事しています。近日出版予定の著書『Head Rush Ajax ― 学びながら読む Ajax 入門』では、賞を獲得した革新的な Head First アプローチを Ajax にもたらしています。最近の著作には、最新バージョンの Java テクノロジーに関する最初の本、『Java 5.0 Tiger』があります。また、彼の著名な『Java & XML』は今でも、Java 言語での XML テクノロジーの使用方法に関するもっとも信頼のおける著作物の 1 つとして数えられています。



2006年 2月 14日

この連載の前回の記事では、XMLHttpRequest オブジェクトの基本を十分に紹介しました。Ajax アプリケーションで最も重要なこのオブジェクトは、サーバー・サイドのアプリケーションまたはスクリプトに対する要求を扱うとともに、サーバー・サイドのコンポーネントから返されたデータも処理します。すべての Ajax アプケーションはこの XMLHttpRequest オブジェクトを使用するため、Ajax アプリケーションをただ機能させるだけではなく、見事に機能させるためには、このオブジェクトを深く理解していなければなりません。

今回の記事では、前回説明した基本を超えて、この要求オブジェクトにとって鍵となる次の 3 つの重要な部分に注目し、その詳細をさらに掘り下げていきます。

  • HTTP Ready 状態
  • HTTP ステータス・コード
  • 実行できる要求のタイプ

上記のそれぞれは、一般には要求のパイプ役と考えられているため、これらの部分については詳細がほとんど書かれていません。けれども、Ajax プログラミングについて聞きかじるだけなく、本格的に取り組むつもりであれば、Ready 状態、ステータス・コード、要求に精通する必要があります。アプリケーションで問題が発生した場合 (問題が起こるのは物事の常です)、Ready 状態について、HEAD 要求を行う方法について、あるいは 400 ステータス・コードの意味を理解しているかどうかが、5 分間のデバッグで済むのか、または苛立ちと混乱のなかで 5 時間を費やすかの分かれ目となります。

XMLHttpRequest か、XMLHttp か: 名前は違っても同じオブジェクトです

Mozilla、Opera、Safari はじめとするほとんどの Microsoft™ 製でないブラウザーでは XMLHttpRequest オブジェクトを使用していますが、Microsoft と Internet Explorer では代わりに XMLHttp という名前のオブジェクトを使用しています。簡単のため、この記事ではどちらのオブジェクト・タイプも XMLHttpRequest と呼びます。Web 全体ではこの呼び方が普通であり、Microsoft でもこれに倣い、Internet Explorer 7.0 では要求オブジェクトを XMLHttpRequest と呼ぶ意向です (詳細については、第 2 回を読み返してください)。

まずは HTTP Ready 状態から取り上げます。

HTTP Ready 状態のさらなる詳細

前回の記事を読んで覚えていると思いますが、XMLHttpRequest オブジェクトには readyState という名前のプロパティーがあります。このプロパティーによってサーバーが要求の処理を完了したことが確実になると、通常はコールバック関数がサーバーからのデータを使用して Web フォームまたはページを更新します。リスト 1 に、その単純な例を記載します (連載の前回の記事にも同じ例が記載されています。「参考文献」を参照してください)。

リスト 1. コールバック関数でサーバーの応答を処理する
 function updatePage() {
if (request.readyState == 4) {
     if (request.status == 200) {
       var response = request.responseText.split("|");
       document.getElementById("order").value = response[0];
       document.getElementById("address").innerHTML =
         response[1].replace(/\n/g, "<br />");
     } else
       alert("status is " + request.status);
   }
 }

これは間違いなく最も一般的な (そして最も単純な) Ready 状態の使い方です。「4」という数字から想像できるように、他にも Ready 状態はあります (このリストも、前回の記事に記載されています。「参考文献」を参照)。

  • 0: 要求が初期化されていない状態 (open() を呼び出す前)
  • 1: 要求はセットアップされているが、送信されていない状態 (send() を呼び出す前)
  • 2: 要求が送信され、処理されている状態 (通常はこの時点で、応答からコンテンツ・ヘッダーを取得することができます)
  • 3: 要求が処理されている状態。大抵は応答から部分的なデータを入手可能であるが、サーバーは応答の処理を完了していない状態
  • 4: 応答が完了し、サーバーの応答を取得して使用できる状態

Ajax プログラミングで基本的な内容以上の処理を行うには、これらの Ready 状態について知るだけでなく、その状態がいつ発生し、どのように使用できるかについても知らなければなりません。特に重要なのは、どの要求の状態のときにそれぞれの Ready 状態が発生するのかを学ぶことです。残念ながら、これはかなり直観に反している上、さらに特殊なケースも関係してきます。

扱われることの少ない Ready 状態

最初の Ready 状態は readyState プロパティー 0 (readyState == 0) として表され、要求が初期化されていない状態を表します。要求オブジェクトで open() を呼び出すと同時に、このプロパティーは 1 に設定されます。open() はほとんど常に、要求の初期化と同時に呼び出されるため、readyState == 0 を目にすることはめったにありません。さらに実際のアプリケーションでは、初期化されていない Ready 状態は、ほとんど役に立ちません。

とは言うものの、完璧さを期してリスト 2 を見てください。ここには、Ready 状態が 0 に設定されているときに、この状態を取得する方法が示されています。

リスト 2. Ready 状態 0 を取得する
   function getSalesData() {
     // Create a request object
     createRequest();		
alert("Ready state is: " + request.readyState);

     // Setup (initialize) the request
     var url = "/boards/servlet/UpdateBoardSales";
     request.open("GET", url, true);
     request.onreadystatechange = updatePage;
     request.send(null);
   }

この単純な例では、(ボタンがクリックされたときなどに) Web ページが getSalesData() 関数を呼び出して要求を開始します。注意する点として、Ready 状態は open() が呼び出される前にチェックしなければなりません。図 1 に、このアプリケーションを実行した結果を示します。

図 1. Ready 状態 0
Ready 状態 0

0 が 4 に相当する場合

複数の JavaScript 関数が同じ要求オブジェクトを使用するような状況では、要求オブジェクトが使用中でないことを確実にするために Ready 状態が 0 であることを確認する、という方法は問題になる可能性があります。readyState == 4 は要求の完了を示しますが、使用されていない要求オブジェクトの Ready 状態が 4 に設定されたままの状態であることも珍しくありません。これは、サーバーからのデータは使用されたけれども、それ以降、Ready 状態をリセットする動作が何も行われていないことを意味します。要求オブジェクトをリセットする abort() という関数はありますが、この関数は実際にはリセットを目的としたものではありません。複数の関数が要求オブジェクトを使用しなければならない場合には、1 つの要求オブジェクトを複数の関数で共有するのではなく、関数ごとに要求オブジェクトを作成して使用したほうが得策です。

明らかに、この状態を取得してもあまり役には立ちません。open() がまだ呼び出されていないことを確認する必要がある場合はかなり限られているからです。現実的な Ajax プログラミングのほとんどの場合、この Ready 状態を唯一利用するのは、複数の関数で同じ XMLHttpRequest オブジェクトを使用して複数の要求を行う場合だけです。その (かなり特殊な) 場合には、要求オブジェクトが初期化されていない状態 (readyState == 0) であることを確認してから新しい要求を行う必要があります。これによって基本的に、同時に別の関数がそのオブジェクトを使用していないことが確実になります。

進行中の要求の Ready 状態を確認する方法

通常の要求および応答では、0 の Ready 状態は別として、要求オブジェクトは各 Ready 状態を遷移し、最終的には Ready 状態 4 にならなければなりません。そこで登場するのが、ほとんどのコールバック関数に含まれる if (request.readyState == 4) というコード行です。これによって、サーバーが処理を完了したこと、そして Web ページの更新やサーバーから返されたデータを基にアクションを行っても安全であることが確実になります。

このプロセスを実際に発生と同時に確認するのは簡単なことです。Ready 状態が 4 になったらコールバックでコードを実行する代わりに、コールバックが呼び出されるたびに Ready 状態を出力すればよいだけの話です。その場合のコードの一例をリスト 3 に記載します。

リスト 3. Ready 状態を確認する
   function updatePage() {
     // Output the current ready state
     alert("updatePage() called with ready state of " + request.readyState);
   }

このコードをどうやって実行するかがわからない場合のために説明すると、その方法は、Web ページから呼び出す関数を作成し、その関数にサーバー・サイドのコンポーネントに対して要求を送信させるというものです (リスト 2 や、連載第 1 回と第 2 回の例に記載されているような関数です)。要求をセットアップするときには、必ずコールバック関数を updatePage() に設定してください。それには、要求オブジェクトの onreadystatechange プロパティーを updatePage() に設定します。

上記のコードは、onreadystatechange が具体的に何を意味するのかを明らかに説明します。このコードでは要求の Ready 状態が変わるたびに updatePage() を呼び出し、ユーザーにアラートを表示します。図 2 に、この関数が呼び出された場合の例を記載します。この例では、Ready 状態は 1 になっています。

図 2. Ready 状態 1
Ready 状態 1

このコードを自分で試してみてください。Web ページにコードを組み込んで、イベント・ハンドラーを有効にすると (ボタンをクリックするか、タブでフィールドから移動するか、または要求をトリガーするためにセットアップした何らかのメソッドを使用します)、要求の Ready 状態が変わるたびに、コールバック関数が実行され、アラートが表示されます。これは、各段階を遷移する要求の状態を把握するのに最適な方法です。

ブラウザー間の矛盾

このプロセスの基本がわかったところで、いくつか異なる種類のブラウザーからこの Web ページにアクセスしてみてください。ブラウザーによって Ready 状態の処理方法に違いがあることがわかるはずです。例えば Firefox 1.5 では、以下の Ready 状態が表示されます。

  • 1
  • 2
  • 3
  • 4

上記の場合、要求の各段階が示されているので驚くようなものではありません。その一方で、Safari を使用して同じアプリケーションにアクセスすると、興味深い結果が表示されます (あるいは、表示されないと言ったほうがよいかもしれません)。Safari 2.0.1 で表示される状態は以下のとおりです。

  • 2
  • 3
  • 4

Safari は最初の Ready 状態を省略していますが、その理由について理にかなった説明はなく、Safari はこのように機能するとしか言えません。また、これは重要な点を明らかにしています。それは、サーバーからのデータを使用する前に要求の Ready 状態が 4 であることを確実にするのは賢明とは言え、中間の各 Ready 状態に依存するコードを作成すると、ブラウザーによって確実に結果が異なってくるということです。

例えば Opera 8.5 を使用した場合、表示される Ready 状態はさらに少なくなります。

  • 3
  • 4

そして肝心の Internet Explorer は以下の状態で応答します。

  • 1
  • 2
  • 3
  • 4

要求で問題が発生した場合に真っ先に調べなければならないのは、この Ready 状態です。すべてが正常に機能していることを確認できるように、要求の Ready 状態を示すアラートを追加してください。Internet Explorer と Firefox の両方でテストすれば、さらに効果的です。なぜなら、この 2 つのブラウザーでは 4 つすべての Ready 状態が表示されるので、要求の各段階を確認することができるからです。

次は、応答について見てみましょう。

応答データについての詳細

要求の実行中に発生する各種の Ready 状態について理解したら、次は XMLHttpRequest オブジェクトのなかのもう 1 つの重要な部分である responseText プロパティーについて見て行くことができます。前回の記事で、サーバーからデータを取得するには、このプロパティーを使用すると説明したことを思い出してください。サーバーは要求の処理を完了すると、要求に応答するために必要なデータを要求 (request) の responseText に配置します。これによって、コールバック関数がそのデータを使用できるようになります (リスト 1 およびリスト 4 を参照)。

リスト 4. サーバーからの応答を使用する
   function updatePage() {
     if (request.readyState == 4) {
       var newTotal = request.responseText;
       var totalSoldEl = document.getElementById("total-sold");
       var netProfitEl = document.getElementById("net-profit");
       replaceText(totalSoldEl, newTotal);

       /* Figure out the new net profit */
       var boardCostEl = document.getElementById("board-cost");
       var boardCost = getText(boardCostEl);
       var manCostEl = document.getElementById("man-cost");
       var manCost = getText(manCostEl);
       var profitPerBoard = boardCost - manCost;
       var netProfit = profitPerBoard * newTotal;

       /* Update the net profit on the sales form */
       netProfit = Math.round(netProfit * 100) / 100;
       replaceText(netProfitEl, netProfit);
     }

かなり単純なリスト 1 と比べると、リスト 4 は多少複雑ですが、両方とも最初に Ready 状態を確認してから responseText プロパティーの値を取得することに変わりはありません。

要求の実行中に応答テキストを確認する方法

Ready 状態と同じように、responseText の値も要求のライフ・サイクルを通して変更されます。実際にどのように変更されるかを確認するには、リスト 5 のようなコードを使用して要求の応答テキスト (responseText) とその Ready 状態 (readyState) をテストします。

リスト 5. responseText プロパティーをテストする
   function updatePage() {
     // Output the current ready state
     alert("updatePage() called with ready state of " + request.readyState +
           " and a response text of '" + request.responseText + "'");
     }

ブラウザーで Web アプリケーションを開いて、要求を開始してください。このコードを最大限に生かすには、Firefox または Internet Explorer のいずれかを使用します。この 2 つのブラウザーは要求の実行中に考えられるすべての Ready 状態をレポートするからです。例えば Ready 状態 2 では、responseText プロパティーは定義されていません (図 3 を参照)。そのため、JavaScript コンソールも開かれている場合には、そこにエラーが表示されます。

図 3. Ready 状態 2 の応答テキスト
Ready 状態 2 の応答テキスト

けれども Ready 状態 3 では、サーバーが responseText プロパティーに値を配置した状態になっているはずです。少なくとも、この例ではそうなっています (図 4 を参照)。

図 4. Ready 状態 3 の応答テキスト
Ready 状態 3 の応答テキスト

Ready 状態 3 での応答は、スクリプトやサーバー、それにブラウザーによっても異なりますが、それでもアプリケーションをデバッグする際には非常に役立ちます。

安全にデータを取得する方法

あらゆる資料と仕様では、データを安全に使用できるのは Ready 状態が 4 の場合に限ると断言しています。私は自信を持って言いますが、Ready 状態が 3 のときに responseText プロパティーからデータを取得できないことはまれです。しかし、アプリケーションでこの事実に頼るのは賢明ではありません。Ready 状態 3 でデータが完全であることに依存するコードを作成するということは、データが不完全になることを保証するようなものです。

それよりも良い考えは、Ready 状態が 3 になった時点で、ユーザーに応答をまもなく入手できるというフィードバックを提供することです。alert() などの関数を使用するのは明らかに愚策ですが (Ajax を使用していながらもアラート・ダイアログ・ボックスでユーザーの操作をブロックするのは直観に反しています)、Ready 状態が変更されたときにフォームやページのフィールドを更新するという方法は使えます。例えば、プログレス・バーを Ready 状態 1 では 25 パーセント、Ready 状態 2 では 50 パーセント、Ready 状態 3 では 75 パーセント、Ready 状態 4 では 100 パーセント (完了) に設定するなどです。

もちろん前に説明したように、この方法は賢いとは言え、ブラウザーに依存します。Opera では最初の 2 つの Ready 状態になることはなく、Safari では最初の Ready 状態 1 を省略します。そのため、これらのブラウザー用には、それに応じたコードを作成しなければなりませんが、このようなコードを作成するのは読者の演習として残しておきます。

次は、ステータス・コードについて説明します。

HTTP ステータス・コードについての詳細

Ajax プログラミング手法として Ready 状態とサーバーの応答を使えるようになったところで、今度は Ajax アプリケーションをさらに洗練したものにします。そのために、HTTP ステータス・コードを扱うようにします。HTTP ステータス・コードは Ajax にとって新しいものではなく、Web ではその誕生以来使用されてきました。例えば、Web ブラウザーでは以下のステータス・コードを目にしたことがあるでしょう。

  • 401: Unauthorized
  • 403: Forbidden
  • 404: Not Found

ステータス・コードは上記の他にもたくさんあります (すべてのコードのリストについては、「参考文献」を参照してください)。Ajax アプリケーションをさらに制御して応答性の良いものにする (特に、より確実なエラー処理を行う) には、要求および応答のステータス・コードを必要に応じて確認する必要があります。

200: すべて問題なし

多くの Ajax アプリケーションでは、Ready 状態を確認した後にサーバーからの応答データの処理を続けるコールバック関数を使用しています (リスト 6 を参照)。

リスト 6. ステータス・コードを無視するコールバック関数
   function updatePage() {
if (request.readyState == 4) {
       var response = request.responseText.split("|");
       document.getElementById("order").value = response[0];
       document.getElementById("address").innerHTML =
         response[1].replace(/\n/g, "<br />");
     }
   }

Ajax プログラミングでは、この手法は短絡的で間違いの元となります。例えばスクリプトが認証を要求する場合、要求が有効なクレデンシャルを提供しなければ、サーバーは 403 または 401 などのエラー・コードを返します。けれどもこれによってサーバーは要求に応答したことになるため (それが目的とする応答ではなかったり、要求に期待される応答ではなかったりしたとしても)、Ready 状態は 4 に設定されます。その結果、ユーザーは有効なデータを取得できないばかりか、存在しないサーバーのデータを JavaScript が使用しようとすると不愉快なエラーを受け取ることになります。

サーバーが要求の処理を完了したことだけでなく、「すべて問題がない」ことを示すステータス・コードを返すようにするには、ほんのわずかな作業しか必要ありません。これに該当するステータス・コード「200」は、XMLHttpRequest オブジェクトの status プロパティーでレポートされます。サーバーが要求の処理を完了しただけでなく、OK のステータスをレポートしたことを確認するには、コールバック関数にさらに別の確認を行うコードを追加します (リスト 7 を参照)。

リスト 7. 有効なステータス・コードを確認する
   function updatePage() {
     if (request.readyState == 4) {
       if (request.status == 200) {
         var response = request.responseText.split("|");
         document.getElementById("order").value = response[0];
         document.getElementById("address").innerHTML =
           response[1].replace(/\n/g, "<br />");
       } else
         alert("status is " + request.status);
     }
   }

このように数行のコードを追加するだけで、問題が発生した場合に、ユーザーに対して何の説明もない文字化けしたデータのページを表示するのではなく、(不明な点はあるものの) 有益なエラー・メッセージを確実に提供することができます。

リダイレクトと再ルーティング

エラーについて詳しく説明する前に、Ajax を使用する場合にはおそらく心配しなくてもよいことについて話しておきます。それは、リダイレクトです。HTTP ステータス・コードでこれに該当するのは 300 番台のステータス・コードで、例えば以下のものがあります。

  • 301: Moved Permanently
  • 302: Found (要求が別の URL/URI にリダイレクトされたことを意味します)
  • 305: Use Proxy (要求は、要求するリソースにプロキシーを使用してアクセスする必要があることを意味します)

Ajax プログラマーがリダイレクトを心配しなくてもよいと思われる理由には、以下の 2 つがあります。

  • まず、Ajax アプリケーションはほとんど常に、特定のサーバー・サイドのスクリプト、サーブレット、またはアプリケーションを対象に作成されます。Ajax プログラマーの知らないうちに、対象とするコンポーネントが消失したり、別の場所に移動されたりすることは極めてまれです。Ajax プログラマーは大抵の場合、(自分で移動したか、移動させたかしたために) リソースが移動されたことを知っていたり、要求 URL を自分で変更したりするはずなので、知らないうちに対象とするコンポーネントが消失したり、別の場所に移動されたりすることは決してないはずです。
  • さらにもっともな理由は、Ajax アプリケーションおよび要求はサンドボックス化されるためです。これは、Ajax 要求を行う Web ページを提供するドメインと、それらの要求に応答するドメインは同じドメインでなければならないことを意味します。つまり、ebay.com から提供される Web ページは、amazon.com で実行されるスクリプトに対して Ajax スタイルの要求を行うことはできません。また、ibm.com の Ajax アプリケーションが netbeans.org で実行されるサーブレットに対して要求を行うことも不可能です。

したがって、セキュリティー・エラーを発生させることなく、要求が別のサーバーにリダイレクトされるという事態は起こり得ません。セキュリティー・エラーが発生した場合には、ステータス・コードを受け取ることは一切なく、通常はデバッグ・コンソールに JavaScript エラーが表示されるだけです。要するに、数多くあるステータス・コードについて考慮する際には、そのうちのリダイレクトに関するコードはほとんど無視できるということです。

エッジ・ケース、そして手強いケース

この時点で、新米プログラマーは一体何がそんなに問題なのかを不思議に思っていることでしょう。Ajax 要求のうち、Ready 状態 2 と 3、そして 403 などのステータス・コードに対処しなければならない要求は、5 パーセントに満たないことは確かです (実際には 1 パーセント程度あるいはそれ以下かもしれません)。しかしこれに該当するケースが重要であり、とりわけ稀な条件に適合する極めて異常な状況で発生する事態であることから、エッジ・ケースと呼ばれます。エッジ・ケースは異常な状況でありながらも、ユーザーが感じるフラストレーションの約 80 パーセントを占めています。

一般的なユーザーは、アプリケーションが 100 回正常に機能してもそれを忘れてしまいますが、正常に機能しなかったときのことははっきりと覚えているものです。エッジ・ケース、そして手強いケースに対処することができれば、ユーザーは満足して、またサイトに戻ってくることになります。

エラー

ステータス・コード 200 には対処しました。300 番台のステータス・コードはほとんど無視できることもわかりました。残るは、さまざまなタイプのエラーを示す 400 番台のステータス・コードに対処すればよいだけです。リスト 7 を見ると、エラーは処理されるものの、ユーザーに出力されるのは極めて一般的なエラー・メッセージでしかないことがわかります。方向としては間違っていませんが、アプリケーションを操作しているユーザーやプログラマーに具体的に何が誤っているのかを伝えるという点では、一般的なエラー・メッセージではほとんど役に立ちません。

そこでまずは、見つからないページに対するサポートを追加します。ページが見つからないという事態は本番システムではあってはならないことですが、テストの際にスクリプトが移動されたり、プログラマーが誤った URL を入力したりすることは珍しくありません。グレースフルに 404 エラーをレポートできれば、混乱したユーザーやプログラマーにさらに大きな救いの手を差し伸べられることになります。例えば、サーバー上のスクリプトが削除されている場合、リスト 7 のコードを使用すると図 5 のように何の説明もないエラーが表示されます。

図 5. 一般的なエラー処理
一般的なエラー処理

これでは、問題が認証なのか、スクリプトが見つからないことなのか (この例の場合の問題はこれです)、ユーザー・エラーなのか、あるいはコードの何かが問題の原因になっているのかを判断する手段がありません。しかし単純なコードを追加することで、このエラーを遙かに具体的な内容にすることができます。リスト 8 では、具体的なメッセージを使用してスクリプトの欠落と認証エラーの両方を処理しています。

リスト 8. 有効なステータス・コードを確認する
   function updatePage() {
     if (request.readyState == 4) {
       if (request.status == 200) {
         var response = request.responseText.split("|");
         document.getElementById("order").value = response[0];
         document.getElementById("address").innerHTML =
           response[1].replace(/\n/g, "<br />");
       } else if (request.status == 404) {
         alert ("Requested URL is not found.");
       } else if (request.status == 403) {
         alert("Access denied.");
       } else
         alert("status is " + request.status);
     }
   }

このコードは比較的単純なものの、追加情報を提供することは確かです。図 6 に示すのは図 5 と同じエラーですが、今回はエラー処理コードがユーザーまたはプログラマーに何が起こっているのかを遙かに明確に伝えています。

図 6. 具体的なエラー処理
具体的なエラー処理

皆さんがそれぞれに作成するアプリケーションでは、認証による失敗が発生した場合にはユーザー名とパスワードを消去し、エラー・メッセージを画面に追加することを検討してください。スクリプトの欠落やその他の 400 番タイプのエラー (許可されていない HEAD 要求を送信した場合の未許可の要求メソッドに対する 405、プロキシーに対する認証が必要となる 407 など) をグレースフルに処理するにも、同様の方法を適用することができます。どのエラーを対象にすることにしても、まずはサーバーから返されたステータス・コードを処理するところから始まります。

その他の要求タイプ

XMLHttpRequest オブジェクトの制御に真剣に取り組みたいのであれば、この最後のセクションの内容を検討してください。それは、HEAD 要求をレパートリーに加えることです。GET 要求を行う方法については前の 2 回の記事で説明しました。POST 要求を使ってサーバーにデータを送信する方法についても今後の記事で詳しく学びます。けれども高度なエラー処理と情報収集を目指すのなら、HEAD 要求を行う方法を学んでください。

HEAD 要求を行う方法

実際に HEAD 要求を行うのはかなり簡単なことで、「GET」や「POST」の代わりに「HEAD」を最初のパラメーターとして指定して open() メソッドを呼び出せばよいだけです (リスト 9 を参照)。

リスト 9. Ajax で HEAD 要求を行う
   function getSalesData() {
     createRequest();
     var url = "/boards/servlet/UpdateBoardSales";
     request.open("HEAD", url, true);
     request.onreadystatechange = updatePage;
     request.send(null);
   }

このような HEAD 要求を行うと、サーバーは GET や POST 要求の場合とは異なり、実際の応答を返しません。サーバーが返すのはリソースのヘッダーだけですが、そこには応答のコンテンツが最後に変更された時刻、要求されたリソースの有無、その他かなりたくさんの興味深い情報が含まれています。これらの情報を利用すると、サーバーが該当リソースを処理して返す以前に、そのリソースに関する情報を得ることができます。

このような要求を使って実行できる最も簡単なことは、単純に応答ヘッダーをすべて出力することです。これにより、HEAD 要求を利用してどのような情報を入手できるのかを把握することができます。リスト 10 に記載するのは、HEAD 要求からすべての応答ヘッダーを出力する単純なコールバック関数です。

リスト 10. HEAD 要求からすべての応答ヘッダーを出力する
   function updatePage() {
     if (request.readyState == 4) {
       alert(request.getAllResponseHeaders());
     }
   }

図 7 に、サーバーに対して HEAD 要求を行う単純な Ajax アプリケーションからの応答ヘッダーを示します。

図 7. HEAD 要求による応答ヘッダー
HEAD 要求による応答ヘッダー

これらのヘッダーのどれを取っても (サーバー・タイプからコンテンツ・タイプに至るまで)、Ajax アプリケーションに含まれる情報または機能がより詳細にわかります。

URL を確認する方法

URL が存在しない場合に 404 エラーを確認する方法はすでに説明しましたが、これが一般的な問題 (特定のスクリプトやサーブレットが頻繁にオフラインになるなど) になった場合には、GET または POST 要求を行う前に、URL を確認する必要があります。そのためには、HEAD 要求を行ってから、コールバック関数で 404 エラーがあるかどうかを調べます。リスト 11 に、コールバックの一例を記載します。

リスト 11. URL の有無を確認する
   function updatePage() {
     if (request.readyState == 4) {
       if (request.status == 200) {
         alert("URL exists");
       } else if (request.status == 404) {
         alert("URL does not exist.");
       } else {
         alert("Status is: " + request.status);
       }
     }
   }

正直なところ、このコードにはほとんど価値がありません。なぜなら、サーバーは要求に応えて応答ヘッダーにコンテンツ長を入力するために、応答のコンテンツを明らかにする必要があるため、処理時間を節約することにはならないからです。その上、リスト 7 のようにエラーを単純に処理するのではなく、HEAD 要求を行って URL が存在するかどうかを確認する時間は、GET や POST を使って要求を行う場合とほとんど変わりません。とは言え、何が使用できるかを具体的に知っておくと有効な場合はときどきあります。独創的なアイデアが突然閃いて、HEAD 要求が必要になるという事態が起こらないとも限りません。

有意義な HEAD 要求

HEAD 要求が役に立つ一例は、コンテンツ長、あるいはコンテンツ・タイプまで確認する場合です。HEAD 要求でコンテンツ長やコンテンツ・タイプを確認することによって、要求を処理するために大量のデータが送信されるのか、サーバーが HTML やテキスト、または XML (JavaScript では、この 3 つのほうがバイナリー・データより遙かに簡単に処理することができます) の代わりにバイナリー・データを返そうとしているのかがわかります。

このような場合、該当するヘッダー名だけを XMLHttpRequest オブジェクトの getResponseHeader() メソッドに渡します。つまり、応答の長さを取得するには request.getResponseHeader("Content-Length") を呼び出し、コンテンツ・タイプを取得するには request.getResponseHeader("Content-Type") を使用します。

多くのアプリケーションでは、HEAD 要求を行っても何の機能も追加されないだけでなく、要求の処理に時間がかかるようになる場合さえあります (時間がかかるようになるのは、HEAD 要求で応答に関するデータを取得してから、続いて GET または POST 要求で実際に応答を取得するからです)。しかし、スクリプトやサーバー・サイドのコンポーネントに関して不安がある場合には、HEAD 要求を使用することで、応答データを扱うことも、その応答を送信するための帯域幅を必要とすることもなく基本的なデータを取得することができます。

まとめ

多くの Ajax および Web プログラマーにとって、この記事の内容はかなり高度に思えるかもしれません。HEAD 要求を行う価値は何なのでしょう。リダイレクトのステータス・コードを明示的にJavaScript で処理しなければならないのは実際にはどのような場合なのでしょう。これらの質問は的を射ています。そして単純なアプリケーションの場合、これらの高度な手法は役立ちそうもないというのが、その答えです

その一方、Web は単純なアプリケーションで許される場所ではなくなっています。ユーザーはより上達し、顧客は堅牢性と高度なエラー・レポートを要求し、マネージャーはアプリケーションのダウン時間が 1 パーセントに及んだというという理由で解雇されます。

そこでプログラマーの仕事となるのが単純なアプリケーションの枠を超えることですが、それには XMLHttpRequest を徹底的に理解する必要があります。

  • さまざまな Ready 状態を考慮することができれば、そしてブラウザーによって Ready 状態が異なることを理解できれば、アプリケーションを素早くデバッグできるようになります。また、Ready 状態と、ユーザーと顧客に対する要求のステータス・レポートに基づいて、独創的な機能を思い付く可能性もあります。
  • ステータス・コードを理解していれば、スクリプトのエラー、予期せぬ応答、そしてエッジ・ケースに対処するようにアプリケーションをセットアップすることができます。その結果、アプリケーションはすべてが正常に行われている場合に限らず、常に正常に機能することになります。
  • さらに HEAD 要求を行って URL の有無を確認し、ファイルがいつ変更されたのかを調べることができれば、ユーザーに有効なページが表示されること、そしてユーザーに表示されている情報が最新のものであることを確実にできるばかりか、(最も重要なこととして) 堅牢かつ万能のアプリケーションでユーザーを感心させることができます。

この記事の目的は、アプリケーションを派手に飾ることでも、フェードアウトする黄色のスポットライトでテキストを強調表示することでも、デスクトップの感覚に近づけることでもありません。このような機能は Ajax の長所でもありますが (今後の記事で取り上げる話題です)、あくまでも飾りでしかありません。Ajax を使用して、アプリケーションがエラーと問題を円滑に処理するための確固たる基礎を築くことができれば、ユーザーはサイトとアプリケーションに戻ってきます。その上で、今後の記事で取り上げる視覚的な手法を加えれば、顧客は感動、興奮、満足することでしょう (真面目な話、次回の記事を見逃さないでください)。


ダウンロード

内容ファイル名サイズ
Example code for this articlewa-ajaxintro3_ajax-xhr_adv.zip183KB

参考文献

学ぶために

  • Ajax の紹介: Web サイト構築の生産的手法としての Ajax とその機能の仕組みを理解する」(developerWorks、2005年12月): この連載の第 1 回では、Ajax コンポーネント技術が 1 つにまとまって機能する仕組みを説明し、XMLHttpRequest オブジェクトをはじめとする Ajax の中心的概念について検討しています。
  • JavaScript と Ajax を使用して行う非同期要求: Web 要求に XMLHttpRequest を使用する」(developerWorks、2006年1月): 連載第 2 回では、特定のブラウザーに依存しないように XMLHttpRequest インスタンスを作成する方法、要求を作成して送信し、サーバーからの応答を処理する方法を学んでください。
  • WebSphere Portal による Ajax の使用」(developerWorks、2006年6月) を読んで、ポータルのパフォーマンスを向上させ、よりクリーンなポータル・アプリケーション・アーキテクチャーを作成する方法、そして最も重要な点として、遙かに応答性に優れたポータルをユーザーに提供する方法を学んでください。
  • Ajax for Java developers: Build dynamic Java applications」(developerWorks、2005年9月): Java パースペクティブを使用してサーバー・サイドから Ajax を検討し、動的な Web アプリケーション・エクスペリエンスを実現する画期的な方法を紹介しています。
  • Ajax for Java developers: Java object serialization for Ajax」(developerWorks、2005年10月): Java オブジェクトをシリアライズする 5 つの手法を紹介し、ネットワークでオブジェクトを送信し、Ajax と対話する方法を説明しています。
  • Call SOAP Web services with Ajax, Part 1: Build the Web services client」(developerWorks、2005年10月): Ajax を既存の SOAP ベースの Web サービスに統合する方法について説明したこの非常に高度な記事で、Ajax 設計パターンを使用して Web ブラウザー・ベースの SOAP Web サービス・クライアントを実装する方法を調べてください。
  • Google GMail: これは、Web が機能する仕組みを変える Ajax アプリケーションの素晴らしい例です。Google ベースの Web 2.0 アプリケーションとしては、Google Maps もあります。
  • Flickr: Ajax を使用して Web ベースのアプリケーションにデスクトップの感覚をもたらしている優れた例です。
  • Ajax: A New Approach to Web Applications」: Ajax の名前を作り出した記事です。すべての Ajax 開発者の必読書です。
  • HTTP ステータス・コード: W3C による完全なリストを入手してください。
  • Head Rush Ajax ― 学びながら読む Ajax 入門』(Elisabeth Freeman、Eric Freeman、Brett McLaughlin 共著、オライリー・ジャパン、2006年2月): ここに記載された Head First スタイルのアイデアを吸収してください。
  • Java & XML 第 2 版』(Brett McLaughlin 著、オライリー・ジャパン、2001年8月): XHTML および XML 変換についての著者の考察を読んでください。
  • JavaScript』(David Flanagan 著、オライリー・ジャパン、2001年11月): JavaScript と動的 Web ページの操作について広範に説明しています。次回の改版では、Ajax についての 2 つの章が追加されます。
  • Head First HTML with CSS & XHTML』(Elizabeth Freeman、Eric Freeman 共著、O'Reilly Media, Inc.、2005年12月): XHTML と CSS、そしてこの 2 つを組み合わせる方法を学ぶには、この完成された資料を熟読してください。
  • developerWorks Web development ゾーン: Web 作成技術を磨いてください。
  • developerWorks technical events and Webcasts: 技術開発者を対象としたソフトウェア・ブリーフィングで最新情報を入手してください。

製品や技術を入手するために

  • IBM 製品の試用版: developerWorks から直接ダウンロードできるソフトウェアで、次の開発プロジェクトを構築してください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development, XML, Java technology
ArticleID=438148
ArticleTitle=Ajax をマスターする: 第 3 回 Ajax での高度な要求と応答
publish-date=02142006