今天幫同事找問題時碰到 Mvc 序列化 Json 的地雷,問題出在 Controller.Json()
是使用 JavaScriptSerializer
序列化物件成 Json 字串,日期經轉換後無法讓前端 JavaScript 直接套用 new Date()
。
原 Json 方法
程式碼:
public ActionResult Test() { var data = new { Id = 1, Name = Guid.NewGuid().ToString(), Now = DateTime.Now }; return Json(data, JsonRequestBehavior.AllowGet); }
回傳:
{ "Id": 1, "Name": "450dd1c9-034c-490e-aa05-8e5a2983c5fb", "Now": "/Date(1545478289178)/" }
首先使用預設的 Controller.Json()
來測試一次的結果如上,微軟預設的 JavaScriptSerializer
將日期轉換成 /Date(1545478289178)/
,如果直接將轉換結果塞到 new Date()
會噴錯誤訊息。
複寫 Json 方法
程式碼:
public class JsonNetResult : JsonResult { public JsonSerializerSettings SerializerSettings { get; set; } public Formatting Formatting { get; set; } public JsonNetResult() { SerializerSettings = new JsonSerializerSettings(); } public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } HttpResponseBase response = context.HttpContext.Response; response.ContentType = !string.IsNullOrWhiteSpace(ContentType) ? ContentType : "application/json"; if (ContentEncoding != null) { response.ContentEncoding = ContentEncoding; } if (Data != null) { using (var writer = new JsonTextWriter(response.Output) { Formatting = Formatting }) { JsonSerializer serializer = JsonSerializer.Create(SerializerSettings); serializer.Serialize(writer, Data); writer.Flush(); } } } } protected override JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior) { // 模擬原本阻擋 GET 的機制 if (behavior == JsonRequestBehavior.DenyGet && string.Equals(this.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) { return new JsonResult(); } return new JsonNetResult() { Data = data, ContentType = contentType, ContentEncoding = contentEncoding }; }
回傳:
{ "Id": 1, "Name": "e42ba764-0497-494a-99b2-90c035ddf169", "Now": "2018-12-22T19:46:31.9098858+08:00" }
上面的程式碼 override 掉原本 Controller.Json()
並採用 Json.NET 這個高效能的 Json 序列化套件,它將日期格式依照 ISO 8601 標準轉換成 2018-12-22T19:46:31.9098858+08:00
,效能也比原本的 JavaScriptSerializer
來的高(測試數據),接著丟到 new Date()
試試果然成功。