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引擎涉及的相关类型/接口以及它们之间的相互关系。

在《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对象没有什么不同。

ASP.NET MVC的Razor引擎:View编译原理 ASP.NET MVC的Razor引擎:RazorView ASP.NET MVC的Razor引擎:IoC在View激活过程中的应用 ASP.NET MVC的Razor引擎:RazorViewEngine

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Golang语言社区

go语言实现通过FTP库自动上传web日志

因为平时管理的web服务器都是VM服务器,为节省硬盘空间,一般给虚拟机分配的硬盘空间都比较小,只有8G,因为,保存不了多少日志,所以每天都需要把每台WEB日志转...

4393
来自专栏张高兴的博客

张高兴的 UWP 开发笔记:汉堡菜单进阶

4446
来自专栏我的小碗汤

用go语言爬取珍爱网 | 第一回

在返回体里可以找到<meta charset="gbk" /> 即编码为gbk,而go默认编码为utf-8,所以就会出现乱码。接下来用第三方库将其编码格式转为u...

642
来自专栏数说工作室

【SAS Says】基础篇:ODS的使用(上)

你不可能总是在SAS上查看结果,如果结果很多你要一一的讲结果复制到word中去,有ODS就好啦!它可以把SAS的结果直接输出到word、PDF等文件中。 5.1...

7166
来自专栏一个爱瞎折腾的程序猿

asp.net mvc项目自定义区域

2.Code:在Global.asax中添加注册区域-->AreaRegistration.RegisterAllAreas();

1661
来自专栏大内老A

MVVM(Knockout.js)的新尝试:多个Page,一个ViewModel

对于面向数据的Web应用来说,MVVM模式是一项不错的选择,它借助JS框架提供的“绑定”机制是我们无需过多关注UI(HTML)的细节,只需要操作绑定的数据源。M...

3629
来自专栏企鹅号快讯

使用多个Python库开发网页爬虫(一)

21CTO社区导读:在本篇文章里,我们将讨论使用Python进行网页抓取以及如何引用多个库,如Beautifusoup,Selenium库,以及JavaScri...

4056
来自专栏非著名程序员

Attr、Style和Theme详解

? 前言 这三个概念贯穿Android框架的方方面面,是Android程序设计中很重要的一环,理解它们,并能学以致用,不但可以让你的代码变得简洁明了,还可以...

2339
来自专栏ASP.NET MVC5 后台权限管理系统

ASP.NET MVC5+EF6+EasyUI 后台管理系统(57)-插件---ueditor使用

目录: 前言 开发环境 知识点 初始使用 自定义工具栏 设置和读取编辑器内容 文件上传 ueditor加水印 -------------------------...

3735
来自专栏林德熙的博客

win10 uwp 按下等待按钮

我们需要一个值让我们知道是不是已经完成了后台,按钮可以按下,在按下时,自动让按钮IsEnable为false。

682

扫码关注云+社区

领取腾讯云代金券