在《[设计篇]》篇中我们通过对View引擎的总体介绍讲述了从ViewResult的创建到View呈现的原理,为了让读者对View引擎及其View呈现机制具有一个深刻的认识,我们自定义一个简单的用于呈现静态HTML的StaticFileViewEngine。在一个通过Visual Studio的ASP.NET MVC项目模板创建的空Web应用中,我们定义了如下一个针对于静态HTML内容呈现的自定义StaticFileView。StaticFileView实现了IView接口,在实现的Render方法中读取制定文件的内容写入作为参数的TextWriter。 [本文已经同步到《How ASP.NET MVC Works?》中]

   1: public class StaticFileView:IView
   2: {
   3:     public string FileName { get; private set; }
   4:     public StaticFileView(string fileName)
   5:     {
   6:         this.FileName = fileName;
   7:     }
   8:     public void Render(ViewContext viewContext, TextWriter writer)
   9:     {
  10:         byte[] buffer;
  11:         using (FileStream fs = new FileStream(this.FileName, FileMode.Open))
  12:         { 
  13:             buffer = new byte[fs.Length];
  14:             fs.Read(buffer, 0, buffer.Length);
  15:         }
  16:         writer.Write(Encoding.UTF8.GetString(buffer));
  17:     }
  18: }

由于StaticFileView中定义的内容完全是静态的,所以缓存显得很有必要。我们只需要基于Controller和View名称对View实施缓存,为此我们定义了如下一个作为Key的数据类型ViewEngineResultCacheKey。

   1: internal class ViewEngineResultCacheKey
   2: {
   3:     public string ControllerName { get; private set; }
   4:     public string ViewName { get; private set; }
   5:  
   6:     public ViewEngineResultCacheKey(string controllerName, string viewName)
   7:     {
   8:         this.ControllerName = controllerName ?? string.Empty;
   9:         this.ViewName = viewName ?? string.Empty;
  10:     }
  11:     public override int GetHashCode()
  12:     {
  13:         return this.ControllerName.ToLower().GetHashCode() ^ this.ViewName.ToLower().GetHashCode();
  14:     }
  15:  
  16:     public override bool Equals(object obj)
  17:     {
  18:         ViewEngineResultCacheKey key = obj as ViewEngineResultCacheKey;
  19:         if (null == key)
  20:         {
  21:             return false;
  22:         }
  23:         return key.GetHashCode() == this.GetHashCode();
  24:     }
  25: }

具有如下定义的StaticFileViewEngine代表StaticFileView对应的ViewEngine。我们通过一个字典类型的字段viewEngineResults作为对ViewEngineResult的缓存,而View的获取操作最终实现在InternalFindView方法中。通过StaticFileView表示的View定义在一个以View名称作为文件名的文本文件中,该文件的扩展名为.shtml(Static HTML)。

   1: public class StaticFileViewEngine : IViewEngine
   2: {
   3:     private Dictionary<ViewEngineResultCacheKey, ViewEngineResult> viewEngineResults = new Dictionary<ViewEngineResultCacheKey, ViewEngineResult>();
   4:     private object syncHelper = new object();
   5:     public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
   6:     {
   7:         return this.FindView(controllerContext, partialViewName, null, useCache);
   8:     }
   9:  
  10:     public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
  11:     {
  12:         string controllerName = controllerContext.RouteData.GetRequiredString("controller");
  13:         ViewEngineResultCacheKey key = new ViewEngineResultCacheKey(controllerName, viewName);
  14:         ViewEngineResult result;
  15:         if (!useCache)
  16:         { 
  17:             result = InternalFindView(controllerContext, viewName, controllerName);
  18:             viewEngineResults[key] = result;
  19:             return result;
  20:         }
  21:         if(viewEngineResults.TryGetValue(key, out result))
  22:         {
  23:             return result;
  24:         }
  25:         lock (syncHelper)
  26:         {
  27:             if (viewEngineResults.TryGetValue(key, out result))
  28:             {
  29:                 return result;
  30:             }
  31:  
  32:             result = InternalFindView(controllerContext, viewName, controllerName);
  33:             viewEngineResults[key] = result;
  34:             return result;
  35:         }             
  36:     }
  37:  
  38:     private ViewEngineResult InternalFindView(ControllerContext controllerContext, string viewName, string controllerName)
  39:     {
  40:         string[] searchLocations = new string[]
  41:         {
  42:             string.Format( "~/views/{0}/{1}.shtml", controllerName, viewName),
  43:             string.Format( "~/views/Shared/{0}.shtml", viewName)
  44:         };
  45:  
  46:         string fileName = controllerContext.HttpContext.Request.MapPath(searchLocations[0]);
  47:         if (File.Exists(fileName))
  48:         {
  49:             return new ViewEngineResult(new StaticFileView(fileName), this);
  50:         }
  51:         fileName = string.Format(@"\views\Shared\{0}.shtml", viewName);
  52:         if (File.Exists(fileName))
  53:         {
  54:             return new ViewEngineResult(new StaticFileView(fileName), this);
  55:         }
  56:         return new ViewEngineResult(searchLocations);
  57:     }
  58:  
  59:     public void ReleaseView(ControllerContext controllerContext, IView view)
  60:     { }
  61: }

在InternalFindView中,我们先在“~/Views/{ControllerName}/”目录下寻找View文件,如果不存在则在“~/Views/Shared/”寻找。如果对应View文件被找到,则以此创建一个StaticFileView对象,并最终返回封装该View对象的ViewEngineResult。如果目标View文件找不到,则根据基于这两个目录的搜寻地址列表创建并返回对应的ViewEngineResult。 现在我们在Global.asax通过如下的代码对自定义的StaticFileViewEngine进行注册,我们将创建的StaticFileViewEngine作为第一个使用的ViewEngine。

   1: public class MvcApplication : System.Web.HttpApplication
   2: {
   3:     protected void Application_Start()
   4:     {
   5:         //其他操作
   6:         ViewEngines.Engines.Insert(0, new StaticFileViewEngine());
   7:     }
   8: }

然后我们定义了如下一个简单的HomeController,Action方法ShowNonExistentView中通过调用View方法呈现一个不存在的View(NonExistentView),而ShowStaticFileView方法则将对应的StaticFileView呈现出来。

   1: public class HomeController : Controller
   2: {
   3:     public ActionResult ShowNonExistentView()
   4:     {
   5:         return View("NonExistentView");
   6:     }
   7:  
   8:     public ActionResult ShowStaticFileView()
   9:     {
  10:         return View();
  11:     }
  12: }

我们为Action方法ShowStaticFileView创建一个StaticFileView类型的View文件ShowStaticFileView.shtml(该View文件保存在“~/Views/Home”目录下,扩展名不是.cshtml,而是shtml),其内容就是如下一段完整的HTML。

   1: <!DOCTYPE html>
   2: <html>
   3:     <head>
   4:         <title>Static File View</title>
   5:     </head>
   6:     <body>
   7:         这是一个自定义的StaticFileView!
   8:     </body>
   9: </html>

现在运行我们的程序,在浏览器中输入相应的地址访问Action方法ShowNonExistentView,会得到如下图所示的输出结果。图中列出的View搜寻位置列表中的前两项正是我们自定义的StaticFileViewEngine寻找对应.shtml文件的两个地址。

如果我们改变浏览器的地址来访问另一个Action方法ShowStaticFileView,会呈现出如下图所示的输出结果,不难看出呈现出来的正是定义在ShowStaticFileView.shtml中的HTML。

ASP.NET MVC的View是如何被呈现出来的?[设计篇]
ASP.NET MVC的View是如何被呈现出来的?[实例篇]

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
原文链接

ASP.NET MVC的View是如何呈现出来的[实例篇]相关推荐

  1. Visual Studio 2015打开ASP.NET MVC的View提示“Object reference not set to an instance of an object“错误的解决方案

    Visual Studio 2015打开ASP.NET MVC的View提示"Object reference not set to an instance of an object&quo ...

  2. ASP.NET MVC 之 View 测试

    项目又出问题了!手贱了一下,使用某个工具整理了一下 View 中的内容,不经意之间,将 View 的输出中大小写不小心搞错了,导致输出的内容没有办法正常解析. 这种问题太隐蔽了,下次再遇到怎么办呢? ...

  3. ASP.NET MVC one view bind many model

    一.自定义视图模型 model.cs public class AorBvm{public List<Role> GetRole { get; set; }public List<C ...

  4. Asp.net mvc 强类型View

    上节对ViewData和ViewBag进行比较和实验,并总结了二者的缺点,ViewData和ViewBag引起的问题根源就因为数据类型,参数值的数据类型被封装在ViewData中,即对象,微软针对上述 ...

  5. ASP.NET MVC 入门5、View与ViewData

    本系列文章基于ASP.NET MVC Preview5. view在MVC模式中与用户进行最直接的接触,它负责数据的呈现.这里要注意一点就是,view只是负责数据的呈现,所以我们应该要尽量让view中 ...

  6. 【译】使用自定义ViewHelper来简化Asp.net MVC view的开发------part1

    从开发者的角度来看,创建Asp.net MVC的View是一件很爽的事,因为你可以精确控制最终生成的HTML.具有讽刺意味的是不得不写出每一行HTML代码同时也是Asp.net MVC的View中让人 ...

  7. ASP.NET MVC中controller和view相互传值的方式

    ASP.NET MVC中Controller向view传值的方式: ViewBag.ViewData.TempData 单个值的传递 Json 匿名类型 ExpandoObject Cookie Vi ...

  8. MVC 各种传值方式 ASP.NET MVC view与controller传值方式

    MVC 各种传值方式 ViewData传值. HomeController.cs Co de: public ActionResult Index() {       ViewData["T ...

  9. How ASP.NET MVC Works?

    一.ASP.NET + MVC IIS与ASP.NET管道 MVC.MVP以及Model2[上篇] MVC.MVP以及Model2[下篇] ASP.NET MVC是如何运行的[1]: 建立在" ...

最新文章

  1. 三步走——带你打造一份完美的数据科学家简历|(附件有PPT福利)
  2. C# 获取电脑的网络连接状态
  3. Linux学习第十篇之用户管理命令useradd、passwd、who、w
  4. 那些年我们踩过的坑-NSTimer
  5. linux中zip文件编码错误,如何避免在 Linux 下解压 zip 文件时可能出现的乱码情况...
  6. C 基础——指针函数与函数指针
  7. 正则表达式学习(一)
  8. js文字展示各种滚动效果
  9. 计算机win7的后缀名怎么显示,win7显示文件后缀名怎么显示?win7显示文件后缀
  10. android加速度传感器 源码,Android项目源码利用加速度传感器实现计步
  11. 职场修炼圣经-和繁重的工作一起修行
  12. Android wifi开发介绍
  13. login主页面+接口+依赖
  14. 汽车引擎布局概念介绍(前置/中置/后置引擎的由来)
  15. 多商户商城系统功能拆解13讲-平台端会员管理
  16. 由 serverAdd.sin_addr.s_addr 引发的思考
  17. 《20个月赚30个亿-陈士骏自传》读后感
  18. 简单两步, 搭建全平台私有同步网盘
  19. 《REWORK》启示录 可笑的求职简历——可笑的求职经历
  20. 【C语言项目设计】小学生系统测验

热门文章

  1. 自制MVC之工具类插件一
  2. DNS转发器引起的CDN访问缓慢
  3. 柳传志大声对iphone说:这是在中国!
  4. MySQL之查看数据库编码
  5. 现代操作系统-虚拟化和云
  6. Linux下通过PXE服务器安装Linux系统
  7. win7笔记本设置wifi热点
  8. AC日记——信息传递 洛谷 P2661 (tarjan求环)
  9. Java数据库——CallableStatement接口
  10. 用原始方法解析复杂字符串,json一定要用JsonMapper么?