クライアント側のスクリプトによって、現在ブラウザに表示されているのと同じ URL に対して帯域外呼び出しを行い、その要求に従ってサーバー側で必要な処置を実行して、結果をクライアントに返すという、クライアントコールバックの話です。
今は、帯域外呼び出しの機能を自動化している ASP.NET AJAX Extensions の UpdatePanel などが利用できるので、この記事に書いたクライアントコールバックを実装する機会はないかもしれませんが、せっかくいろいろ調べて分かったことがありますので、備忘録として書いておきます。
ASP.NET クライアントコールバックは、XMLHttpRequest の必要な呼び出しをラッピングして独自の JavaScript API を提供してくれますので、開発者が帯域外呼び出しを実行するための JavaScript コードを自力で書く必要はありません。
代わりに、Page.ClientScript オブジェクトの GetCallbackEventReference メソッドを利用して、帯域外呼び出しを実行するために使用する JavaScript コードを生成します。このコードは、クライアント側のイベントハンドラに設定して利用します。
サーバー側では、ICallbackEventHandler インターフェイスの RaiseCallbackEvent が呼び出されて必要なサーバー側での処置が実行され、次にブラウザへのレスポンスが生成されるときに GetCallbackResult メソッドが呼び出されます。
上の画像を表示したコードをアップしておきます。画像は Callback ボタンをクリックしてコールバック前にクライアント側で実行されるスクリプト(例では alert)が実行されたところです。
alert ダイアログの OK ボタンをクリックするとコールバックがかかり、表示されている「コールバック前」という文字列が「クライアントからサーバーに渡した文字列 + サーバー側で追加した文字列」に書き換えられます。
<%@ Page Language="C#" %>
<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
// ICallbackEventHandler インターフェイスを実装し、
// RaiseCallbackEvent メソッドを提供している必要がある。
private string returnValue;
// サーバー側でコールバックを実行するためにこのメソッド
// が呼び出される。
public void RaiseCallbackEvent(string arg)
{
// 引数 arg にはクライアントスクリプト CallServer
// 関数の第一引数に設定した文字列が渡される。
returnValue = arg + " + サーバー側で追加した文字列";
}
// コールバック結果をクライアントに返すメソッド。
public string GetCallbackResult()
{
return returnValue;
}
protected void Page_Load(object sender, EventArgs e)
{
ClientScriptManager cm = Page.ClientScript;
// サーバーに実際に要求を行うヘルパー関数。
// この例では cbReference は以下のようになる。
// WebForm_DoCallback('__Page',arg,
// ReceiveServerData,"",null,false);
string cbReference =
cm.GetCallbackEventReference(this, "arg",
"ReceiveServerData", "");
// サーバーに要求を行うクライアントスクリプト。
// arg は RaiseCallbackEventメソッドの引数に渡さ
// れる文字列。context はコールバック前にクライ
// アント側で実行されるスクリプト。
String callbackScript =
"function CallServer(arg, context) {"
+ cbReference + "; }";
// この例では以下のクライアントスクリプトが登録
// される。
// function CallServer(arg, context) {
// WebForm_DoCallback('__Page',arg,
// ReceiveServerData,"",null,false);
// }
cm.RegisterClientScriptBlock(this.GetType(),
"CallServer", callbackScript, true);
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Client Callback</title>
<script type="text/javascript">
//<![CDATA[
// クライアント コールバック関数
// コールバック イベントを処理したサーバーコード
// から呼び出され、結果を表す文字列を受け取る。
function ReceiveServerData(arg, context) {
// 2011/11/19 修正(下の追記参照)
var element = document.getElementById("Message");
if (typeof element.textContent != "undefined") {
element.textContent = arg;
} else {
element.innerText = arg;
}
}
var str = "クライアントからサーバーに渡した文字列";
// コールバック前に実行するクライアントスクリプト
function DoBeforeCallback() {
alert('コールバック前に実行された alert');
}
//]]>
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<h1>Client Callback</h1>
<input type="button" value="Callback"
onclick="CallServer(str, DoBeforeCallback());"/>
<br />
<span id="Message">コールバック前</span>
</div>
</form>
</body>
</html>
クライアントコールバックの問題は、クライアントとサーバー間の受け渡しが文字列に限定されていることです。文字列の内容は自由ですが、カスタムオブジェクトを受け渡したい場合はシリアル化と逆シリアル化は開発者が自力でコードを書かなければなりません。
というわけで、今後この方法を使うことはなさそうです。
------------ 2010/4/14 追記 ------------
この記事で紹介したコードを実際に動かして試せるよう 実験室 にアップしました。興味のある方は試してみてください。
------------ 2010/11/19 追記 ------------
Firefox では span id="Message" の「コールバック前」の文字列が書き換わらなかったので修正しました。修正したのは ReceiveServerData 関数の中身のコードです。
最初は、getElementById を使わず Message.innerText = arg; というようにしていたのが原因と思っていましたが、それだけが原因ではなくて、Firefox では innerText は使えないのでした。
Firefox では textContent を使うらしいです。ただし、これは IE では使えないのでtextContent を使えないブラウザがあるらしいので(IE では使えないという話がありますが IE9 は問題なかったです)、使えるか否かを判定して innerText と使い分ける必要があります。
加えて、コメントに「ここで定義せず GetCallbackEventReference メソッドの第 4 引数に設定することも可能。」と書いてあったのですが、これは間違っていたので削除しました。正しくは、第 4 引数はコールバック前に実行するクライアントスクリプトの文字列だったのです。(汗)