第三節:框架前期準備篇之利用Newtonsoft.Json改造MVC預設的JsonResult

来源:https://www.cnblogs.com/yaopengfei/archive/2018/08/28/9518725.html
-Advertisement-
Play Games

一. 背景 在MVC框架中,我們可能經常會用到 return Json(),而Json方法內部又是一個JsonResult類,那麼JsonResult內部又是什麼原理呢?在MVC框架中,各種xxxResult便捷了我們的開發,但這些都不是本節的重點,在這裡我們只需要知道JsonResult內部的原理 ...


一. 背景

  在MVC框架中,我們可能經常會用到 return Json(),而Json方法內部又是一個JsonResult類,那麼JsonResult內部又是什麼原理呢?在MVC框架中,各種xxxResult便捷了我們的開發,但這些都不是本節的重點,在這裡我們只需要知道JsonResult內部的原理即可。
  JsonResult內部原理是基於 JavaScriptSerializer來做的序列化,在使用過程中,有這麼幾個弊端:   ①:DateTime類型返回給前端格式不友好:\/Date(1535009968228)\/ ,相當彆扭。(PS:前端有很多辦法處理的)   ②:對於前端而言,對於屬性名可能更傾向於小寫開頭,但在C#中,很多都是大寫,但JsonResult將原結果預設返回給前端,前端人員可能會有點小不爽。(PS:這也可以算作是一個習慣問題,沒有明確的對錯)   ③:迴圈引用的問題。   關於使用Newtonsoft.Json改造MVC預設的JsonResult,有很多種方式,本節僅是整理了一下在我日常開發中的使用方法。(PS:這裡的MVC版本為: 5.2.4.0)

   這裡簡單的分析一下JsonResult的源碼:

 ①:繼承了ActionResult, 實現了ExecuteResult方法。

 ②:解讀源碼可知,JsonResult內部實現原理是調用了JavaScriptSerializer對象中的Serialize方法,將Json對象轉換成了Json字元串,通過:response.Write(javaScriptSerializer.Serialize(this.Data)); 傳遞給前臺。

 ③:預設是禁止Get請求訪問的. JsonRequestBehavior.DenyGet。

 ④:在MVC的Action中,return Json(),這裡的Json通過源碼可知,即new了一個JsonResult對象而已,並且MVC中封裝了很多重載。

 

  本節涉及到的知識點有:

    1. MVC中的各種Result,可參考:http://www.cnblogs.com/yaopengfei/p/7910767.html

    2. MVC中的過濾器,可參考:https://www.cnblogs.com/yaopengfei/p/7910763.html

二. 測試JsonResult的弊端

   這裡主要測試一下DateTime類型“亂碼”(時間戳)問題和預設大小寫問題。

後臺代碼:

 1     public ActionResult Json1()
 2     {
 3        var msg = new
 4        {
 5           ID = 1,
 6           Name = "ypf1",
 7           time = DateTime.Now
 8        };
 9        return Json(msg);
10    }

前臺代碼:

1   $("#btn1").on("click", function () {
2        $.post("Json1", {}, function (data) {
3              console.log(data);
4        });
5    });

測試結果:

 

下麵提供一種解決時間戳轉換的問題,使用該js文件,對Date類型進行擴展,代碼如下:

 1 /**     
 2  * 對Date的擴展,將 Date 轉化為指定格式的String     
 3  * 月(M)、日(d)、12小時(h)、24小時(H)、分(m)、秒(s)、周(E)、季度(q) 可以用 1-2 個占位符     
 4  * 年(y)可以用 1-4 個占位符,毫秒(S)只能用 1 個占位符(是 1-3 位的數字)     
 5  * eg:     
 6  * (new Date()).pattern("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423     
 7  * (new Date()).pattern("yyyy-MM-dd E HH:mm:ss") ==> 2009-03-10 二 20:09:04     
 8  * (new Date()).pattern("yyyy-MM-dd EE hh:mm:ss") ==> 2009-03-10 周二 08:09:04     
 9  * (new Date()).pattern("yyyy-MM-dd EEE hh:mm:ss") ==> 2009-03-10 星期二 08:09:04     
10  * (new Date()).pattern("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18     
11 
12 使用:(eval(value.replace(/\/Date\((\d+)\)\//gi, "new Date($1)"))).pattern("yyyy-M-d h:m:s.S");
13 
14  */
15 Date.prototype.pattern = function (fmt) {
16     var o = {
17         "M+": this.getMonth() + 1, //月份        
18         "d+": this.getDate(), //
19         "h+": this.getHours() % 12 == 0 ? 12 : this.getHours() % 12, //小時        
20         "H+": this.getHours(), //小時        
21         "m+": this.getMinutes(), //
22         "s+": this.getSeconds(), //
23         "q+": Math.floor((this.getMonth() + 3) / 3), //季度        
24         "S": this.getMilliseconds() //毫秒        
25     };
26     var week = {
27         "0": "/u65e5",
28         "1": "/u4e00",
29         "2": "/u4e8c",
30         "3": "/u4e09",
31         "4": "/u56db",
32         "5": "/u4e94",
33         "6": "/u516d"
34     };
35     if (/(y+)/.test(fmt)) {
36         fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
37     }
38     if (/(E+)/.test(fmt)) {
39         fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? "/u661f/u671f" : "/u5468") : "") + week[this.getDay() + ""]);
40     }
41     for (var k in o) {
42         if (new RegExp("(" + k + ")").test(fmt)) {
43             fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
44         }
45     }
46     return fmt;
47 }
View Code

在前端這麼使用,就可以將時間轉換成正常的顯示:(詳細的見上面的代碼)

 

三. 自我改造

   有了前面的JsonResult的代碼分析,這裡先寫一種最簡單粗暴的改造方式,當然需要實現安裝 Newtonsoft.Json程式集。

改造方案一:

   新建YpfSimpleJsonResult類,繼承ActionResult類,利用構造函數傳遞數據,override ExecuteResult方法,在裡面利用Newtonsoft進行改寫,代碼如下:

 1     /// <summary>
 2     /// 簡潔版的改寫,只是替換了實現方式
 3     /// </summary>
 4     public class YpfSimpleJsonResult : ActionResult
 5     {
 6         private object _Data = null;
 7         public YpfSimpleJsonResult(object data)
 8         {
 9             this._Data = data;
10         }
11         public override void ExecuteResult(ControllerContext context)
12         {
13             context.HttpContext.Response.ContentType = "application/json";
14             context.HttpContext.Response.Write(JsonConvert.SerializeObject(this._Data));
15         }
16     }

測試介面:

 1    public ActionResult Json3()
 2         {
 3             var msg = new
 4             {
 5                 ID = 1,
 6                 Name = "ypf1",
 7                 time = DateTime.Now
 8             };
 9             return new YpfSimpleJsonResult(msg);
10         }

測試結果:

 

改造方案二:

  有了上面的方案的基礎,下麵深度改造一下,新建YpfJsonResult類,直接繼承高層封裝JsonResult類,並配置引用問題、預設小寫問題、自定義時間格式,代碼如下:

 1  public class YpfJsonResult : JsonResult
 2     {
 3         public YpfJsonResult()
 4         {
 5             Settings = new JsonSerializerSettings
 6             {
 7                 //1. 忽略迴圈引用問題,建議設置為Error,這樣的話遇到迴圈引用的時候報錯
 8                 ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
 9                 //2. 日期格式化,這裡可以將Newtonsoft預設的格式進行修改
10                 DateFormatString = "yyyy-MM-dd HH:mm:ss",
11                 //3. 設置屬性為開頭字母小寫的駝峰命名
12                 ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
13             };
14         }
15 
16         public JsonSerializerSettings Settings { get; private set; }
17 
18         public override void ExecuteResult(ControllerContext context)
19         {
20             if (context == null)
21             {
22                 throw new ArgumentNullException("context");
23             }
24             if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
25             {
26                 throw new InvalidOperationException("GET is not allowed");
27             }
28             HttpResponseBase response = context.HttpContext.Response;
29             response.ContentType = string.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType;
30             if (this.ContentEncoding != null)
31             {
32                 response.ContentEncoding = this.ContentEncoding;
33             }
34             if (this.Data == null)
35             {
36                 return;
37             }
38             var scriptSerializer = JsonSerializer.Create(this.Settings);
39             scriptSerializer.Serialize(response.Output, this.Data);
40         }
41     }

測試介面:

 1    public ActionResult Json2()
 2    {
 3       var msg = new
 4        {
 5           ID = 1,
 6           Name = "ypf1",
 7           time = DateTime.Now
 8        };
 9       //註意:這裡的Data是JsonResult類中的一個獲取和設置數據的屬性。
10       return new YpfJsonResult() { Data = msg };
11    }

測試結果:

總結:

   雖然我們通過第二套方案已經達到了我們的目的,但它存在一個弊端,就是侵入性太強,每個方法中都要改寫,那麼有沒有一種方式可以全局控制呢?

  顯然是有的,可以考慮使用全局過濾器。

 

四. 全局處理

   這裡換一種思路,通過註冊一個全局過濾器,對每個Action進行監測,如果使用的是JsonResult,就把JsonResult替換成自己編寫的YpfJsonResult,這樣的話業務中的調用代碼,不需要發生任何變化,仍然可以使用 return Json()方法。

  特別註意:這裡的過濾器要使用行為過濾器,並且要在OnActionExecuted方法中進行業務的編寫。(這是過濾器執行順序決定的)

代碼分享:

 1   public class YpfJsonFilter: ActionFilterAttribute
 2     {
 3 
 4         public override void OnActionExecuted(ActionExecutedContext filterContext)
 5         {
 6             if (filterContext.Result is JsonResult
 7                 && !(filterContext.Result is YpfJsonResult))
 8             {
 9                 JsonResult jsonResult = (JsonResult)filterContext.Result;
10                 YpfJsonResult jsonNetResult = new YpfJsonResult();
11                 jsonNetResult.ContentEncoding = jsonResult.ContentEncoding;
12                 jsonNetResult.ContentType = jsonResult.ContentType;
13                 jsonNetResult.Data = jsonResult.Data;
14                 jsonNetResult.JsonRequestBehavior = jsonResult.JsonRequestBehavior;
15                 jsonNetResult.MaxJsonLength = jsonResult.MaxJsonLength;
16                 jsonNetResult.RecursionLimit = jsonResult.RecursionLimit;
17                 filterContext.Result = jsonNetResult;
18             }
19         }
20 
21 
22     }
過濾器代碼

編寫完過濾器後,需要全局註冊一下:

  可以在在FilterConfig文件中註冊 filters.Add(new YpfJsonFilter());

  或者直接去:Global文件中:GlobalFilters.Filters.Add(new YpfJsonFilter()); 代碼來註冊,道理都一樣

介面代碼,不需要做任何改變,繼續沿用return Json()即可。

測試結果:

 

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 本人才疏學淺,用郭德綱的話說“我是一個小學生”,如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲     明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • IDEA 版本:2018.2.1 1.點擊 File——New——Project 2.選擇SpringInitializr和Jdk, 點擊 next 3.設置相關包名 工程類型選擇MavenProject 點擊next 4.配置Web相關依賴,搜索'web'點擊第一個,IDEA會為你的項目裝載web ...
  • Spring裝配有三種方式: 基於XML的顯式配置 基於註解的自動裝配 在java中進行顯示配置 1、基於註解的自動裝配 Spring從組件掃描和自動裝配兩個角度實現自動轉配 組件掃描:Spring會自動發現應用上下文中所創建的bean,配置時需要指定掃描的包 組件掃描會將標記了以下註解的類實例化交 ...
  • 前言 在 "上一篇" 中我們學習了結構型模式的適配器模式和橋接模式。本篇則來學習下結構型模式的外觀模式和裝飾器模式。 外觀模式 簡介 外觀模式隱藏系統的複雜性,並向客戶端提供了一個客戶端可以訪問系統的介面。這種類型的設計模式屬於結構型模式,它向現有的系統添加一個介面,來隱藏系統的複雜性。 簡單的來說 ...
  • 前言 在互聯網設計架構過程中,日誌非同步落庫,儼然已經是高併發環節中不可缺少的一環。為什麼說是高併發環節中不可缺少的呢? 原因在於,如果直接用mq進行日誌落庫的時候,低併發下,生產端生產數據,然後由消費端非同步落庫,是沒有什麼問題的,而且性能也都是異常的好,估計tp99應該都在1ms以內。但是一旦併發增 ...
  • 什麼是元類 源自一句話: 在Python中, 一切皆對象, 而對象都是由類實例化得到的 對象tea1是調用OldboyTeacher類得到的, 如果說一切皆對象, 那麼OldboyTeacher也是一個對象, 只要是對象都是調用一個類實例化得到的, 即OldboyTeacher=元類(....),內 ...
  • 最近在做註冊登錄服務時,學慣用Go語言操作MySQL資料庫實現用戶數據的增刪改查,現將個人學習心得總結如下,另外附有代碼倉庫地址,歡迎各位有興趣的fork。 軟體環境:Goland、Navicat for MySQL。 一、實現思路 1,我的總體設計思路是先寫出連接資料庫和關閉資料庫的邏輯,再建立四 ...
  • 1、Eclipse常用快捷鍵操作 2、Eclipse文檔註釋導出幫助文檔 3、Eclipse項目的jar包導出與使用jar包 4、不同修飾符混合使用細節 5、辨析何時定義變數為成員變數 6、類、抽象類、介面作為方法參數 7、類、抽象類、介面作為方... ...
  • Props是配置Actor和實例化Actor,那實例化後,就應該訪問了,Props.Actor提供了Actor.Spawn(),Actor.SpawnPrefix(),Actor.SpawnNamed()三個方法,來獲取Actor實例,需要註意的是,這些方法返回的並不是真正的Actor對象,而是一個... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...