閱讀62 返回首頁    go 阿裏雲 go 技術社區[雲棲]


ASP.NET MVC的Razor引擎:RazorViewEngine

基於Web Form引擎的WebFormViewEngine和針對Razor引擎的RazorViewEngine都是抽象類型BuildManagerViewEngine的子類,而後者又繼承自VirtualPathProviderViewEngine。在這裏我們僅僅對實現在RazorViewEngine中View獲取的邏輯進行簡單介紹。由於Razor引擎下的View通過RazorView對象來表示,而RazorView通過View文件的虛擬路徑來構建,所以RazorViewEngine的View獲取機製在於根據當前上下文找到與指定View名稱相匹配的View文件(.cshtml或者.vbhtml文件),然後根據該 View文件的虛擬路徑創建一個RazorView對象並最終封裝成ViewEngineResult對象返回。[本文已經同步到《How ASP.NET MVC Works?》中]

實現在RazorViewEngine中的目標View文件的搜索是根據一個預定義順序進行的。如果當前請求不是針對某個Area的,下麵的列表代表了View的搜索順序:

  • ~/Views/{ControllerName}/{ViewName}.cshtml
  • ~/Views/{ControllerName}/{ViewName}.vbhtml
  • ~/Views/Shared/{ViewName}.cshtml
  • ~/Views/Shared/{ViewName}.vbhtml

對於針對某個Area的請求,RazorViewEngine會先按照如下的順序對目標View進行搜索。如果在這個列表中沒有成功找到目標View文件,會繼續按照上麵的屬性進行搜索。

  • ~/Areas/{AreaName}/Views/{ControllerName}/{ViewName}.cshtml
  • ~/Areas/{AreaName}/Views/{ControllerName}/{ViewName}.vbhtml
  • ~/Areas/{AreaName}/Views/ Shared /{ViewName}.cshtml
  • ~/Areas/{AreaName}/Views/ Shared /{ViewName}.vbhtml

如果按照上麵的搜索順序依然找不目標View文件,RazorViewEngine會根據這個列表創建並返回一個ViewEngineResult對象。這裏介紹的View搜索機製不僅僅應用於普通的View文件,還應用於Partial View和布局文件的搜索。

ViewEngine不僅僅通過FindView/FindPartialView根據當前上下文獲取指定的View,還通過ReleaseView對指定的View進行釋放回收操作。ReleaseView方法在RazorViewEngine的實現很簡單,如果指定的View對象的類型實現IDispose接口,它會直接調用其Dispose方法。下圖所示的UML體現了Razor引擎涉及的相關類型/接口以及它們之間的相互關係。

image

在《ASP.NET MVC的Razor引擎:RazorView》一文中我們創建了一個用於模擬RazorView的SimpleRazorView,現在我們為它創建一個對應的RazorViewEngine,我們直接在該實例項目中添加如下一個SimpleRazorViewEngine。
   1: public class SimpleRazorViewEngine: IViewEngine
   2: {
   3:     private string[] viewLocationFormats = new string[] { 
   4:         "~/Views/{1}/{0}.cshtml", 
   5:         "~/Views/{1}/{0}.vbhtml", 
   6:         "~/Views/Shared/{0}.cshtml", 
   7:         "~/Views/Shared/{0}.vbhtml" };
   8:     private string[] areaViewLocationFormats = new string[] { 
   9:         "~/Areas/{2}/Views/{1}/{0}.cshtml", 
  10:         "~/Areas/{2}/Views/{1}/{0}.vbhtml", 
  11:         "~/Areas/{2}/Views/Shared/{0}.cshtml", 
  12:         "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
  13:     
  14:     public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
  15:     {
  16:         return FindView(controllerContext, partialViewName, null, useCache);
  17:     }
  18:  
  19:     public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
  20:     {
  21:         string controllerName = controllerContext.RouteData.GetRequiredString("controller");
  22:         object areaName;
  23:         List<string> viewLocations = new List<string>();
  24:         Array.ForEach(viewLocationFormats, format => viewLocations.Add(string.Format(format, viewName, controllerName)));
  25:         if (controllerContext.RouteData.Values.TryGetValue("area", out areaName))
  26:         {
  27:             Array.ForEach(areaViewLocationFormats, format=>viewLocations.Add(string.Format(format,viewName,controllerName, areaName)));
  28:         }
  29:         foreach (string viewLocation in viewLocations)
  30:         {
  31:             string filePath = controllerContext.HttpContext.Request.MapPath(viewLocation);
  32:             if (File.Exists(filePath))
  33:             {
  34:                 return new ViewEngineResult(new SimpleRazorView(viewLocation), 
  35:                     this);
  36:             }
  37:         }
  38:         return new ViewEngineResult(viewLocations);
  39:     }
  40:  
  41:     public void ReleaseView(ControllerContext controllerContext, IView view)
  42:     {
  43:         IDisposable disposable = view as IDisposable;
  44:         if (null != disposable)
  45:         {
  46:             disposable.Dispose();
  47:         }
  48:     }
  49: }
我們完全按照上麵介紹的路徑順序搜索指定的目標View。簡單起見,我們在對目標View進行搜索時忽略了指定的布局文件名和對ViewEngineResult的緩存。這個自定義的SimpleRazorViewEngine在Global.asax中通過如下的代碼對進行注冊。
   1: public class MvcApplication : System.Web.HttpApplication
   2: {
   3:     protected void Application_Start()
   4:     {
   5:         //其他操作
   6:         ViewEngines.Engines.Clear();
   7:         ViewEngines.Engines.Add(new SimpleRazorViewEngine());
   8:     }
   9: }

我們定義了如下一個HomeController:

   1: public class HomeController : Controller
   2: {
   3:     public ActionResult Index()
   4:     {
   5:         Contact contact = new Contact 
   6:         { 
   7:              Name             = "張三", 
   8:              PhoneNo          = "123456789", 
   9:              EmailAddress     = "zhangsan@gmail.com" 
  10:         };
  11:         return View(contact);
  12:     }
  13: }
  14:  
  15: public class Contact
  16: {
  17:     [DisplayName("姓名")]
  18:     public string Name { get; set; }
  19:  
  20:     [DisplayName("電話號碼")]
  21:     public string PhoneNo { get; set; }
  22:  
  23:     [DisplayName("電子郵箱地址")]
  24:     public string EmailAddress { get; set; }
  25: }

我們的View(“~/Views/Home/Index.cshtml”)很簡單。如下麵的代碼片斷所示,這是一個Model類型為Contact的強類型View,在該View中我們直接調用HtmlHelper<TModel>的擴展方法EditorForModel將作為Model的Contact對象以編輯模式呈現在一個表單之中。

   1: @model Contact
   2: @{
   3:     ViewBag.Title = Model.Name;
   4: }
   5:  
   6: @using (Html.BeginForm())
   7: { 
   8:     @Html.EditorForModel()
   9:     <input type="submit" value="保存" />
  10: }

為了驗證我們自定義的SimpleRazorView對布局文件和_ViewStart頁麵的支持,我們在“~/Views/Shared/”目錄下定義了如下一個名為“_Layout.cshtml”的布局文件。布局文件的設置通過定義在“~/Views/”目錄下具有如下定義的“_ViewStart.cshtml”文件來指定。

   1: _Layout.cshtml:
   2: <html>
   3:     <head>
   4:         <title>@ViewBag.Title </title>
   5:     </head>    
   6:     <body>
   7:         <h3>編輯聯係人信息</h3>
   8:         @RenderBody()
   9:     </body>
  10: </html>
  11:  
  12: _ViewStart.cshtml:
  13: @{
  14:     Layout = "~/Views/Shared/_Layout.cshtml";
  15: }

運行我們的程序後會在瀏覽器中呈現如下圖所示的輸出結果,可以看出這和我們直接在Action方法Index中返回一個ViewResult對象沒有什麼不同。

image

ASP.NET MVC的Razor引擎:View編譯原理
ASP.NET MVC的Razor引擎:RazorView
ASP.NET MVC的Razor引擎:IoC在View激活過程中的應用
ASP.NET MVC的Razor引擎:RazorViewEngine


作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-25 16:33:47

  上一篇:go  ASP.NET MVC的Razor引擎:IoC在View激活過程中的應用
  下一篇:go  如何在調用WCF服務之前彈出一個確認對話框?