執筆はMicrosoft デベロッパーエバンジェリストの松崎剛さんです。
ASP.NET AJAXを理解する - @IT
「.NET開発者への道 ~ Web開発編」を2月5日(月)に開始
これに伴って、ASP.NET Developer Centerがリニューアルされました。
ASP.NET Developer Center
さらに、MSDNフォーラムにASP.NETのフォーラムが開設されました。
ASP.NET - MSDNフォーラム
[ 情報元 ]
Hiroyasu Kitagawa's Blog : .NET開発者への道 ~ Web開発編
「.NET開発者への道~Web開発編」が本日からスタート。
Download details: ASP.NET 2.0 AJAX Extensions 1.0 Source Code
[ 情報元 ]
aspxの日記(ASP++ブログ) by Moo - ASP.NET Ajax Server/Client Source Codeが公開になる!?
Download details: ASP.NET AJAX 1.0
ASP.NET 2.0 AJAX Extensions と Microsoft AJAX Library が含まれます。
Visual Studio 2005へのテンプレート追加なども行われます。
AJAX Control Toolkit
CodePlexで公開されている、拡張コントロールツールキットです。多数の拡張コントロールも含まれています
拡張コントロール開発用のプロジェクトテンプレート、拡張コントロールを用いたAjax Webアプリケーション開発用のテンプレートが利用できるようになります。
AJAX : The Official Microsoft ASP.NET AJAX Site
ASP.NET AJAX の Microsoft公式サイトです。
[ 情報元 ]
ASP.NET AJAX 1.0のリリースとControl Toolkitの充実
Ajaxについての簡単な説明から始まり、Ajaxの問題点、ASP.NET AJAXの開発目標、構成、シナリオ等について聴かせて頂き、デモンストレーションも披露して頂きました。
[ 色々と驚いた ]
今回、ASP.NET AJAXの紹介が主な内容だろうと思っていたので、あえて事前学習のようなことを全く行わずにセッションに参加しました ( いや、ちょっとは知ってたけどさ ) 。おかげで、かなり楽しめました。以下に、今回驚いたり感動した点をいくつか挙げてみます。
・VSUGって「ぶいさぐ」って読むんだ!?
すみません、知らなかったです。「ぶいえすゆーじー」だと思ってました。。。
・サーバー中心の開発モデルで、C/S間でやりとりされるデータ
サーバー中心の開発モデルというのは、「ASP.NET 2.0 AJAX Extensions」をメインに利用した開発モデルで、コーディングのほとんどがC#やVBなどのサーバーサイドコードとなります。場合によってはJavaScriptノンコーディングで行けるそうです。
サーバー中心の開発モデルでは、クライアントからサーバーへの非同期通信時に渡されるデータは、従来のポストバックと同等のデータとなるそうです。そして、サーバーからのレスポンスで渡されるデータは、UIに関するデータと動作の更新に関するJavaScriptコードとなるそうです。
これはAjax的にはオーバーヘッドと言えますね。現に、クライアント中心の開発モデル ( 「Microsoft AJAX Library」をメインに利用した開発モデル ) では、クライアントとサーバーの間で非同期にやりとりされるのは、UIや動作に関する情報を含まない純粋なデータのみとなるそうです ( ユーザーの実装方法にもよりそうだけど ) 。
でもまぁ、きちんとその辺を理解して設計・コーディングすれば、恐らくさほど問題にならないのではないでしょうか ( 少なくとも、従来の単なるポストバック方式よりパフォーマンスが落ちるということはないのでしょう、たぶん ) 。
・ASP.NET 2.0 AJAX Extensionsは、従来のASP.NET 2.0のコントロールと連携して動作する
これ、感動しました。従来のASP.NET 2.0のコントロール ( TextBoxコントロールやLabelコントロールなど ) をそのまま使えるんです。コントロールをUpdatePanelというコントロールにD&DするだけでAjax化するんです。
トリガーとなるコントロール ( ボタン等 ) は、D&DせずにUpdatePanelのTriggerプロパティで指定してやることもできるそうです。
ただ、全てのコントロールに対応しているわけではないので注意が必要です。
[ 質問タイム ]
3つほど質問が挙ってました。 ( 3つ目の質問は私です^^; )
特に興味深かったのは、2つ目の「例外ハンドリングに関して」の質問でした。ただ、この質問は後日VSUGフォーラムで回答ということになったので、楽しみにしてます。
[ 感想 ]
セッションに設けられた時間が1時間ちょいと短かったのが残念ですが、とても有益でした。
個人的には、JavaScript好きなので「Microsoft AJAX Library」も興味深々です。今回のセッションでは触れられませんでしたが、クラス, インターフェイス, 継承なども、Microsoft AJAX Libraryで考慮されているようです。
ただ実際の業務では、手軽にAjax化を実現できる「ASP.NET 2.0 AJAX Extension」の方が多用されそうですね。 ( もちろん、Microsoft AJAX Libraryでなきゃ実現できないこともあるし、ちょっと凝った事をやるにはMicrosoft AJAX Libraryが重要となるでしょう。 )
どちらにせよ、Ajaxによって画面のちらつきが取り除かれるし、従来のポストバック方式では実現できなかった機能 ( 例えばデモでも披露していた、テキスト入力補完機能など ) も実現できるようになるわけで、ASP.NET AJAXにはかなり期待してます。
JSONとはJavaScript Object Notationの略で、JavaScriptとの親和性が非常に高いデータ交換フォーマットである。
詳しくは↓
The application/json Media Type for JavaScript Object Notation (JSON)
Introducing JSON (→日本語翻訳ページ)
JSON in JavaScript (→日本語翻訳ページ)
JSONは、前回の記事で書いたオブジェクトの表記方法を応用している。
種を明かせば、例のオブジェクト表記方法を文字列化してデータのやり取りを行い、JavaScriptのeval関数でJavaScriptコードに変換(=オブジェクトに変換)するという仕組み。eval関数にJSONを渡す際は、JSONを文字列"()”で囲む必要がある。
ただ、気をつけなければいけない点がいくつか。
- プロパティ名(リンク先に倣えば"名前")は、文字列として記述すること。
- オブジェクト(リンク先に倣えば”値”)には、数値,Boolean値,文字列,null参照、またはこれらのみで構成される配列,オブジェクトのみ許可されている。当然だけど関数はダメ。
eval関数の性質上、これらを無視してもオブジェクトに変換することはできるが、極端な話それはJSONとは言えない。
ちなみに、JSONファイルの拡張子は.jsonである。
JSON形式のファイルを記述する例を以下に記述する。
Hoge.json{
"piyo": "test",
"hogera":
{
"foo": true,
"bar": [10, 20, 30, 40, 50]
}
}
Hoge.jsonをJavaScriptコードで扱う例を以下に記述する。
Hoge.jsonを扱う例function jsonControlSample(json)
{
var hoge = eval("(" + json + ")");
var message = "";
message += hoge.piyo + "¥r¥n";
message += hoge.hogera.foo + "¥r¥n";
for (var i = 0; i < hoge.hogera.bar.length; i++)
{
message += hoge.hogera.bar[i] + "¥r¥n";
}
alert(message);
}
なお、eval関数は文字列をJavaScriptのコードに変換(して実行)する関数であるため、受け取ったJSONの中身が実は危険なJavaScriptコードを文字列化したものであったりしても、そのままコードが実行されてしまう。冒頭のリンク先にも書いてある通り、eval関数を用いるより、json.jsのJSON parserを使用した方が安全である。json.jsには、JavaScriptのオブジェクトをJSONに変換するJSON stringifierも含まれている。
しかし、Webページに実装させてしまうと他のページで使い回しできない。汎用性を考えるのなら、Webユーザーコントロールに実装させる。また、Webユーザーコントロールにはロジックのみを実装し、HTML要素は配置しない。(ロジックのみにすることで、使い方が縛られず、色々なコントロールと組み合わせられる。)
以下に、クライアント コールバックのロジックを汎用的に提供するWebユーザーコントロールの作成方法を記述する。
[ サーバーサイドコード ]
まず、以下の5つのフィールドを用意する。
- 処理を開始するクライアント関数の名称フィールド
- 処理結果を実際に受け取るクライアント関数の名称フィールド
- 処理結果を受け取るクライアント関数(利用側が用意する関数)の名称フィールド
- 例外発生時に例外メッセージを受け取るクライアント関数(利用側が用意する関数)の名称フィールド
- 処理結果フィールド
フィールド1は、文字列"Begin"とthis.UniqueIDを結合させた文字列となる。フィールド2は文字列”End”とthis.UniqueIDを結合させた文字列となる。フィールド1とフィールド2が示すクライアント関数は、このコントロールのascxファイルに定義する。
フィールド3とフィールド4が示すクライアント関数は、初期値がnullで、このコントロールを利用する側が独自に用意することになる。
次に、以下の4つのプロパティを用意する。
- フィールド1の読み取り専用プロパティ
- フィールド2の読み取り専用プロパティ(可視性はプロテクト)
- フィールド3のプロパティ
- フィールド4のプロパティ
プロパティ1とプロパティ2は読み取り専用である。プロパティ2は可視性をプロテクトにすること。
プロパティ3とプロパティ4は、フィールドの説明にも書いたように、このコントロールを利用する側が値を設定することとなる。
次に、以下のイベントハンドラを用意する。
- Initイベントのハンドラ
Initイベントのハンドラでは、フィールド1とフィールド2の初期化を行う。Loadイベントではないので注意すること。
最後にIClientCallbackEventHandlerインターフェイスの各メソッドを実装する。
以下に、コード例を記述する。
AsyncAcquireHogeControl.ascx.cs // クライアント コールバックでHogeを取得するためのコントロール。
public partial class AsyncAcquireHogeControl : System.Web.UI.UserControl, ICallbackEventHandler
{
// 処理結果。
private string result = null;
// このコントロールに用意されている、処理を開始するクライアントスクリプト関数の名称の名称。
private string clientBeginFunction;
// このコントロールに用意されている、処理結果を受け取るクライアントスクリプト関数の名称。
private string clientEndFunction;
// 処理結果を受け取るクライアントスクリプト関数の名称。
private string clientReceptionFunction = null;
// 例外発生時に呼び出されるクライアントスクリプト関数の名称。
private string clientErrorFunction = null;
// このコントロールに用意されている、処理を開始するクライアントスクリプト関数の名称を取得する。
// このクライアントスクリプト関数は、一つの文字列型の引数を受け取る。引数には取得したいHogeの名称を指定する。
public string ClientBeginFunction
{
get
{
return this.clientBeginFunction;
}
}
// このコントロールに用意されている、処理結果を受け取るクライアントスクリプト関数の名称を取得する。
protected string ClientEndFunction
{
get
{
return this.clientEndFunction;
}
}
// 処理結果を受け取るクライアントスクリプト関数の名称を取得または設定する。
// このクライアントスクリプト関数には、一つのHoge型の引数を用意すること。引数には取得したHogeが渡される。
public string ClientReceptionFunction
{
get
{
return clientReceptionFunction;
}
set
{
clientReceptionFunction = value;
}
}
// 例外発生時に呼び出されるクライアントスクリプト関数の名称を取得または設定する。
// このクライアントスクリプト関数には、一つの文字列型の引数を用意すること。引数には例外メッセージが渡される。
public string ClientErrorFunction
{
get
{
return clientErrorFunction;
}
set
{
clientErrorFunction = value;
}
}
// 初期化イベントのハンドラ。
protected void Page_Init(object sender, EventArgs e)
{
this.clientBeginFunction = "Begin" + this.UniqueID;
this.clientEndFunction = "End" + this.UniqueID;
}
// コールバックイベントの結果を返す。
public string GetCallbackResult()
{
return this.result;
}
// コールバックイベントを処理する。
public void RaiseCallbackEvent(string eventArgument)
{
// eventArgument引数に対応するHogeを取得し、
// XMLデータに変換後、resultフィールドに格納する。
}
}
[ クライアントサイドコード ]
クライアントサイドには、以下の2つの関数を用意する。
- 処理を開始する関数
- 処理結果を受け取る関数
関数1は、プロパティ1の値("Begin" + this.UniqueID)を関数名とする。この関数は、this.Page.ClientScript.GetCallbackEventReferenceメソッドの戻り値で得られるスクリプト関数を実行する。GetCallbackEventReferenceメソッドのclientErrorCallback引数にはプロパティ4の値を渡す。
関数2は、プロパティ2の値("End" + this.UniqueID)を関数名とする。この関数は、引数でクライアント コールバックの処理結果をサーバーから取得し、プロパティ3に指定されているクライアント関数を呼び出す。この時、必要なら処理結果をわかりやすい形式に変換(XMLデータを、JavaScriptで定義したラッパークラスに変換する等)してからプロパティ3のクライアント関数に渡す。
以下に、コード例を記述する。
AsyncAcquireHogeControl.ascx <%@ Control Language="C#" AutoEventWireup="true" CodeFile="AsyncAcquireHogeControl.ascx.cs" Inherits="AsyncAcquireHogeControl" %>
<script type="text/javascript" src="Hoge.js"></script>
<script type="text/javascript">
function <%= this.ClientBeginFunction %>(arg)
{
<%= this.Page.ClientScript.GetCallbackEventReference(this, "arg", this.ClientEndFunction, "null", this.ClientErrorFunction, true) %>
}
function <%= this.ClientEndFunction %>(result, context)
{
<% if (!string.IsNullOrEmpty(this.ClientReceptionFunction)){ %>
var hoge = new Hoge();
hoge.readXml(result);
<%= this.ClientReceptionFunction %>(hoge);
<% } %>
}
</script>
作成したWebユーザーコントロールを利用するには、以下のような使い方をする。
Default.aspx<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ register src="AsyncAcquireHogeControl.ascx" tagname="AcquireHoge" tagprefix="async" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>クライアント コールバックのサンプル</title>
<async:AcquireHoge id="acquireHoge1" runat="server" ClientReceptionfunction="reception1" ClientErrorFunction="error" />
<async:AcquireHoge id="acquireHoge2" runat="server" ClientReceptionfunction="reception2" ClientErrorFunction="error" />
<script type="text/javascript">
function reception1(result)
{
document.getElementById("textBox1").value = result.name;
}
function reception2(result)
{
alert(result.name);
}
function error(message)
{
alert(message);
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<button id="button1" onclick="<%= this.acquireHoge1.ClientBeginFunction %>('Foo');" >
テキストボックスに表示
</button>
<input id="textBox1" type="text" />
<br />
<button id="button2" onclick="BeginacquireHoge2('Foo');" >
メッセージボックスに表示
</button>
</div>
</form>
</body>
</html>
button2のonclickで行っているように、クライアント コールバックの開始関数は、ClientBeginFunctionで取得する代わりに、コントロールのIDの先頭に"Begin"を結合した文字列を直接記述しても良い。
サーバー側(ASP.NET)では、型指定されたデータセットを用意して、DataSet.GetXml()メソッドでXMLデータを生成するのが手っ取り早い。XML DOMを利用する方法もある。
クライアント側(JavaScript)では、XMLデータのラッパークラスを用意する。XMLデータの解析にはXML DOMを使用する。
ASP.NET Web ページでポストバックせずにクライアント コールバックを実装する
http://msdn2.microsoft.com/ja-jp/library/ms178208.aspx
ページをリロードせずにサーバーとやり取りする方法
http://www.microsoft.com/japan/msdn/asp.net/tips/ClientCallback/
クライアント・コールバックで複数の値をクライアントへ渡す方法
http://www.microsoft.com/japan/msdn/asp.net/tips/ClientCallback2/