2012年11月15日木曜日

[ASP.NET MVC] JSONを受け取る、JSONを返す


2012/12/21 コードが間違っていたので修正 orz


ASP.NET MVC3以上ならJSONをモデルバインディングしてくれるようだったのでやってみる

参考サイト:JavaScriptおよびAJAXの改善点(下の方)
http://www.atmarkit.co.jp/fdotnet/scottgublog/20100811aspnetmvc3/aspnetmvc3.html


適当にJSONを送りつける画面を作った


■ビュー(の一部)

2012/12/21 赤文字の部分を変更
変更前はJSONじゃなくてフォームデータ形式でデータを送信していた模様…

<input type="button" value="JSON送信" onclick="json_post();" />

<script type="text/javascript">
    function json_post() {
        var url = '@Url.Action("JSONTest")';

        var post_data =
            {
                Grade : 1
                , Class : 2
                , Name: "クライアントさん"
                , Results: [
                    { Subject: "数学", Score: 64 }
                    , { Subject: "理科", Score: 70 }
                    , { Subject: "英語", Score: 80 }
                ]
            };


        //var post_data =
        //    {
        //        "Grade": "1"
        //        , "Class": "2"
        //        , "Name": "クライアントさん"
        //        , "Results[0].Subject": "数学"
        //        , "Results[0].Score": 90
        //        , "Results[1].Subject": "理科"
        //        , "Results[1].Score": 88
        //        , "Results[2].Subject": "英語"
        //        , "Results[2].Score": 51
        //    };


        $.ajax(
            {
                url: url,
                type: 'POST',
                data: JSON.stringify(post_data),   // JSONの文字列に変換
                dataType: 'json' 
                contentType:'application/json',    // content-typeをJSONに指定する
                error: function () { },
                complete: function (data) { alert(data.responseText) },
            }
        );
    }
</script>


こんな画面
















ボタンをぽちっと押すと下記のアクションにJSONを送りつける


■コントローラー(の一部)

        [HttpPost]
        public ActionResult JSONTest(SchoolViewModel viewModel)
        {
            // クライアントからJSONを受け取って出力ウィンドウに出してみる
            System.Diagnostics.Trace.WriteLine(viewModel.Grade + "年");
            System.Diagnostics.Trace.WriteLine(viewModel.Class + "組");
            System.Diagnostics.Trace.WriteLine(viewModel.Name);
            foreach (var result in viewModel.Results)
            {
                System.Diagnostics.Trace.WriteLine(result.Subject + ":" + result.Score + "点");
            }

            // クライアントに適当なJSONデータを返す
            var res = new SchoolViewModel()
            {
                Grade = 2
                ,
                Class = 3
                ,
                Name = "サーバーさん"
                ,
                Results = new List<TestResult>() {
                    new TestResult() { Subject = "数学", Score = 64 }
                    , new TestResult() { Subject = "理科", Score = 70 }
                    , new TestResult() { Subject = "英語", Score = 80 }
                }
            };
            return Json(res);
        }

バインドするクラスはこんな感じで何の変哲もないViewModel的なクラス。

    public class SchoolViewModel
    {
        public int Grade { get; set; }
        public int Class { get; set; }
        public string Name { get; set; }
        public List<TestResult> Results { get; set; }
    }

    public class TestResult
    {
        public string Subject { get; set; }
        public int Score { get; set; }
    }

実行した結果


■クライアントから送信したJSONの内容をVisualStudioの出力ウィンドウに出してみたもの
1年
2組
クライアントさん
数学:90点
理科:88点
英語:51点


■サーバーから返されたJSON








やったね!ちゃんと受け取れてるし、JSON返してるし!



↓2012/12/21 削除 そもそもJSONとして送ってなかった。ううう…ごめんなさい。

JSONをコレクションにモデルバインディングする時の注意


最初、コレクションに対応する、クライアントから送信するJSONは、

"Results": [ { "Subject[0]": "数学", "Score": 95 }, { "Subject": "英語", "Score": 51 } ]

のように配列で書いていたのだが、これではListにバインドしてくれなかった。


下記のサイトによると
http://msdn.microsoft.com/ja-jp/magazine/hh781022.aspx

> 少々直観に反することですが、JSON 要求にも同じ要件があります。つまり、JSON 要求もフォーム ポストの名前付け構文に従っている必要があります。
> 既定の値プロバイダーやモデル バインダーでは、データを JSON のフォーム ポストとして表す必要があります。

>    {
>      "UnitPrice[0].Code": "USD",
>      "UnitPrice[0].Amount": 100.00,
>    
>      "UnitPrice[1].Code": "EUR",
>      "UnitPrice[1].Amount": 73.64
>    }


どうやら配列のまま送れないらしい('A`)
フォームデータのように番号をつけないといけないようだ。
そのくせサーバーから返るJSONはしっかり配列になっているので、なんだか納得いかないのだった。



モデルバインディングしなくてもResponse.InputStreamから自力でデータを取り出して
DataContractJsonSerializerでデシリアライズすればクラスに落とし込めるよね!
と思ったけどやはり面倒そうである(なんだそりゃ)

0 件のコメント:

コメントを投稿