在ASP.NET MVC中,可透過以下方式將物件轉為JSON字串傳給前端:

public ActionResult TestJson()
{
    return Json(DateTime.Now);
}

而在ASP.NET Web API,Action多半會回傳特定資料型別,ApiController會依據呼叫端所要求將資料型別轉成XML(例如: 瀏覽器直接輸入URL)、JSON(例如: 使用$.getJSON())或其他格式[延伸閱讀]:

public class BarController : ApiController
{
    public DateTime Index()
    {
        return DateTime.Now;
    }
}

ASP.NET MVC 4在上述傳回JSON的情境中,是用什麼類別進行JSON轉換呢?

答案: ASP.NET MVC 4用JavaScriptSerializer,而ASP.NET Web API從RC起己改用Json.NET? 讓我們來驗證一下。

開啟一個ASP.NET MVC 4空白專案,分別加入BooController及BarController:

BooController有兩個Action,Index()提供測試Web API專用的View、TestJson()則傳回JSON轉換後的DateTime: (在此型別特別選用DateTime,以便由輸出結果協助判斷背後的轉換核心為DataContractSerializer、JavaScriptSerializer或是Json.NET?)

排版顯示純文字
using System;
using System.Web.Mvc;
 
namespace MyMvc.Controllers
{
    public class BooController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
        public ActionResult TestJson()
        {
            return Json(DateTime.Now, JsonRequestBehavior.AllowGet);
        }
    }
}

由於Web API直接以瀏覽器檢視會傳回XML,故在Index.cshtml中我們透過$.getJSON()來測試傳回內容:

排版顯示純文字
@{
    Layout = null;
}
 
<!DOCTYPE html>
 
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <script src="~/Scripts/jquery-1.8.0.js"></script>
    <script>
        $(function () {
            $(":button").click(function () {
                $.getJSON("@Url.Content("~/Api/Bar")", {}, function (r) {
                    alert(r);
                });
            });
        });
    </script>
</head>
<body>
    <input type="button" value="Test Web API" />
</body>
</html>

BarController是一個Web API Controller,在Index() Action中傳回DateTime型別:

排版顯示純文字
using System;
using System.Web.Http;
 
namespace MyMvc.Controllers
{
    public class BarController : ApiController
    {
        public DateTime Index()
        {
            return DateTime.Now;
        }
    }
}

程式完成後,開始測試。首先使用瀏覽器真接測試Web API BarController,在地址列輸入URL,因屬GET動作,故經過Content Neogociation後傳回XML:

為了一睹Web API傳回的JSON內容,Boo/Index.cshtml透過$.getJSON()取得結果,該格式與Json.NET的日期轉換結果一致:

接著測試Json(DateTime.Now),由"\/Date(Tick)\/"研判,其使用的是JavaScriptSerializer:(如為DataContractSerializer,會再加上時區資訊)

既然是JavaScriptSerializer,就得留意使用JavaScriptSerializer反序列化後出現時區差異的鳥問題,用一段程式讓這顆地雷現形:

排版顯示純文字
        public ActionResult DateJsonWtf()
        {
            WebClient wc = new WebClient();
            Uri url = Request.Url;
            string s = wc.DownloadString(
                string.Format("http://{0}:{1}/Boo/TestJson", 
                url.Host, url.Port));
            JavaScriptSerializer jss = new JavaScriptSerializer();
            DateTime d = jss.Deserialize<DateTime>(s);
            return Content(string.Format(
                "Now={0:MM-dd HH:mm}, TestJson={1:MM-dd HH:mm}",
                DateTime.Now, d));
        }

果不其然,由ASP.NET MVC Action接收Json()序列化後的DateTime.Now再使用JavaScriptSerializer還原,得到的時間是UTC格林威治時間,與本機時間產生了8小時的時差! 在做類似應用時要特別小心。

至於是否有簡便的解決方案? 且待下回分解。


Comments

# by KKBruce

就小弟瞭解,應該是希望未來如果真的有傳回json的需求,應該盡量透過web api來實作,而不是使用mvc來實作。web api才有辦法完成RESTful。

Post a comment