CodeZine(コードジン)

特集ページ一覧

XMLHTTPRequestを利用したクライアント側の妥当性検証

JavascriptとXMLHTTPによるリアルタイムデータチェック

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2005/05/19 00:00

本稿では、ユーザーにもっと快適にWebを利用してもらうために、MicrosoftのXMLHTTPRequestコンポーネントを利用して、リアルタイムにデータルックアップを行うフォームの妥当性検証の方法を解説します。

はじめに

 私は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と呼び出しの種類(GETPOST)を指定して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メソッドは、この他にPUTPOSTなどのメソッドもサポートしています(POSTは非常に強力なメソッドで、その名前が示すとおり、フォームをプログラム的にポストすることもできます)。2つ目のパラメータは、接続先のURLを示しています。このスクリプトはクライアント上で実行するので、絶対URLにする必要があります。これで、ユーザーIDが存在するかどうかを判別するカスタムASPページ(詳しくは後述)への呼び出しができます。

 3つ目のパラメータでは、この呼び出しを非同期的に行うかどうかをオブジェクトに指示します。この例ではfalseに設定しており、呼び出しが返るまで実行を待機するよう指定しています。

 さらに、usernamepasswordという省略可能なパラメータがあります。これらのパラメータを使用すると、ユーザー名/パスワードの認証情報を要求するサイトに接続できます。

 これで要求の準備ができたので、後はこれを送信するだけです。

// 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オブジェクトという別のコンポーネントを紹介しておきます。XMLHTTPServerXMLHTTPの違いについては、Microsoftサポート技術情報の記事Q290761を参照してください。ServerXMLHTTPは、その名のとおりサーバー側からのHTTP接続に適しており、もう少し洗練された方法でタイムアウトを処理できます。もっと大規模で高度な処理をしたいときには、このコンポーネントが役に立ちます。本稿のテーマはクライアントサイドの妥当性検証だったので、今回は、クライアントサイドコンポーネント用に設計、最適化されたXMLHTTPオブジェクトにスポットを当てました。

 本稿を通じて、MicrosoftのXMLHTTPRequestオブジェクトがいかに強力なものかを知っていただければ幸いです。その他の例についてはMSDN Webサイトを参照してください。



  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • japan.internet.com(ジャパンインターネットコム)

    japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.com や EarthWeb.c...

  • Jonathan Zufi(Jonathan Zufi)

    UDU World社(http://www.uduworld.com)の創立者。同社は独自のActiveInbox技術に基づくソリューションを販売しているソフトウェア会社。ActiveInboxとは、電子メールおよびモバイルテキストメッセージングによる情報アクセスを実現するためのバックエンドの情報/...

バックナンバー

連載:japan.internet.com翻訳記事

もっと読む

All contents copyright © 2005-2020 Shoeisha Co., Ltd. All rights reserved. ver.1.5