連載
» 2009年07月10日 00時00分 公開

第3回 ActionResultオブジェクトでアクション操作も自由自在連載:ASP.NET MVC入門(3/5 ページ)

[山田祥寛(http://www.wings.msn.to/),著]

JSON形式のデータを受け渡しする− JsonResultオブジェクト −

 「JSON(JavaScript Object Notation」とは、JavaScriptにおけるオブジェクト形式(ハッシュ形式)のデータ記述言語である。JavaScriptとの親和性が高い、XMLデータよりもサイズが小さいなどの理由から、特にAjax技術によるクライアント/サーバ通信で用いられることが多い。

 そして、JSON形式のコンテンツをアクション・メソッドから出力するのが、JsonResultオブジェクトの役割だ。ControllerクラスのJsonメソッドを経由して生成できる。

 JSONデータそのものは、プレーン・テキストに過ぎないので、前述したContentメソッドを使っても代替することは可能だ。しかし、Jsonメソッド(JsonResultオブジェクト)では、与えられた引数がオブジェクトである場合にも、これをJSON形式にシリアライズしてくれる、コンテンツ・タイプを自動で設定してくれる、などのアドバンテージがある。特別な理由がない限りは、Jsonメソッドを優先して利用するべきだろう。

 それではさっそく、具体的なサンプルを見ていこう。ここでは、Ajax技術を使った、以下のような書籍検索の仕組みを作成するものとする。

図3 ここで作成するサンプルの実行結果
テキストボックスから入力されたISBNコードに従って検索処理を実行。検索結果はダイアログで表示される。

[1]アクション・メソッドを作成する

 まずは、アクション・メソッドの作成から。Formメソッドは検索フォームを呼び出すためのアクション、SearchメソッドはAjax通信で呼び出され、書籍情報の検索を行い、その結果をJSON形式で返すためのアクションだ。

// 検索フォームの呼び出し
public ActionResult Form() {
  return View();
}

//[検索]ボタンのクリック時に呼び出され、検索処理を実行
public ActionResult Search(String isbn) {

  // リクエストがAjax通信(非同期通信)である場合のみ検索を実行
  if (Request.IsAjaxRequest()) { (1)

    var _db = new MyMvcEntities();

    var bok = (from b in _db.Book where b.isbn == isbn
      select new {b.title, b.price, b.publish}).FirstOrDefault();

    return Json(bok); (2)
  } else {
    // リクエストがAjax通信以外の場合、何もしない
    return new EmptyResult();(3)
  }
}

' 検索フォームの呼び出し
Function Form() As ActionResult
  Return View()
End Function

' [検索]ボタンのクリック時に呼び出され、検索処理を実行
Function Search(ByVal isbn As String) As ActionResult

  ' リクエストがAjax通信(非同期通信)である場合のみ検索を実行
  If Request.IsAjaxRequest() Then (1)

    Dim _db = New MyMvcEntities()

    Dim bok = (From b In _db.Book Where b.isbn = isbn _
      Select b.title, b.price, b.publish).FirstOrDefault()

    Return Json(bok) (2)
  Else
    ' リクエストがAjax通信以外の場合、何もしない
    Return New EmptyResult() (3)
  End If
End Function

リスト4 書籍情報を検索し、その結果をJSON形式で返すコード(上:ResultController.cs、下:ResultController.vb)

 Formアクションについては、ビューの呼び出しているだけなので、特筆すべき点はない。ここではSearchアクションに注目して、コード中の(1)(3)のポイントを押さえていこう。

(1)Ajax通信であるかを判定する

 Ajaxで利用するために用意したアクションが、通常の同期通信から呼び出されることはアプリケーションに思わぬ挙動を引き起こす一因にもなり得る。Ajax通信での利用を想定したアクションでは、基本的にIsAjaxRequestメソッド*3で、リクエストがAjax経由で呼び出されたかどうかを判定するのが好ましい。ここでは、IsAjaxRequestメソッドがTrueを返す(Ajax通信である)場合にのみ、検索処理を実行するようにしている。

*3 「IsAjaxRequest」がプロパティではなくメソッドである点に要注意。IsAjaxRequestメソッドは、AjaxRequestExtensionsクラス(System.Web.Mvc名前空間)で定義されたHttpRequestBaseクラス(System.Web名前空間)の拡張メソッドである。メンバの拡張は、現時点ではプロパティに対しては行えないのだ。


 ちなみに、IsAjaxRequestメソッドは、X-Requested-Withリクエスト・ヘッダの値がXMLHttpRequestであるかどうかによって、Ajax通信の是非を判定している。X-Requested-Withヘッダは、prototype.jsをはじめ、jQuery、Dojoなど代表的なJavaScriptライブラリで使われているが、すべてのJavaScriptライブラリが付与するとは限らないので注意されたい。

(2)Jsonメソッドは自動シリアライズを行う

 冒頭述べたように、Jsonメソッドは引数に指定されたオブジェクトを、JavaScriptSerializer.Serializeクラス(System.Web.Script.Serialization名前空間)でシリアライズし、最終的なレスポンスとして返す。

 .NETの型とJSON形式におけるデータ型の対応関係については、「JavaScriptSerializerクラス(System.Web.Script.Serialization)」が詳しいので、併せて参照いただくとよいだろう。このクラスによりシリアライズされたすべての値が、JSON形式から元の形式にデシリアライズできるとは限らない点に注意されたい。

(3)何も処理をしないのはEmptyResultオブジェクト

 アクション・メソッドの処理後、「何も行わない」という指定をすることもできる。これには、EmptyResultオブジェクトを返すようにすればよい。

 もしくは、アクションが戻り値を持たない場合も、内部的にはEmptyResultオブジェクトを返すのと同じ意味であると見なされる。例えば、以下は事後に何も行わないアクションの例である(戻り値のないメソッドとなる)。

Sub Test()
  ' アクションで行うべき任意の処理
End Sub


 その性質上、EmptyResultオブジェクトを利用する機会はそれほどにないと思われるが*4、一応、頭の片隅に留めておくとよいだろう。

*4 ActionResult(派生)オブジェクトで出力を生成する代わりに、HttpResponseオブジェクト(System.Web名前空間)でレスポンスを生成するというケースも考えられるが、ビュー/ロジックの分離という観点からは、そのようなコードは避けるべきだ。


[2]ビューを生成する

 次に、Formアクションに対応するビュー・スクリプトを用意する。ビューを新規に作成するには、前回までで解説しているように、コード・エディタ上で該当のアクションにカーソルを合わせた状態でマウスを右クリックし、表示されたコンテキスト・メニューから[Add View]を選択すればよい。

 [Add View]ダイアログが表示されるので、以下の表2の要領で必要な情報を入力する。

項目 設定値
View name Form
Create a partial view(.ascx) チェックしない
Create a strongly-typed view チェックしない
Select master page チェックしない
表2 [Add View]ダイアログの設定値

 ここまではおなじみの手順でもあるので、もはや迷うところはないだろう。ビュー・スクリプトの骨組みが生成されるので、次のリスト5の要領でコードを追加する(追記部分を濃い文字で表している)。

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
<!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 id="Head1" runat="server">
<title>書籍検索</title>


<!--Ajax通信に必要なスクリプトをインポート-->


<script type="text/javascript">

  // 非同期通信の成功時に呼び出されるイベント・ハンドラ

</script>
</head>
<body>
<div>

</div>
</body>
</html>

<%@ Page Language="VB" Inherits="System.Web.Mvc.ViewPage" %>
<!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>


<!--Ajax通信に必要なスクリプトをインポート-->


// 非同期通信の成功時に呼び出されるイベント・ハンドラ

</script>
</head>
<body>
<div>

</div>
</body>
</html>

リスト5 検索フォームを生成するためのビュー・スクリプト(Result/Form.aspx。上:C#、下:VB)

 ここで押さえておきたいポイントは、次のとおりである。

[A]必要なスクリプトをインポートする

 Ajax機能の実装に最低限必要なファイルは、MicrosoftAjax.js/MicrosoftMvcAjax.jsの2つである。

 前者はMicrosoft AJAX Libraryの基本ライブラリを含んだスクリプトで、MicrosoftMvcAjax.jsはビュー・スクリプトで使うAjaxヘルパー(後述するAjax.BeginFormメソッドなど)をサポートするためのスクリプトだ。

[B]フォームの処理結果をAjax処理するのは、Ajax.BeginFormメソッド

 フォームからの処理結果をAjax処理するためには、Ajax.BeginFormメソッドを使用する。Ajax.BeginFormメソッドで定義されたフォームの内容はAjax通信の対象となり、サブミット・ボタンをクリックしてもページはリフレッシュされなくなる(JavaScriptライブラリの1つであるSys.Mvc.AsyncFormオブジェクトで処理されるようになる)。

 これまでにも利用してきたHtml.BeginFormメソッドのAjax対応版であると思っておけばよいだろう。Html.BeginFormメソッドについては、第1回の内容をご覧いただきたい。Html.BeginFormメソッドと異なる点は、引数の末尾にAjaxOptionsオブジェクトを指定している点である。

 AjaxOptionsクラス(System.Web.Mvc.Ajax名前空間)は、名前のとおり、Ajax関連のオプションを指定するためのクラスで、以下のようなプロパティを指定できる。


表3 AjaxOptionsクラス(System.Web.Mvc.Ajax名前空間)の主なプロパティ

 ここでは、OnCompleteプロパティを指定することで、Searchアクションへの非同期通信が成功したタイミングでdisp関数を呼び出し、その結果をダイアログ表示しているわけだ。

[C]非同期通信前後での処理を定義する

 いま[B]でも触れたように、ASP.NET MVCでは非同期通信の前後(OnBegin、OnComplete、OnSuccess、OnFailure)のタイミングで、クライアントサイドの処理を実行できる。ここでは非同期通信成功(OnSuccess)のタイミングでdisp関数を呼び出し、結果をリスト形式に整形のうえ、ダイアログ表示している。

 このイベント・ハンドラで注目してほしいのは、非同期通信に関する詳細情報を引数context(Sys.Mvc.AjaxContextオブジェクト)として受け取っているという点である。

 Sys.Mvc.AjaxContextオブジェクトでは、以下のようなプロパティを公開しており*5、これらプロパティを介することで、非同期通信の結果にアクセスすることができる。

*5 プロパティ値には、ゲッター・メソッド「get_プロパティ名」を介してアクセスできる。


プロパティ 概要
data 応答に含まれる結果文字列
insertionMode 挿入モード(0:Replace、1:InsertBefore、2:AfterBefore)
loadingElement LoadingElementIdプロパティで指定された要素
response レスポンス情報(Sys.Net.WebRequestExecutorオブジェクト)
request リクエスト情報(Sys.Net.WebRequestオブジェクト)
updateTarget UpdateTargetIdプロパティで指定された要素
表4 Sys.Mvc.AjaxContextオブジェクトの主なプロパティ

 ここではdataプロパティを参照することで、サーバからの応答(JSONデータ)を取得し、eval関数でこれをJavaScriptオブジェクトとして解析し直している。なお、eval関数でJSONデータを解析する場合、データ全体を丸カッコで囲まなければならない。さもないと、正しくデータが解析できない。

 JavaScriptオブジェクトができてしまえば、後は、ここから必要なプロパティを取り出し、Sys.StringBuilderオブジェクトでリスト形式の文字列を整形するだけだ。Sys.StringBuilderオブジェクトについては、拙稿「MS AJAX Libのユーティリティ機能を活用しよう」も併せて参照いただきたい。

 以上の手順を終えたら、サンプルの動作を確認してみよう。サンプル・アプリケーションには、以下のアドレスでアクセスできる(ポート番号は環境によって異なる可能性がある)。

http://localhost:3568/Result/Form


 冒頭の図のように、データベースに登録されているISBNコードで検索することで、対応する詳細な書籍情報が表示されれば、サンプルは正しく動作している。

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。