题外话

一周之前写的《仅此一文让你明白ASP.NET MVC原理》受到了广大学习ASP.NET MVC同学的欢迎,于是下定决心准备把它写成一个系列,以满足更多求知若渴的同学们。蒋金楠老师已经在他的《ASP.NET MVC 4框架揭秘》书中已经做了很深入的讲解。我总不能把他的文章抄下来放给大家。那大家还不如看他的博客去。我想做的就是给大家提供基于图形化、直观、系统、简洁的理解。部分内容想深入理解的同学,还是花点银子去买本他的书,非常值得一看(绝非打广告⊙﹏⊙‖∣)。

有些人要问题,为什么我要学框架?这里我简单说一下,深入理解一个框架,给你带来最直接的好处:

  1. 使用框架时,遇到问题可以快速定位,并知道如何解决;
  2. 当框架中有些功能用着不爽时,你可以自由扩展,实现你想要的操作,甚至可以拿到源码直接修改;
  3. 想成为框架师的必经之路;
  4. 提取框架中的优秀代码和思想,为己所用;

更多好处,你可以自己去体会,有兴趣的可以看一下asp.net中 mvc部分的源码:http://aspnetwebstack.codeplex.com/

本文目的

上一篇文章是让你明白MVC最核心的两个流程(如果没看过请猛戳这里,只需耽误你打盹的几分钟)。我们接着上篇从ControllerActionInvoker的InvokeAction方法执行Action说起:

  1. //执行Action,并得到ActionResult
  2. ActionResult actionResult = method.Invoke(controllerContext.Controller,
  3. parameters.ToArray()) as ActionResult;
  4.  
  5. //最终ActionResult用HttpResponse将数据传回客户进行显示
  6. actionResult.ExecuteResult(controllerContext);

本文的目的就是让你明白这段代码到底做了哪些事情?MVC中的Controller如何找到View,并进行显示。

开始旅程

先放上与本文相关的类结构图:

此图看起来结构相当复杂,但其实他很简单,给我两分钟,我会让你明白这些鬼东西到底是什么、有什么关系?

先说ActionResult,从图上看,ActionResult是个抽象类,他的子类有很多,比如JsonResult,ContentResult,ViewResult,EmptyResult等等。这个东西大家用MVC的时候常常接触,比如:

  1. public class HomeController:Controller
  2. {
  3. public ActionResult Index()
  4. {
  5. return View();
  6. }
  7.  
  8. public ActionResult GetInfo()
  9. {
  10. ...
  11. return Json(obj);
  12. }
  13.  
  14. public ActionResult GetContent()
  15. {
  16. return Content("test");
  17. }
  18. }

上面三个Action返回分别对应ViewResult、JsonResult、ContentResult,而我图中只画ViewResult,因为它是我们最常用的一个ActionResult,而且是最复杂的一个(因为要负责View的显示)。而看看ContentResult的核心源码,我想简单的大家都笑翻了:

  1. public class ContentResult : ActionResult
  2. {
  3. public string Content { get; set; }
  4.  
  5. public override void ExecuteResult(ControllerContext context)
  6. {
  7. HttpResponseBase response = context.HttpContext.Response;
  8.  
  9. if (Content != null)
  10. {
  11. response.Write(Content);
  12. }
  13. }
  14. }

它的实现和上面我画的结构图完全没有半毛钱关系,直接一个Response.Write输出就完成了。所以我用ViewResult来写本文。

接下来注意ViewEngines这个静态类,看一下它的源码:

  1. public static class ViewEngines
  2. {
  3. private static readonly ViewEngineCollection _engines = new ViewEngineCollection
  4. {
  5. new WebFormViewEngine(),
  6. new RazorViewEngine(),
  7. };
  8.  
  9. public static ViewEngineCollection Engines
  10. {
  11. get { return _engines; }
  12. }
  13. }

只有一个静态只读的ViewEngineCollection类型的成员,初始化时封装了两个视图引擎,WebFormViewEngine和RazorViewEngine?这两个是什么?为了方便大家理解,我们看看RazorViewEngine的源码(WebFormViewEngine基本一样,篇幅限制下面我只用Razor举例):

  1. public class RazorViewEngine : BuildManagerViewEngine
  2. {
  3. internal static readonly string ViewStartFileName = "_ViewStart"; //存储ViewStart模板的
  4.  
  5. public RazorViewEngine(IViewPageActivator viewPageActivator)
  6. : base(viewPageActivator)
  7. {
  8. //这些构造大家应该觉得很亲切
  9. ViewLocationFormats = new[]
  10. {
  11. "~/Views/{1}/{0}.cshtml",
  12. "~/Views/{1}/{0}.vbhtml",
  13. "~/Views/Shared/{0}.cshtml",
  14. "~/Views/Shared/{0}.vbhtml"
  15. };
  16. MasterLocationFormats = new[]
  17. {
  18. "~/Views/{1}/{0}.cshtml",
  19. "~/Views/{1}/{0}.vbhtml",
  20. "~/Views/Shared/{0}.cshtml",
  21. "~/Views/Shared/{0}.vbhtml"
  22. };
  23. PartialViewLocationFormats = new[]
  24. {
  25. "~/Views/{1}/{0}.cshtml",
  26. "~/Views/{1}/{0}.vbhtml",
  27. "~/Views/Shared/{0}.cshtml",
  28. "~/Views/Shared/{0}.vbhtml"
  29. };
  30.  
  31. FileExtensions = new[]
  32. {
  33. "cshtml",
  34. "vbhtml",
  35. };
  36. }
  37.  
  38. protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
  39. {
  40. var view = new RazorView(controllerContext, viewPath,
  41. layoutPath: masterPath, runViewStartPages: true, viewStartFileExtensions: FileExtensions, viewPageActivator: ViewPageActivator)
  42. {
  43. DisplayModeProvider = DisplayModeProvider
  44. };
  45. return view;
  46. }
  47. }

从代码中可以看出,RazorViewEngine只是封装了View文件的相关路径。后面我会说明他们是怎么被用到的。

知道上面几个类的基本情况后,看一下它们的执行流程,你会对各个类的功能有个大致的了解,当控制器的Action返回一个ViewResult时并执行ExecuteResult时(文章开始部分介绍的代码):

  1. 从ViewEngines的Engines中获取ViewResultEngine对象,实质是遍历RazorViewEngine和WebFormViewEngine,然后通过它们本身继承VirtualPathProviderViewEngine的成员函数FindView创建ViewResultEngine【从下面的时序图中可以看出,虽然MVC把这个东西藏的很深,但基本都是在父类与子类间传递,结构还算清晰】;
  2. 通过得到的ViewResultEngine中的View执行Render进行界面显示,内部会调用RazorView的RenderView进行最终的显示处理;

核心流程就是上面两步,执行时序图如下所示(点击查看大图):

现在注意流程的第17步,即执行BuildManagerCompliedView的Render函数,这个函数是View显示的灵魂:

  1. public virtual void Render(ViewContext viewContext, TextWriter writer)
  2. {
  3. object instance = null;
  4.  
  5. //这个ViewPath就是根据RazorViewEngine的模板位置得到的View具体路径,在RazorViewEngine创建ViewEngineResult传进来的
  6. Type type = BuildManager.GetCompiledType(ViewPath);
  7. if (type != null)
  8. {
  9. instance = ViewPageActivator.Create(_controllerContext, type);
  10. }
  11.  
  12. if (instance == null)
  13. {
  14. throw new InvalidOperationException(
  15. String.Format(
  16. CultureInfo.CurrentCulture,
  17. MvcResources.CshtmlView_ViewCouldNotBeCreated,
  18. ViewPath));
  19. }
  20.  
  21. RenderView(viewContext, writer, instance);
  22. }

上面根据ViewPath生成的那个instance是什么?看看RazorView中RendView的实现就知道了:

  1. protected override void RenderView(ViewContext viewContext, TextWriter writer, object instance)
  2. {
  3. WebViewPage webViewPage = instance as WebViewPage;
  4. //其它代码先不管
  5. webViewPage.ExecutePageHierarchy(new WebPageContext(context: viewContext.HttpContext, page: null, model: null), writer, startPage);
  6. }

原来是个WebViewPage的对象,查一查MSDN就知道,所有View都是从WebViewPage的泛型WebViewPage<TMode>直接继承出来的。我们来看一下这个类:

  1. public abstract class WebViewPage : WebPageBase, IViewDataContainer, IViewStartPageChild
  2. {
  3. private ViewDataDictionary _viewData;
  4. private DynamicViewDataDictionary _dynamicViewData;
  5.  
  6. public AjaxHelper<object> Ajax { get; set; }
  7. public HtmlHelper<object> Html { get; set; }
  8.  
  9. public object Model
  10. {
  11. get { return ViewData.Model; }
  12. }
  13.  
  14. public TempDataDictionary TempData
  15. {
  16. get { return ViewContext.TempData; }
  17. }
  18.  
  19. public UrlHelper Url { get; set; }
  20.  
  21. public dynamic ViewBag
  22. {
  23. get
  24. {
  25. if (_dynamicViewData == null)
  26. {
  27. _dynamicViewData = new DynamicViewDataDictionary(() => ViewData);
  28. }
  29. return _dynamicViewData;
  30. }
  31. }
  32.  
  33. public ViewContext ViewContext { get; set; }
  34.  
  35. public override void ExecutePageHierarchy()
  36. {
  37. Execute(); //这个函数是基类中定义的抽象函数,会在最终aspx/cshtml生成的类中被重载
  38. }
  39.  
  40. public virtual void InitHelpers()
  41. {
  42. Ajax = new AjaxHelper<object>(ViewContext, this);
  43. Html = new HtmlHelper<object>(ViewContext, this);
  44. Url = new UrlHelper(ViewContext.RequestContext);
  45. }
  46. }

是不是在里面看到了很多平时最常用的属性。当用ExecutePageHierarchy生成页面时,实际会调用Execute函数,这个函数会在最终cshtml,aspx等生成的类中被重载。我们看一个简单的例子:

假设我们有一个强类型的视图:/Views/Home/Index.cshtml,一个简单的DemoModel类型,只有一个UserName属性:

  1. @model Controllers.DemoModel
  2. <div>Index</div>
  3. @Model.UserName

那么这个Index.cshtml被编译后就会生成下面这个类:

  1. public class _Page_Views_Home_Index_cshtml : WebViewPage<DemoModel>
  2. {
  3. public override void Execute()
  4. {
  5. this.WriteLiteral("<div>Index</div");
  6. this.Write(Model.UserName);
  7. }
  8. }

就这样,最终将WEB显示出来。

后记

本以为这篇文章很好写,没想到断断续续地写了两天。还是那句话:希望新手看的明白,老手多提意见,谢谢!

接下来一篇会放出View显示的重要组成部分:Model Data的讲解(难道你们看着看着没觉得少了点什么吗?讲了半天流程,实际的数据如何传递?如何显示?),敬请关注!

仅此一文让你明白ASP.NET MVC 之View的显示(仅此一文系列二)的更多相关文章

  1. 仅此一文让你明白ASP.NET MVC 之View的显示

    有些人要问题,为什么我要学框架?这里我简单说一下,深入理解一个框架,给你带来最直接的好处: 使用框架时,遇到问题可以快速定位,并知道如何解决: 当框架中有些功能用着不爽时,你可以自由扩展,实现你想要的 ...

  2. 仅此一文让你明白ASP.NET MVC 之Model的呈现(仅此一文系列三)

    本文目的 我们来看一个小例子,在一个ASP.NET MVC项目中创建一个控制器Home,只有一个Index: public class HomeController : Controller { pu ...

  3. 仅此一文让你明白ASP.NET MVC 之Model的呈现

    本文目的 我们来看一个小例子,在一个ASP.NET MVC项目中创建一个控制器Home,只有一个Index: public class HomeController : Controller { pu ...

  4. 仅此一文让你明白ASP.NET MVC原理

    ASP.NET MVC由以下两个核心组成部分构成: 一个名为UrlRoutingModule的自定义HttpModule,用来解析Controller与Action名称: 一个名为MvcHandler ...

  5. 【转】仅此一文让你明白ASP.NET MVC原理

    原文地址:http://www.cnblogs.com/DotCpp/p/3269043.html ASP.NET MVC由以下两个核心组成部分构成: 一个名为UrlRoutingModule的自定义 ...

  6. ASP.NET MVC 之View

    仅此一文让你明白ASP.NET MVC 之View的显示(仅此一文系列二)   题外话 一周之前写的<仅此一文让你明白ASP.NET MVC原理>受到了广大学习ASP.NET MVC同学的 ...

  7. TransactionScope事务处理方法介绍及.NET Core中的注意事项 SQL Server数据库漏洞评估了解一下 预热ASP.NET MVC 的VIEW [AUTOMAPPER]反射自动注册AUTOMAPPER PROFILE

    TransactionScope事务处理方法介绍及.NET Core中的注意事项   作者:依乐祝 原文链接:https://www.cnblogs.com/yilezhu/p/10170712.ht ...

  8. 返璞归真 asp.net mvc (4) - View/ViewEngine

    原文:返璞归真 asp.net mvc (4) - View/ViewEngine [索引页] [源码下载] 返璞归真 asp.net mvc (4) - View/ViewEngine 作者:web ...

  9. ASP.NET MVC with Entity Framework and CSS一书翻译系列文章之第六章:管理产品图片——多对多关系(上篇)

    在这章中,我们将学习如何创建一个管理图片的新实体,如何使用HTML表单上传图片文件,并使用多对多关系将它们和产品关联起来,如何将图片存储在文件系统中.在这章中,我们还会学习更加复杂的异常处理,如何向模 ...

随机推荐

  1. 公众号第三方平台开发 获取 component_verify_ticket 2015-07-05 10:16 59人阅读 评论(0) 收藏

    8.推送component_verify_ticket协议 在公众号第三方平台创建审核通过后,微信服务器会向其"授权事件接收URL"每隔10分钟定时推送component_veri ...

  2. ECMAScript 5

    2009年12月,ECMAScript 5.02011年6月,ECMAscript 5.1版发布2015年6月,ECMAScript 6正式通过,成为国际标准ES6第一个版本 ES2015,发布于20 ...

  3. win7(x64)下安装cocos2d并编译安卓项目

    好吧,不为啥,就是如题. win7 x64 脑袋内存比较小,说不定明儿就忘了,今天记录一下. 没有什么经验,所有步骤基本都是百度出来的,这里边操作边记录,为了保护原创作者,这里我都附上我查找的链接. ...

  4. gdb的可视化工具安装

    红帽推出的insight https://www.sourceware.org/insight/index.php http://wiki.ubuntu.org.cn/Insight%E7%9A%84 ...

  5. windowsservice

    1.创建 windows服务 项目  文件 -> 新建项目 -> 已安装的模板 -> Visual C# -> windows ,在右侧窗口选择"windows 服务 ...

  6. Hibernate 非常见异常集合

    异常一:org.hibernate.AnnotationException: Collection has neither generic type or OneToMany.targetEntity ...

  7. BZOJ 4066 简单题 ——KD-Tree套替罪羊树

    [题目分析] 直接x,y二维轮番划分,暴力即可. 套上替罪羊,打碎重构,对于时间复杂度有了保证. 写起来好麻烦,重构的技巧很棒! [代码] #include <cstdio> #inclu ...

  8. Linux内核笔记——内存管理之slab分配器

    内核版本:linux-2.6.11 内存区和内存对象 伙伴系统是linux用于满足对不同大小块内存分配和释放请求的解决方案,它为slab分配器提供页框分配请求的实现. 如果我们需要请求具有连续物理地址 ...

  9. spring源码解析——spring源码导入eclipse

    一.前言     众所周知,spring的强大之处.几乎所有的企业级开发中,都使用了spring了.在日常的开发中,我们是否只知道spring的配置,以及简单的使用场景.对其实现的代码没有进行深入的了 ...

  10. 读取web项目properties文件路径 解决tomcat服务器找不到properties路径问题

    1.需求:有时候我们产品经理给我们的需求是会不断变化的,例如数量是1000现在变成500,我们不可以去改代码吧,这样很麻烦,所以就可以改配置文件properties(这个数据库链接一样),当然也有js ...