特集

枯れた新しいUI革命「Ajax」をASP.NETで活用する

山田 祥寛(http://www.wings.msn.to/
2005/08/24

Page1 Page2 Page3 Page4

Ajax+ASP.NETによる郵便番号検索(テキスト通信)

 さて、以下で紹介するのは、画面左のテキストボックスに郵便番号を入力したタイミングで、データベースのaddressテーブル(郵便番号情報テーブル)を検索し、対応する住所を右のテキストボックスにオート・コンプリートするASP.NETによるサンプル・アプリケーションだ。

Ajaxを利用した郵便番号による住所検索アプリケーション
画面左のテキストボックスに郵便番号を入力すると(上画面)、リアルタイムに右のテキストボックスに対応する住所をオート・コンプリートする(下画面)。

 本節のサンプルを動作させるには、あらかじめデータベース上に以下のようなaddressテーブルを作成しておく必要がある。

フィールド名 データ型 概要
postnum VARCHAR(7) 郵便番号(主キー)
prefecture VARCHAR(10) 県名
city VARCHAR(50) 市町村名
other VQRCHAR(50) 字、丁目、番地など
addressテーブルのフィールド・レイアウト
郵便番号情報については、日本郵政公社のサイトから提供されている郵便番号ダウンロードサービスを利用することで、CSV(カンマ区切りテキスト)形式のデータを入手できる(http://www.post.japanpost.jp/zipcode/

 なお、記事冒頭の処理フロー図に本節のサンプルを当てはめると、以下のようなイメージになる。それぞれのステップが、コードのどの部分に対応するのかを意識しながら、見ていくことにしよう。

郵便番号検索サンプル・アプリケーションの仕組み

■クライアント側スクリプトを記述する

 「Asynchronous JavaScript And Xml」というその名のとおり、Ajaxの中核となるのは、クライアント・ページにおける変更イベントの検知とサーバ側との通信、そして、結果データの反映を担うJavaScriptだ。郵便番号検索サービスにアクセスするクライアント・ページのコードは、以下のようになる。

 なお、本節では、サーバ側のコードとして、C#版とVB.NET版の2種類を用意している。誌面上は、VB.NET版のコードにアクセスするようにURLを指定しているが、C#版にアクセスしたい場合は、コード中の太字部分を適切なファイル名に変更してほしい。ちなみに、いずれの版を使用しても、得られる結果は変わらない。

<%@ Page ContentType="text/html" Language="VB" %>
<html>
<head>
<title>Ajaxによる郵便番号検索</title>

<script language="JavaScript">
<!--
// テキストボックス「postnum」の内容が変更されたタイミングで実行
function search() {

  // XMLHttpRequestオブジェクトの生成
  try {
    // IEの場合
    xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
  } catch(e) {
    // Firefoxなどそのほかのブラウザの場合
    xmlHttp = new XMLHttpRequest();
  }

  // XMLHttpRequestオブジェクトの状態が変更されたタイミング
  // で行うべき処理を定義
  xmlHttp.onreadystatechange = function() {
    // データ受信に成功した場合、
    // 受信データをテキストボックス「address」に反映
    if (xmlHttp.readyState == 4) {
      if (xmlHttp.status == 200) {
        var data = xmlHttp.responseText;
        document.fm.address.value = data;
      }
    }
  }

  // HTTP GETメソッドでpostnum_server_vb.aspxにデータを送信
  xmlHttp.open("GET",
    "postnum_server_vb.aspx?postnum="
    + document.fm.postnum.value.replace("-", ""), true);
  xmlHttp.send(null);
}
-->
</script>

</head>
<body>
<h1>郵便番号による住所検索</h1>
<form name="fm">
  郵便番号:
  <input type="text" name="postnum" size="10" maxlength="8"
    onchange="search()" />
  <input type="text" name="address" size="40" />
</form>
</body>
</html>
postnum_server_vb.aspx(postnum_server_cs.aspx)から取得した住所情報を反映するWebフォーム(postnum_client.aspx)

 便宜上、拡張子は「.aspx」としているが、内容はコードをご覧いただければ分かるように、ASP.NETのコードをまったく含まない単なるHTML+JavaScriptだ。このため拡張子は、「.html」でも構わない。以上のコードで注目すべき点は、以下の3点だ。

(1)クライアント側で発生したイベントを捕捉する

 クライアント側スクリプトでは、ページ上で発生したイベントをトリガーに処理を起動する「イベント駆動型」モデルとなる。対応するイベントは使用しているブラウザによって異なるが、Internet ExplorerやFirefoxで利用可能な主なイベントは以下のとおり。

イベント 発生するタイミング
onblur 要素からフォーカスが外れたとき
onchange 要素の内容が変更されたとき
onclick 要素をクリックしたとき
ondblclick 要素をダブルクリックしたとき
onfocus 要素にフォーカスが当たったとき
onkeydown キーを押したとき
onkeyup キーを離したとき
onload ページがロードされたとき
onmousedown マウスボタンを押したとき
onmousemove マウスが移動したとき
onmouseout マウスが要素から外れたとき
onmouseover マウスが要素に乗ったとき
onmouseup マウスボタンを離したとき
onreset リセットされたとき
onselect テキストが選択されたとき
onsubmit フォームがサブミットされたとき
onunload ページがアンロードされたとき
クライアント側スクリプトで利用可能な主なイベント

 ここでは、テキストボックス「postnum」が変更されたタイミング(onchangeイベント発生時)で、JavaScriptのsearch関数を呼び出し、サーバとの通信処理を開始している。

(2)サーバとの通信を行うのはXMLHttpRequestオブジェクトの役割

 サーバ側(本稿ではASP.NET)との非同期通信を管理するのは、XMLHttpRequestオブジェクトの役割だ。XMLHttpRequestオブジェクトは、多くのブラウザで古くから実装されているオブジェクトであるが、反面、ブラウザ独自の実装も多いので注意が必要だ。

 その最たるものがオブジェクトの生成方法である。ここでは、例外処理を利用して、まずtryブロックでXMLHTTPオブジェクト(IE用)の生成を試み、それに失敗した場合はcatchブロックでXMLHttpRequestオブジェクト(Firefox用)を生成するという方法を採っている。これによって、ブラウザに合わせたオブジェクトを生成できるというわけだ。

 XMLHttpRequestオブジェクトで利用可能なプロパティ/メソッドは以下のとおり(なお、以降は、XMLHTTPオブジェクトも含め「XMLHttpRequest」と表記する)。

分類 メンバ名 概要
プロパティ onreadystatechange[R/W] XMLHttpRequestオブジェクトの状態が変化したタイミングで呼び出されるイベント・ハンドラ
readyState[R] HTTP通信の状態を取得
戻り値 概要
UNINITIALIZED(0) 未初期化(openメソッドが呼び出されていない)
LOADING(1) ロード中(openメソッドは呼び出されたが、sendメソッドが呼び出されていない)
LOADED(2) ロード済み(sendメソッドは呼び出されたが、ステータスやヘッダはまだ利用できない)
INTERACTIVE(3) 一部データを取得済み(ヘッダは読み込み可能だが、本体取得はまだできない)
COMPLETED(4) 全データを取得済み
responseBody[R] レスポンスとして返される複数フォームの1つを取得
responseText[R] レスポンスをプレーン・テキストとして取得
responseXML[R] レスポンスをXMLDocumentオブジェクトとして取得
status[R] HTTPステータスコードを取得。200:OK、404:Not Foundなど
statusText[R] HTTPステータスの詳細メッセージを取得
メソッド abort() 現在行っているHTTP要求を中断
getAllResponseHeaders() 受信した全HTTP応答ヘッダを取得(sendメソッドが成功した後にのみ有効)
getResponseHeader(header) 指定されたHTTP応答ヘッダを取得(sendメソッドが成功した後にのみ有効)
open(method ,url [,async [,usr [,passwd]]]) 指定されたHTTPメソッド(GET|POST|PUT|PROPFIND)で指定URLにリクエストをオープン(asyncは非同期モードかどうか、usr、passwdは認証が必要な場合にのみ指定)
send(body) HTTP要求を送信(POSTメソッドで送信した場合には、bodyに要求ボディを指定可能)
setRequestHeader(header,value) HTTP要求時にヘッダを追加
XMLHttpRequestオブジェクトで利用可能なプロパティ/メソッド
プロパティ名に併記した[R/W][R]は、それぞれ「読み書き可能」「読み取り専用」を表す。

 サーバ側から返された戻り値を捕捉し、クライアント・ページに反映させるのは、onreadystatechangeイベント・ハンドラの役割だ。onreadystatechangeイベントは、XMLHttpRequestオブジェクトの状態(ステータス)が変更されたタイミングで発生するイベントである。これに設定したJavaScriptの関数(コールバック関数)は、そのタイミングで呼び出される(コールバックされる)ことになる。

 ここでは、XMLHttpRequestオブジェクトの全データが取得済み(readyStateプロパティがCOMPLETED)であり、かつ、HTTPステータスコード(statusプロパティ)が「200」(OK)である場合に、サーバ側から送られてきたデータを取得し、それを右側のテキストボックス「address」の値としてセットしているというわけだ。

 サーバ側からの戻り値を取得するメソッドは、XMLHttpRequestオブジェクトにいくつか用意されているが、プレーン・テキストとして取得するにはresponseTextプロパティを使えばよい。

 XMLHttpRequestオブジェクトは、もともとはXMLデータを授受するために用意されたオブジェクトであるが、なにも利用できるのはXMLデータばかりではない。本節のサンプルのようにプレーン・テキスト(HTMLを含んでも可)を扱うことも可能だ。本節サンプルのような住所情報ならば、プレーン・テキストとしてデータを取得した方が、クライアント側でDOM(Document Object Mode)による処理を行う必要もなく、コードがシンプルに記述できるはずだ。

 なお、XMLHttpRequestオブジェクトで扱える文字コードは「UTF-8」に限定されるので、注意すること。ほかの文字コードを利用している場合には、マルチバイト文字が文字化けする原因となる。ASP.NETではデフォルトの応答文字コードはUTF-8に設定されているので、特に構成を変更しない限りは問題ないはずだ。

[参考]ASP.NETの応答文字コード

 ASP.NETの応答文字コードの設定個所は、以下の3カ所である。

設定場所 設定項目 適用範囲
Webフォーム @PageディレクティブのResponseEncoding属性 該当ページのみ
web.config <GLOBALIZATION>要素のResponseEncoding属性 該当アプリケーション(またはフォルダ)配下のみ
machine.config <GLOBALIZATION>要素のResponseEncoding属性 マシン全体
ASP.NETにおける応答文字コードの設定場所

 通常は、machine.configにデフォルトで設定されている文字コード(UTF-8)がデフォルト値になっているはずだ。もしもmachine.config上に明示的に値が設定されていない場合、システム・デフォルトの文字コードが使用される。

 Ajaxアプリケーションで文字化けが発生した場合にはこれらを確認し、ASP.NETから正しくUTF-8で応答が返されているかどうかを確認していただきたい。

(3)サーバ側にデータを送信するのはsendメソッド

 コールバック関数の定義が終わったら、後はサーバに対してHTTPリクエストを発行するだけだ。リクエストを初期化するのは、XMLHttpRequestオブジェクトのopenメソッドの役割だ(リクエスト時に特別な要求ヘッダを設定したい場合には、setRequestHeaderメソッドを利用することも可能)。

 openメソッドには、第1パラメータからHTTPメソッド、URL、非同期モード指定、ユーザー名、パスワードの順でパラメータを指定できる(第3パラメータ以降は任意)。ただし、XMLHttpRequestオブジェクトでは、セキュリティ上の理由から、URLにページと異なるドメインをセットすることはできない。使用しているブラウザによっては、同一ドメインでもドメインを明示するだけで正しく処理できないものもあるので注意が必要だ。URLには必ずパスのみ(「http://」は不要)を指定するようにすること。

 リクエストの準備ができたら、sendメソッドでリクエストを送信する。sendメソッドのパラメータには、openメソッドの第1パラメータに「POST」を指定した場合にのみ、要求本体を指定することができる。繰り返しになるが、このHTTP要求の結果は、onreadystatechangeイベント・ハンドラで処理することができる。

■サーバ側のコードを記述する

 クライアント・ページの挙動を理解したところで、次にサーバ側のコードを見てみよう。今回の場合には、サーバ側のコードは非常にシンプルだ。

<%@ Page ContentType="text/plain" Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>

<script runat="Server">
void Page_Load(Object sender, EventArgs e) {

  SqlConnection db = new SqlConnection("Data Source=(local);User ID=sa;Password=sa;Persist Security Info=True;Initial

Catalog=dotnet");

  // クエリ情報「postnum」の値をキーに
  // addressテーブルから住所情報を取得
  SqlCommand comm = new SqlCommand("SELECT prefecture,city FROM address WHERE postnum=@postnum", db);
  comm.Parameters.Add("@postnum", Request.QueryString["postnum"]);
  db.Open();
  SqlDataReader reader = comm.ExecuteReader();

  // レコードが取得できた場合、「県名+市町村名」で結果を出力
  if(reader.Read()){
    Response.Write(reader.GetString(0) + reader.GetString(1));
  }
  db.Close();
}
</script>
郵便番号をキーに県名+市町村名を取得するWebフォーム(C#版:postnum_server_cs.aspx)
 
<%@ Page ContentType="text/plain" Language="VB" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>

<script runat="Server">
Sub Page_Load(sender As Object, e As EventArgs)

  Dim db As New SqlConnection("Data Source=(local);User ID=sa;Password=sa;Persist Security Info=True;Initial Catalog=dotnet")

  ' クエリ情報「postnum」の値をキーに
  ' addressテーブルから住所情報を取得
  Dim comm As New SqlCommand("SELECT prefecture,city FROM address WHERE postnum=@postnum", db)
  comm.Parameters.Add("@postnum", Request.QueryString("postnum"))
  db.Open()
  Dim reader As SqlDataReader = comm.ExecuteReader()

  ' レコードが取得できた場合、「県名+市町村名」で結果を出力
  If Reader.Read() Then
    Response.Write(reader.GetString(0) & reader.GetString(1))
  End If
  db.Close()
End Sub
</script>
郵便番号をキーに県名+市町村名を取得するWebフォーム(VB.NET版:postnum_server_vb.aspx)

 サーバ側で行っている内容は、なんということはない。クライアント・ページから受け取ったクエリ情報「postnum」をキーにaddressテーブルを検索し、取得した「県名+市町村名」をHttpResponseオブジェクトのWriteメソッドで出力するだけだ。

 本稿は、サーバ側をASP.NETで実装しているが、もちろん、JSP(JavaServer Pages)&サーブレットやPHP(PHP:Hypertext Preprocessor)など、ほかの技術を利用しても、ほとんど同じ要領で記述できる。

 なお、ここではクライアント側への応答をテキスト形式で出力しているので、@PageディレクティブのContentType属性は「text/plain」と明示しておこう。


 INDEX
  [特集]枯れた新しいUI革命「Ajax」をASP.NETで活用する
    1.Ajax技術の基本的な仕組みとアプリケーション例
  2.Ajax+ASP.NETによる郵便番号検索(テキスト通信)
    3.Ajax+ASP.NETによる郵便番号あいまい検索(XML通信)
    4.JavaScriptライブラリ活用で高度なAjax連携
 


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間