ASP.NET MVC的View是如何呈现出来的[实例篇]
2012-08-23 07:58
881 查看
在《[设计篇]》篇中我们通过对View引擎的总体介绍讲述了从ViewResult的创建到View呈现的原理,为了让读者对View引擎及其View呈现机制具有一个深刻的认识,我们自定义一个简单的用于呈现静态HTML的StaticFileViewEngine。在一个通过Visual Studio的ASP.NET MVC项目模板创建的空Web应用中,我们定义了如下一个针对于静态HTML内容呈现的自定义StaticFileView。StaticFileView实现了IView接口,在实现的Render方法中读取制定文件的内容写入作为参数的TextWriter。 [本文已经同步到《How ASP.NET MVC Works?》中]
[/code]
由于StaticFileView中定义的内容完全是静态的,所以缓存显得很有必要。我们只需要基于Controller和View名称对View实施缓存,为此我们定义了如下一个作为Key的数据类型ViewEngineResultCacheKey。
[/code]
具有如下定义的StaticFileViewEngine代表StaticFileView对应的ViewEngine。我们通过一个字典类型的字段viewEngineResults作为对ViewEngineResult的缓存,而View的获取操作最终实现在InternalFindView方法中。通过StaticFileView表示的View定义在一个以View名称作为文件名的文本文件中,该文件的扩展名为.shtml(Static HTML)。
[/code]
在InternalFindView中,我们先在“~/Views/{ControllerName}/”目录下寻找View文件,如果不存在则在“~/Views/Shared/”寻找。如果对应View文件被找到,则以此创建一个StaticFileView对象,并最终返回封装该View对象的ViewEngineResult。如果目标View文件找不到,则根据基于这两个目录的搜寻地址列表创建并返回对应的ViewEngineResult。 现在我们在Global.asax通过如下的代码对自定义的StaticFileViewEngine进行注册,我们将创建的StaticFileViewEngine作为第一个使用的ViewEngine。
[/code]
然后我们定义了如下一个简单的HomeController,Action方法ShowNonExistentView中通过调用View方法呈现一个不存在的View(NonExistentView),而ShowStaticFileView方法则将对应的StaticFileView呈现出来。
[/code]
我们为Action方法ShowStaticFileView创建一个StaticFileView类型的View文件ShowStaticFileView.shtml(该View文件保存在“~/Views/Home”目录下,扩展名不是.cshtml,而是shtml),其内容就是如下一段完整的HTML。
[/code]
现在运行我们的程序,在浏览器中输入相应的地址访问Action方法ShowNonExistentView,会得到如下图所示的输出结果。图中列出的View搜寻位置列表中的前两项正是我们自定义的StaticFileViewEngine寻找对应.shtml文件的两个地址。
如果我们改变浏览器的地址来访问另一个Action方法ShowStaticFileView,会呈现出如下图所示的输出结果,不难看出呈现出来的正是定义在ShowStaticFileView.shtml中的HTML。
ASP.NET MVC的View是如何被呈现出来的?[设计篇]
ASP.NET MVC的View是如何被呈现出来的?[实例篇]
[code] public class StaticFileView:IView { public string FileName { get; private set; } public StaticFileView(string fileName) { this.FileName = fileName; } public void Render(ViewContext viewContext, TextWriter writer) { byte[] buffer; using (FileStream fs = new FileStream(this.FileName, FileMode.Open)) { buffer = new byte[fs.Length]; fs.Read(buffer, 0, buffer.Length); } writer.Write(Encoding.UTF8.GetString(buffer)); } }
[/code]
由于StaticFileView中定义的内容完全是静态的,所以缓存显得很有必要。我们只需要基于Controller和View名称对View实施缓存,为此我们定义了如下一个作为Key的数据类型ViewEngineResultCacheKey。
[code] internal class ViewEngineResultCacheKey { public string ControllerName { get; private set; } public string ViewName { get; private set; } public ViewEngineResultCacheKey(string controllerName, string viewName) { this.ControllerName = controllerName ?? string.Empty; this.ViewName = viewName ?? string.Empty; } public override int GetHashCode() { return this.ControllerName.ToLower().GetHashCode() ^ this.ViewName.ToLower().GetHashCode(); } public override bool Equals(object obj) { ViewEngineResultCacheKey key = obj as ViewEngineResultCacheKey; if (null == key) { return false; } return key.GetHashCode() == this.GetHashCode(); } }
[/code]
具有如下定义的StaticFileViewEngine代表StaticFileView对应的ViewEngine。我们通过一个字典类型的字段viewEngineResults作为对ViewEngineResult的缓存,而View的获取操作最终实现在InternalFindView方法中。通过StaticFileView表示的View定义在一个以View名称作为文件名的文本文件中,该文件的扩展名为.shtml(Static HTML)。
[code] public class StaticFileViewEngine : IViewEngine { private Dictionary<ViewEngineResultCacheKey, ViewEngineResult> viewEngineResults = new Dictionary<ViewEngineResultCacheKey, ViewEngineResult>(); private object syncHelper = new object(); public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache) { return this.FindView(controllerContext, partialViewName, null, useCache); } public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache) { string controllerName = controllerContext.RouteData.GetRequiredString("controller"); ViewEngineResultCacheKey key = new ViewEngineResultCacheKey(controllerName, viewName); ViewEngineResult result; if (!useCache) { result = InternalFindView(controllerContext, viewName, controllerName); viewEngineResults[key] = result; return result; } if(viewEngineResults.TryGetValue(key, out result)) { return result; } lock (syncHelper) { if (viewEngineResults.TryGetValue(key, out result)) { return result; } result = InternalFindView(controllerContext, viewName, controllerName); viewEngineResults[key] = result; return result; } } private ViewEngineResult InternalFindView(ControllerContext controllerContext, string viewName, string controllerName) { string[] searchLocations = new string[] { string.Format( "~/views/{0}/{1}.shtml", controllerName, viewName), string.Format( "~/views/Shared/{0}.shtml", viewName) }; string fileName = controllerContext.HttpContext.Request.MapPath(searchLocations[0]); if (File.Exists(fileName)) { return new ViewEngineResult(new StaticFileView(fileName), this); } fileName = string.Format(@"\views\Shared\{0}.shtml", viewName); if (File.Exists(fileName)) { return new ViewEngineResult(new StaticFileView(fileName), this); } return new ViewEngineResult(searchLocations); } public void ReleaseView(ControllerContext controllerContext, IView view) { } }
[/code]
在InternalFindView中,我们先在“~/Views/{ControllerName}/”目录下寻找View文件,如果不存在则在“~/Views/Shared/”寻找。如果对应View文件被找到,则以此创建一个StaticFileView对象,并最终返回封装该View对象的ViewEngineResult。如果目标View文件找不到,则根据基于这两个目录的搜寻地址列表创建并返回对应的ViewEngineResult。 现在我们在Global.asax通过如下的代码对自定义的StaticFileViewEngine进行注册,我们将创建的StaticFileViewEngine作为第一个使用的ViewEngine。
[code] public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { //其他操作 ViewEngines.Engines.Insert(0, new StaticFileViewEngine()); } }
[/code]
然后我们定义了如下一个简单的HomeController,Action方法ShowNonExistentView中通过调用View方法呈现一个不存在的View(NonExistentView),而ShowStaticFileView方法则将对应的StaticFileView呈现出来。
[code] public class HomeController : Controller { public ActionResult ShowNonExistentView() { return View("NonExistentView"); } public ActionResult ShowStaticFileView() { return View(); } }
[/code]
我们为Action方法ShowStaticFileView创建一个StaticFileView类型的View文件ShowStaticFileView.shtml(该View文件保存在“~/Views/Home”目录下,扩展名不是.cshtml,而是shtml),其内容就是如下一段完整的HTML。
[code] <!DOCTYPE html> <html> <head> <title>Static File View</title> </head> <body> 这是一个自定义的StaticFileView! </body> </html>
[/code]
现在运行我们的程序,在浏览器中输入相应的地址访问Action方法ShowNonExistentView,会得到如下图所示的输出结果。图中列出的View搜寻位置列表中的前两项正是我们自定义的StaticFileViewEngine寻找对应.shtml文件的两个地址。
如果我们改变浏览器的地址来访问另一个Action方法ShowStaticFileView,会呈现出如下图所示的输出结果,不难看出呈现出来的正是定义在ShowStaticFileView.shtml中的HTML。
ASP.NET MVC的View是如何被呈现出来的?[设计篇]
ASP.NET MVC的View是如何被呈现出来的?[实例篇]
相关文章推荐
- ASP.NET MVC的View是如何呈现出来的[实例篇]
- ASP.NET MVC的View是如何被呈现出来的?[设计篇]
- ASP.NET MVC的View是如何被呈现出来的?[设计篇]
- ASP.NET MVC的View是如何被呈现出来的?
- ASP.NET MVC学习笔记-ViewEngine 深入解析与应用实例
- [ASP.NET MVC]视图是如何呈现的 (续)
- asp.net MVC 如何在aspx页面的head标签里输出Key为常量的ViewData呢?
- 从零开始学习 ASP.NET MVC 1.0 (五) ViewEngine 深入解析与应用实例
- Asp.Net MVC中不指定View Name时如何返回ActionResult的
- asp.net mvc 如何获取ViewBag里面的动态属性
- 白话ASP.NET MVC之三:Controller是如何解析出来的
- 从零开始学习 ASP.NET MVC 1.0 (五) ViewEngine 深入解析与应用实例
- asp.net mvc源码分析-Controllerl篇 如何创建Controller实例
- 从零开始学习 ASP.NET MVC 1.0 (五) ViewEngine 深入解析与应用实例
- ASP.NET MVC中如何以ajax的方式在View和Action中传递数据
- 白话ASP.NET MVC之三:Controller是如何解析出来的
- 如何创建Asp.net MVC ViewModel
- Asp.Net MVC<八>:View的呈现
- 从零开始学习 ASP.NET MVC 1.0 (五) ViewEngine 深入解析与应用实例 【转】
- Asp.net mvc中controller与view间的如何传递数据