zoukankan      html  css  js  c++  java
  • ASP.NET MVC的View是如何呈现出来的[实例篇]

    在《[设计篇]》篇中我们通过对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文件的两个地址。

    image

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

    image

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

    作者:Artech
    出处:http://artech.cnblogs.com/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    CSS优化压缩
    CSS clear both清除浮动总结
    jQuery下拉列表二级联动插件
    网站靠什么赚钱?
    程序员到高级程序员的职业生涯
    ie数组不支持indexOf 方法解决
    jquery中的each方法,$.each this.each $.fn.each
    css3写出0.5px的边框
    js 自己创建ready多个可以依次加载
    javascript中的removeEventListener失效问题
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2652021.html
Copyright © 2011-2022 走看看