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も含まれている。
オブジェクトは通常どおり、数値やBoolean値ならそのまま記述、文字列ならダブルクォーテーションで囲んで記述、配列なら[]の中にカンマ区切りでオブジェクトを記述、関数ならfunction(){}の形式で記述する。{[プロパティ名]: [オブジェクト], ...}の形式のオブジェクトも当然含ませることができる。
以下に例を記述。
オブジェクトを直接記述する例var obj1 =
{
p1: 1,
p2: "1",
p3:
[
{
p1: "a1",
p2: "a2"
},
{
p1: "b1",
p2: "b2"
}
],
m1:
function()
{
alert(this.p1 + this.p2);
},
m2:
function()
{
var message = "";
for (var i = 0; i < this.p3.length; i++)
{
message += this.p3[i].p1 + "," + this.p3[i].p2 + "¥r¥n";
}
alert(message);
},
m3:
function(arg1)
{
alert(arg1);
}
}
obj1.m1();
obj1.m2();
obj1.m3("test1");
Windows Script Debuggerは以下のリンク先からダウンロードできる。
ダウンロードの詳細 : Windows Script Debugger
インストール後、IEの [ ツール ] - [ インターネット オプション ] - [ 詳細設定 ] にて、 [ スクリプトのデバッグを使用しない (Internet Explorer) ] のチェックを外しておく必要がある。
Windows Script Debuggerにより、自分が開発しているASP.NETプロジェクトのJavaScriptはもちろん、それ以外のサイトのJavaScriptもデバッグすることができ、ステップ実行,ブレークポイント,ウォッチ,クイックウォッチなど様々なデバッグ機能が使用できる。
自分が開発しているASP.NETプロジェクトのJavaScriptをデバッグするには、まずサーバーコードをデバッグする時と同じようにデバッグ開始を行う。ページが表示されたら、IEの [ 表示 ] - [ スクリプト デバッガ ] - [ 開く ] を実行する。これにより、ブレークポイントの貼られたJavaScriptコードに到達した時に中断されるようになる。ブレークポイントを貼るのは、デバッグ開始を行う前・後どちらでもよい。
なお、 [ 表示 ] - [ スクリプト デバッガ ] - [ 次のステートメントで中断 ] を実行すれば、次にJavaScriptコードが実行されるタイミングで一時中断させることができる。
Windows Script Debuggerのドキュメントへのリンク
Microsoft Scripting Technologies
Hoge.jsをインクルードする場合に、HTMLに記述するタグ<script type="text/javascript" src="Hoge.js"></script>
jsファイルが別のjsファイルを利用する場合は、通常、以下のように記述する。
Hoge.jsがFoo.jsを利用する場合に、HTMLに記述するタグ<script type="text/javascript" src="Foo.js"></script>
<script type="text/javascript" src="Hoge.js"></script>
これで問題はないのだが、どうせならjsファイル自身に、インクルードのためのコードを含めてしまいたい。だが、JavaScriptにそのような構文は用意されていない。そこでどうするかというと、以下のように、”別のjsファイルをインクルードするためのHTMLのタグ”を出力するコードをjsファイルに記述すればよい。
Hoge.jsがFoo.jsを利用する場合に、Hoge.jsに記述するコードdocument.write("<script type=\"text/javascript\" src=\"/WebSite1/Foo.js\"></script>");
この時、scriptタグのsrc属性に指定するパスを絶対パスにしておけば、Hoge.jsを利用するHTMLがHoge.jsと同じ階層のディレクトリにあろうが違う階層のディレクトリにあろうが関係なく、正常に動作する。
このHoge.jsを利用するHTMLでは、Hoge.jsのインクルードのみ行えばよい。
信頼性に関するベスト プラクティス
制約された実行領域 (CER: Constrained Execution Region)
CriticalFinalizerObject クラス (System.Runtime.ConstrainedExecution)
SafeHandle クラス (System.Runtime.InteropServices)
CriticalHandle クラス (System.Runtime.InteropServices)
ReliabilityContractAttribute クラス (System.Runtime.ConstrainedExecution)
Webサービスは、メソッドのみを公開できるが、公開するメソッドはアクセス修飾子がpublicで更にWebMethodAttributeが付加されている必要がある。
Webサービスを他のプロジェクトから利用するには、利用するプロジェクトにWeb参照を追加してやればいい。
プロジェクトにWeb参照を追加した後にWebサービスが更新された場合は、Web参照を更新する必要がある。
以下に、Webサービスを利用するサンプルコードを示す。
Webサービス(HogeService)を利用するサンプルコードpublic void WebServiceTestMethod()
{
HogeService hoge = new HogeService();
hoge.Method1();
hoge.Method2();
}
一見すると普通のクラスと同じように扱えるように見える。しかしWebサービスの挙動は普通のクラスとは大きく異なる。
まず、ユーザーがHogeServiceに定義したコンストラクタは、HogeServiceのインスタンス化の時点では呼び出されない。そして、HogeServiceのWebメソッドにアクセスするたびに、ユーザーが定義したコンストラクタが呼び出される。(更に、WebサービスのDispose()メソッドも呼び出される。)
つまり、WebメソッドにアクセスするたびにWebサービスは全く別のインスタンスとなってしまうのだ。
なぜ、このような挙動を取るかというと、ここで利用しているHogeServiceクラスは、Webサービス本体ではなく、Webサービスにアクセスするためのプロキシクラスだからである。プロキシクラスは、Web参照の追加を行うと自動で生成される。
プロキシクラスには、Webメソッドを呼び出すためのメソッド(Webメソッドと同名)が用意される。このメソッドは、Webサービスに対してSOAPプロトコルでWebメソッドの処理要求をする。Webメソッドの処理要求を受信したWebサービスは、(Webサービスが配置されている場所で)インスタンス化→Webメソッド実行→Dispose()実行を行い、プロキシクラスに対してSOAPプロトコルで処理結果を返却する。そして処理結果を受け取ったプロキシクラスのメソッドはその処理結果を返却する。
なお、プロキシクラスには、Webメソッドを非同期に呼び出すためのメソッドも用意される。
とりあえずこの記事には”ブログ”と”タグ”と”分類”の3つをタギングしてみたお。(”タギング”って言葉の使い方合ってるかな?w)
それにしても、ブログ始めたばっかで記事数少ない内でよかった(;゚∀゚)=3
しかし、ここで取得される例外が必ずHttpUnhandledExceptionという訳ではない。
というのも、アプリケーションではなくASP.NETが発生させた例外に対してはHttpUnhandledExceptionがスローされないからである。
例えば、Webフォームに対して危険な文字列を送信すると、HttpRequestValidationExceptionがASP.NETからスローされる。この例外はWebフォームのPage_Errorでハンドルすることができるのだが、ここでハンドルしなくてもHttpUnhandledExceptionはスローされない。
ASP.NETが発生させる例外は、全てHttpExceptionである。HttpRequestValidationExceptionはもちろんのこと、HttpUnhandledExceptionもHttpExceptionの派生クラスであり、ASP.NETが発生させる例外の一つである。
しかし、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"を結合した文字列を直接記述しても良い。
<!-- Web.Configのconfigurationセクションに記述 -->
<system.codedom>
<compilers>
<compiler
language="c#;cs;csharp"
extension=".cs"
compilerOptions="/define:TRACE"
type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
warningLevel="1"/>
</compilers>
</system.codedom>
このように、compilerセクションのcompilerOptions属性にTRACEシンボルを定義するようにコンパイルオプションを指定する。language属性,extension属性,type属性は省略できない。
これで、ASP.NETでSystem.Diagnostics.Traceを使用できるようになる。
// 追記 ( 2007/03/22 )
ちなみに、最適化オプション ( "/optimize+" ) は指定しなくても最適化されるから問題なし^^;