はじめに
私は15seconds.comの熱心な読者であり、フォームの妥当性検証に関する記事をこれまでに数多く読んできました。フォームの妥当性検証では、フィールドの内容や構造を調べるだけでなく、リアルタイムのデータルックアップも扱うべきです。本稿では、MicrosoftのXMLHTTPRequestコンポーネントを利用してフォームの妥当性検証を改良し、ユーザーにもっと快適にWebを利用してもらうための方法を説明します。
HTTPのプログラミング
Win32プラットフォーム上でHTTP呼び出しを行うには、以前からいくつかの方法がありました。Visual Basic開発者とC++開発者はWinInetライブラリを使用でき、Visual BasicプログラマはVisual Basic 6.0付属のInternet Controlを使用できました。しかし、ASP開発者がこの機能を使用するためにはもう少し手間がかかりました。ASPコードと洗練されたやり取りをするには、これを独自のコンポーネント内にラップする必要があったからです。「生の」スクリプティングでは、この作業は困難です。
たいていの人は、HTTPのことを「ブラウザがWebサーバーと通信するときに使うプロトコル」と考えています。しかし、HTTPはただのプロトコルであり、そのままで強力な機能を備えているということに注意してください。HTTPは、どんなアプリケーションが別のアプリケーションやコンポーネントと通信するときにも使用できるプロトコルであり、使用するアプリケーションはブラウザやWebサーバーに限りません。我々Web開発者は、HTTPプロトコルを使用することの利点をよく知っています。たとえば、ファイアウォールを越えて利用できること、インターネット標準に従っていることなどです。
Microsoftが自社のXMLツールキットにXMLHTTPRequest
コンポーネントを含めたおかげで、標準のHTTPプロトコルを使用してXML文書をインターネット経由でやり取りできるようになりました。どの資料でもHTTP経由でのXMLのやり取りを例に挙げていますが、このコンポーネントのいいところは、XMLをまったく扱わなくてもその能力を活用できるという点です。わずか数行のコードを書くだけで、HTTP呼び出しを行い、結果を得ることができるのです。これにより、XMLHTTPRequest
コンポーネントはすべてのWeb開発者、特にASP開発者にとって非常に強力なツールになっています。
XMLHTTPRequestとXMLHTTP
XMLHTTPRequest
コンポーネントは、Internet Explorer 5.0以上に付属しているMSXMLの一部です。このことは、このコンポーネントをさらに魅力的なツールにしています。XMLHTTPRequest
内のコアオブジェクトはXMLHTTP
オブジェクトです。XMLHTTP
オブジェクトはさまざまなバージョンのMicrosoft MSXMLパッケージに含まれているため、さまざまなバージョンのXMLHTTP
オブジェクトがあります。最新バージョンの概要とインストールの問題については、Microsoftのサポート技術情報の記事Q278969を参照してください(編注:現在リンク切れ)。
XMLHTTP
オブジェクトはWebブラウザのすべての通信機能を提供し、いくつかのメソッドとプロパティを備えています。このオブジェクトを使用するには、XMLHTTP
オブジェクトを作成し、アクセス先のURLと呼び出しの種類(GET
、POST
)を指定してopen
メソッドを呼び出し、それからsend
メソッドを呼び出します。XMLHTTPオブジェクトは基本的にブラウザに似た動作をし、URLからデータを取得し、それをresponseText
プロパティ内で使用できるようにします。また、呼び出しを同期化または非同期化することができます。非同期呼び出しに関してはコールバック機能も使用できます。さらに、非常に便利で非常にシンプルです。
XMLHTTPを使用してリアルタイムのデータ妥当性検証を実行する
たとえば、あなたが自分の開発しているWebサイトでユーザー登録情報を収集したいと考えているとします。フィールドの1つは[User ID](ユーザーID)です。当然ながら、ユーザーIDはサイト内で一意でなければなりません。そこで、ユーザー登録時には、入力されたユーザーIDがサイト内にまだ存在しないIDであることを確認する必要があります。入力されたユーザーIDが既に存在する場合は、その旨をユーザーに通知し、別のIDを入力するよう求めます。
このような要件を満たすための一般的な方法は、フォームのポスト時にルックアップ処理を行うことです。しかし、ユーザーの立場からすると、これは最善の方法とは言えません。フォームを発行してみないとどの値を入力し忘れているかがわからない、という状況は非常にイライラします。ページのポスト時にルックアップをするという方法のもう1つの欠点は、「○○を訂正してください」メッセージを表示する場合に、再びサーバーや画像、スクリプトなどを通らなければならないという点です。
この問題に対処するための理想的な方法は、ユーザーがユーザーIDを入力したときに、それが一意かどうかをその場で判定して通知することです。デスクトップアプリケーションであれば、話は簡単です。テキストボックスの「フォーカス喪失」イベントにコードを記述すればよいのです。JavascriptとXMLHTTP
オブジェクトでも、同様のインタラクションを実現することができます。
それでは、具体的な例を見てみましょう。ある架空企業のユーザー登録のシナリオを考えてみます。
[User ID]フィールドのHTMLコードは次のとおりです。
<input type="text" name="UserID" onblur="validateuserid(this.value);">
このonblurイベントでは、ユーザーが[User ID]テキストボックスからフォーカスを移動したときに妥当性検証ルーチンを呼び出します(注:フォーカスドリブン式の妥当性検証を好まない人は、このルーチンを[Register]ボタンのonclickイベントから呼び出してもかまいません)。
では、妥当性検証を行うJavaScriptコードを見てみましょう。
<SCRIPT TYPE="text/javascript" LANGUAGE="JavaScript"> function validateuserid(suserid) { document.body.style.cursor='wait'; // Create an instance of the XML HTTP Request object var oXMLHTTP = new ActiveXObject( "Microsoft.XMLHTTP" ); // Prepare the XMLHTTP object for a HTTP POST to our validation ASP page var sURL = "http://mysite/mypath/validateuser.asp?username=" + suserid oXMLHTTP.open( "POST", sURL, false ); // Execute the request oXMLHTTP.send(); if (oXMLHTTP.responseText == "exists") alert("Sorry - the User ID " + suserid + " already exists."); document.body.style.cursor='auto'; } </SCRIPT>
このスクリプトで何をしているかをブロックごとに見ていきます。
document.body.style.cursor='wait';
このスクリプトではこれからHTTP呼び出しを行いますが、その処理に数秒かかることがあるので、マウスポインタを砂時計に変更して、ユーザーにフィードバックを与えます。ユーザー側の利便性を考えると、マウスポインタの形状を変更すること以外にも、もっといろいろできることがあります。JavascriptとDHTMLを使用すれば、必要なことは何でもできます。実際、フィードバックを返すにはもっと洗練された方法がありますが、それは後で紹介します。
// Create an instance of the XML HTTP Request object var oXMLHTTP = new ActiveXObject( "Microsoft.XMLHTTP" );
ここではXMLHTTP
オブジェクトのインスタンスを作成しています。
// Prepare the XMLHTTP object for a HTTP POST to our validation ASP page var sURL = "http://mysite/mypath/validateuser.asp?userid=" + suserid oXMLHTTP.open( "GET", sURL, false );
このopen
メソッドでは、これから行う要求の準備をしています。1つ目のパラメータは、使用する接続を定義しています。この例では、GET
メソッドを使用します。open
メソッドは、この他にPUT
やPOST
などのメソッドもサポートしています(POST
は非常に強力なメソッドで、その名前が示すとおり、フォームをプログラム的にポストすることもできます)。2つ目のパラメータは、接続先のURLを示しています。このスクリプトはクライアント上で実行するので、絶対URLにする必要があります。これで、ユーザーIDが存在するかどうかを判別するカスタムASPページ(詳しくは後述)への呼び出しができます。
3つ目のパラメータでは、この呼び出しを非同期的に行うかどうかをオブジェクトに指示します。この例ではfalse
に設定しており、呼び出しが返るまで実行を待機するよう指定しています。
さらに、username
とpassword
という省略可能なパラメータがあります。これらのパラメータを使用すると、ユーザー名/パスワードの認証情報を要求するサイトに接続できます。
これで要求の準備ができたので、後はこれを送信するだけです。
// Execute the request
objXMLReq.send();
この時点で、オブジェクトが実際に要求をインターネットに送出し、ターゲットページのコンテンツを取得します。ここで、コンテンツをチェックして検証結果を判別します。
if (objXMLReq.responseText == "exists") alert("Sorry - the User ID " + suserid + " already exists.");
ユーザーIDが既に別のユーザーに割り当てられている場合は、ターゲットページから「exists」という単語が返されます。これは、そのユーザーIDが既にデータベース内にあることを意味します(ここで返される値は自由に定義してかまいません。たとえばASPページは数値を返すこともできます)。最後に、マウスポインタを元に戻します。
document.body.style.cursor='auto';
次に、前述のスクリプトがルックアップ時に参照するASPページのコードを見てみましょう。
<% Dim objConn, objRS, sUserID 'Capture the username that we need to lookup, making sure its prepared for 'our forthcoming SQL query sUserID = Replace(Trim(Request.QueryString("userid")),"'","") 'Fire up ADO - ask the database whether or not the user idexists Set objConn = Server.CreateObject("ADODB.Connection") objConn.Open CONNECTIONSTRING sSQL = "select userid from usertable where userid = '" + sUserID + "'" Set objRS = objConn.Execute(sSQL) If Not objRS.EOF Then Response.Write "exists" 'Clean up objRS.Close objConn.Close Set objRS = Nothing Set objConn = Nothing %>
これは、渡されたユーザーIDがデータベース内に存在する場合に単語「exists」を書き出す簡単なASPコードです。このページを単独で実行した場合、指定のユーザーIDが存在しないときはブラウザに何も表示されません。このページは、ユーザーIDが存在する場合に単語「exists」を返すだけだからです。この動作を利用して、検証用ASPページをクライアントスクリプトに統合する前に、ASPページをテストすることができます。
タイムアウトの処理
この方法でまず疑問に思うのは、「リモートサイトがダウンしている場合にはどうするか。タイムアウトやエラーはどう処理すればよいか」という問題でしょう。send
メソッドのエラーをトラップするという方法もありますが、この方法では、呼び出しを行っている間はユーザーを待機させることになります。
ここで、XMLHTTP
オブジェクトのopen
メソッドの3つ目のパラメータが役に立ちます。先ほどの例では、このパラメータをfalse
に設定しました。これをtrue
に設定すると呼び出しが即座に行われ、制御は次の行に進みます。したがって、呼び出しが成功した場合でも失敗した場合でも、呼び出しの完了についてポーリングするか通知を受け取る必要があります。幸い、XMLHTTP
オブジェクトは、HTTP要求の処理の進み具合を特定の関数に通知することができます。次に示すのは、前述のクライアントサイドスクリプトに少々手を加えて、タイムアウトに対処できるようにしたものです。
<!-define a div that will be our pseudo progress indicator --> <div id="divProgress">Please wait</div> <SCRIPT TYPE="text/javascript" LANGUAGE="JavaScript"> // Create an instance of the XML HTTP Request object var oXMLHTTP = new ActiveXObject( "Microsoft.XMLHTTP" ); var userid; function validateuserid(suserid) { // Prepare the XMLHTTP object for a HTTP POST to our validation ASP page userid = suserid; var sURL = " http://mysite/mypath/validateuser.asp?userid=" + userid; oXMLHTTP.open( "POST", sURL, false ); // Define an event handler for processing oXMLHTTP.onreadystatechange = managestatechange; // Execute the request try { oXMLHTTP.send(); } catch (e) { alert("Could not validate your User ID at this time."); document.all.item("FirstName").focus; } } function managestatechange() { switch (oXMLHTTP.readyState) { case 2, 3: // Display a progress indicator of some kind, informing the // user that you are checking to see if the UserID exists document.all.item("divProgress").style.display = "block"; break; case 4: if (oXMLHTTP.responseText == "exists") alert("Sorry - the User ID " + userid + " already exists."); document.all.item("divProgress").style.display = "none"; break; } } // Hide the progress indicator for now document.all.item("divProgress").style.display = "none"; </script>
このスクリプトで重要なのは、イベントハンドラを設定する部分のコードです。
// Define an event handler for processing
oXMLHTTP.onreadystatechange = managestatechange;
この行では、HTTP要求の状態が変化したときにmanagestatechange
関数を呼び出すようXMLHTTP
オブジェクトに指示しています。この機能では、オブジェクトが作成されてから要求が完了するまでの間の状態変化を監視します。次の5種類の状態を監視できます。
ステータスコード | 説明 |
(0)UNINITIALIZED | オブジェクトが作成されましたが、初期化されていません(まだopen メソッドが呼び出されていません)。 |
(1)LOADING | オブジェクトが作成されましたが、まだsend メソッドが呼び出されていません。 |
(2)LOADED | send メソッドが呼び出され、状態とヘッダーが使用可能になりましたが、応答がまだ使用可能になっていません。 |
(3)INTERACTIVE | 一部のデータを受信しました。responseBodyとresponseTextを呼び出して、現時点の部分的な結果を取得できます。 |
(4)COMPLETED | すべてのデータを受信し、responseBodyとresponseTextで完全なデータを利用できます。 |
- MSDNより
修正後のスクリプトでは、堅牢性を高めるためにエラー処理コードも追加しています。リモートサイトがダウンしている場合や、ページが存在しない場合も考えられるからです。
// Execute the request try { oXMLHTTP.send(); } catch (e) { alert("Could not validate your User ID at this time."); document.all.item("FirstName").focus; }
このコールバック関数内のコードが終了すると、制御は呼び出し側ルーチンに戻ります。問題が発生した場合は、問題に対処します。ここでフォーカスを別のフィールドに設定していることに注意してください。これは重要です。そうしないと、ユーザーが[User ID]フィールドから移動できなくなるからです。
余談になりますが、ここでServerXMLHTTP
オブジェクトという別のコンポーネントを紹介しておきます。XMLHTTP
とServerXMLHTTP
の違いについては、Microsoftサポート技術情報の記事Q290761を参照してください。ServerXMLHTTPは、その名のとおりサーバー側からのHTTP接続に適しており、もう少し洗練された方法でタイムアウトを処理できます。もっと大規模で高度な処理をしたいときには、このコンポーネントが役に立ちます。本稿のテーマはクライアントサイドの妥当性検証だったので、今回は、クライアントサイドコンポーネント用に設計、最適化されたXMLHTTP
オブジェクトにスポットを当てました。
本稿を通じて、MicrosoftのXMLHTTPRequest
オブジェクトがいかに強力なものかを知っていただければ幸いです。その他の例についてはMSDN Webサイトを参照してください。