ASP.NET MVC的JSON序列化陷阱
1 | 18,789 |
在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。