定製屬於你自己的ViewEngine(一套邏輯多套UI)
阿新 • • 發佈:2018-12-13
ASP.NET MVC出來這麼久了,心中卻又很多的疑惑:為什麼所有的View都要放在Views目錄下? 為什麼Shared資料夾下面的頁面可以被共享? 為什麼Page既可以是*.cshtml,也可以是*.aspx?
其實上面的幾個問題歸結起來都是檢視引擎的功效。
在傳統的ASP.NET中,可能還沒有ViewEngine的概念。因為在Web From裡面,實現Page實現了IHttpHanlder的介面,所以Page既是響應的處理類,也是檢視的渲染類。在ASP.NET MVC中,檢視的概念被抽象了出來,試圖引擎的概念也被抽象成了一個介面。
首先來看一下IViewEngine介面的定義
1namespace System.Web.Mvc 2 { 3 public interface IViewEngine 4 { 5 ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache); 6 ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, booluseCache); 7 void ReleaseView(ControllerContext controllerContext, IView view); 8 } 9 }
總共3個函式,總結起來大概就是兩個功能:Find & Release。
預設情況下,ASP.NET MVC提供了兩個檢視引擎:WebFormViewEngine和RazorViewEngine
1 namespace System.Web.Mvc 2 { 3 public static class ViewEngines 4 {5 private static readonly ViewEngineCollection _engines = new ViewEngineCollection 6 { 7 new WebFormViewEngine(), 8 new RazorViewEngine(), 9 }; 10 11 public static ViewEngineCollection Engines 12 { 13 get { return _engines; } 14 } 15 } 16 }
這就是為什麼ASP.NET MVC既支援*.aspx,又支援*.cshtml的原因了(個人覺得如果已經確定要使用RazorView的話,不如把WebFormViewEngine給移除,可能對效能會有所幫助)。
那為什麼所有的檢視都要放在Views目錄下呢,這個就要拜RazorViewngines所賜了。
下面是RazorViewEngine的建構函式:
1 public RazorViewEngine(IViewPageActivator viewPageActivator) 2 : base(viewPageActivator) 3 { 4 AreaViewLocationFormats = new[] 5 { 6 "~/Areas/{2}/Views/{1}/{0}.cshtml", 7 "~/Areas/{2}/Views/{1}/{0}.vbhtml", 8 "~/Areas/{2}/Views/Shared/{0}.cshtml", 9 "~/Areas/{2}/Views/Shared/{0}.vbhtml" 10 }; 11 AreaMasterLocationFormats = new[] 12 { 13 "~/Areas/{2}/Views/{1}/{0}.cshtml", 14 "~/Areas/{2}/Views/{1}/{0}.vbhtml", 15 "~/Areas/{2}/Views/Shared/{0}.cshtml", 16 "~/Areas/{2}/Views/Shared/{0}.vbhtml" 17 }; 18 AreaPartialViewLocationFormats = new[] 19 { 20 "~/Areas/{2}/Views/{1}/{0}.cshtml", 21 "~/Areas/{2}/Views/{1}/{0}.vbhtml", 22 "~/Areas/{2}/Views/Shared/{0}.cshtml", 23 "~/Areas/{2}/Views/Shared/{0}.vbhtml" 24 }; 25 26 ViewLocationFormats = new[] 27 { 28 "~/Views/{1}/{0}.cshtml", 29 "~/Views/{1}/{0}.vbhtml", 30 "~/Views/Shared/{0}.cshtml", 31 "~/Views/Shared/{0}.vbhtml" 32 }; 33 MasterLocationFormats = new[] 34 { 35 "~/Views/{1}/{0}.cshtml", 36 "~/Views/{1}/{0}.vbhtml", 37 "~/Views/Shared/{0}.cshtml", 38 "~/Views/Shared/{0}.vbhtml" 39 }; 40 PartialViewLocationFormats = new[] 41 { 42 "~/Views/{1}/{0}.cshtml", 43 "~/Views/{1}/{0}.vbhtml", 44 "~/Views/Shared/{0}.cshtml", 45 "~/Views/Shared/{0}.vbhtml" 46 }; 47 48 FileExtensions = new[] 49 { 50 "cshtml", 51 "vbhtml", 52 }; 53 }
所有的定址路徑都被格式化了,是不是很眼熟呢,關於這裡為啥用陣列而不用List,個人覺得,陣列的定址效率要更高些,遍歷速度更快。
好了,找了“罪魁禍首”,就好好地調教一個,讓它乖乖聽話,小樣讓去哪就去哪裡。
1 /// <summary> 2 /// razor檢視引擎擴充套件 3 /// </summary> 4 public class CustomerViewEngine : RazorViewEngine 5 { 6 /// <summary> 7 /// 可以分開部署不同語種 8 /// </summary> 9 /// <param name="engineName"></param> 10 public CustomerViewEngine(string engineName) 11 { 12 base.ViewLocationFormats = new[] 13 { 14 "~/Views" + engineName + "/{1}/{0}.cshtml", 15 "~/Views" + engineName + "/Shared/{0}.cshtml" 16 }; 17 18 base.PartialViewLocationFormats = new[] 19 { 20 "~/Views" + engineName + "/{1}/{0}.cshtml", 21 "~/Views" + engineName + "/Shared/{0}.cshtml" 22 }; 23 24 base.AreaViewLocationFormats = new[] 25 { 26 "~Areas/{2}/Views" + engineName + "/{1}/{0}.cshtml", 27 "~Areas/{2}/Views" + engineName + "/Shared/{0}.cshtml" 28 }; 29 30 base.AreaPartialViewLocationFormats = new[] 31 { 32 "~Areas/{2}/Views" + engineName + "/{1}/{0}.cshtml", 33 "~Areas/{2}/Views" + engineName + "/Shared/{0}.cshtml" 34 }; 35 } 36 37 public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) 38 { 39 this.SetEngine(controllerContext); 40 return base.FindView(controllerContext, viewName, masterName, useCache); 41 } 42 43 public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) 44 { 45 this.SetEngine(controllerContext); 46 return base.FindPartialView(controllerContext, partialViewName, useCache); 47 } 48 49 /// <summary> 50 /// 根據條件自行設定,目前是chrome瀏覽器就展示預設的 51 /// 不是chrome瀏覽器的話就展示/Themes/Eleven下的 52 /// 可以直接測試是移動端還是pc端 53 /// 然後寫入cookie 54 /// </summary> 55 private void SetEngine(ControllerContext controllerContext) 56 { 57 string engineName = "/Themes/Eleven"; 58 if (controllerContext.HttpContext.Request.UserAgent.IndexOf("Chrome/65") >= 0) 59 { 60 engineName = null; 61 } 62 63 //if (controllerContext.HttpContext.Request.IsMobile())//檢測是不是移動端 64 //{ 65 // engineName = null; 66 //} 67 68 base.ViewLocationFormats = new[] 69 { 70 "~/Views" + engineName + "/{1}/{0}.cshtml", 71 "~/Views" + engineName + "/Shared/{0}.cshtml" 72 }; 73 74 base.PartialViewLocationFormats = new[] 75 { 76 "~/Views" + engineName + "/{1}/{0}.cshtml", 77 "~/Views" + engineName + "/Shared/{0}.cshtml" 78 }; 79 80 base.AreaViewLocationFormats = new[] 81 { 82 "~Areas/{2}/Views" + engineName + "/{1}/{0}.cshtml", 83 "~Areas/{2}/Views" + engineName + "/Shared/{0}.cshtml" 84 }; 85 86 base.AreaPartialViewLocationFormats = new[] 87 { 88 "~Areas/{2}/Views" + engineName + "/{1}/{0}.cshtml", 89 "~Areas/{2}/Views" + engineName + "/Shared/{0}.cshtml" 90 }; 91 }
接下去就很簡單了,只需要把原來的檢視引擎清空,載入自己的檢視引擎就可以了
1 protected void Application_Start() 2 { 3 AreaRegistration.RegisterAllAreas(); 4 5 ViewEngines.Engines.Clear(); 6 7 ViewEngines.Engines.Add(new CustomViewEngine()); 8 9 RegisterGlobalFilters(GlobalFilters.Filters); 10 RegisterRoutes(RouteTable.Routes); 11 }