一周之前写的《仅此一文让你明白ASP.NET MVC原理》受到了广大学习ASP.NET MVC同学的欢迎,于是下定决心准备把它写成一个系列,以满足更多求知若渴的同学们。蒋金楠老师已经在他的《ASP.NET MVC 4框架揭秘》书中已经做了很深入的讲解。我总不能把他的文章抄下来放给大家。那大家还不如看他的博客去。我想做的就是给大家提供基于图形化、直观、系统、简洁的理解。部分内容想深入理解的同学,还是花点银子去买本他的书,非常值得一看(绝非打广告⊙﹏⊙‖∣)。
有些人要问题,为什么我要学框架?这里我简单说一下,深入理解一个框架,给你带来最直接的好处:
更多好处,你可以自己去体会,有兴趣的可以看一下asp.net中 mvc部分的源码:http://aspnetwebstack.codeplex.com/
上一篇文章是让你明白MVC最核心的两个流程(如果没看过请猛戳这里,只需耽误你打盹的几分钟)。我们接着上篇从ControllerActionInvoker的InvokeAction方法执行Action说起:
//执行Action,并得到ActionResult ActionResult actionResult = method.Invoke(controllerContext.Controller, parameters.ToArray()) as ActionResult; //最终ActionResult用HttpResponse将数据传回客户进行显示 actionResult.ExecuteResult(controllerContext);
本文的目的就是让你明白这段代码到底做了哪些事情?MVC中的Controller如何找到View,并进行显示。
先放上与本文相关的类结构图:
此图看起来结构相当复杂,但其实他很简单,给我两分钟,我会让你明白这些鬼东西到底是什么、有什么关系?
先说ActionResult,从图上看,ActionResult是个抽象类,他的子类有很多,比如JsonResult,ContentResult,ViewResult,EmptyResult等等。这个东西大家用MVC的时候常常接触,比如:
public class HomeController:Controller { public ActionResult Index() { return View(); } public ActionResult GetInfo() { ... return Json(obj); } public ActionResult GetContent() { return Content("test"); } }
上面三个Action返回分别对应ViewResult、JsonResult、ContentResult,而我图中只画ViewResult,因为它是我们最常用的一个ActionResult,而且是最复杂的一个(因为要负责View的显示)。而看看ContentResult的核心源码,我想简单的大家都笑翻了:
public class ContentResult : ActionResult { public string Content { get; set; } public override void ExecuteResult(ControllerContext context) { HttpResponseBase response = context.HttpContext.Response; if (Content != null) { response.Write(Content); } } }
它的实现和上面我画的结构图完全没有半毛钱关系,直接一个Response.Write输出就完成了。所以我用ViewResult来写本文。
接下来注意ViewEngines这个静态类,看一下它的源码:
public static class ViewEngines { private static readonly ViewEngineCollection _engines = new ViewEngineCollection { new WebFormViewEngine(), new RazorViewEngine(), }; public static ViewEngineCollection Engines { get { return _engines; } } }
只有一个静态只读的ViewEngineCollection类型的成员,初始化时封装了两个视图引擎,WebFormViewEngine和RazorViewEngine?这两个是什么?为了方便大家理解,我们看看RazorViewEngine的源码(WebFormViewEngine基本一样,篇幅限制下面我只用Razor举例):
public class RazorViewEngine : BuildManagerViewEngine { internal static readonly string ViewStartFileName = "_ViewStart"; //存储ViewStart模板的 public RazorViewEngine(IViewPageActivator viewPageActivator) : base(viewPageActivator) { //这些构造大家应该觉得很亲切 ViewLocationFormats = new[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" }; MasterLocationFormats = new[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" }; PartialViewLocationFormats = new[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" }; FileExtensions = new[] { "cshtml", "vbhtml", }; } protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath) { var view = new RazorView(controllerContext, viewPath, layoutPath: masterPath, runViewStartPages: true, viewStartFileExtensions: FileExtensions, viewPageActivator: ViewPageActivator) { DisplayModeProvider = DisplayModeProvider }; return view; } }
从代码中可以看出,RazorViewEngine只是封装了View文件的相关路径。后面我会说明他们是怎么被用到的。
知道上面几个类的基本情况后,看一下它们的执行流程,你会对各个类的功能有个大致的了解,当控制器的Action返回一个ViewResult时并执行ExecuteResult时(文章开始部分介绍的代码):
核心流程就是上面两步,执行时序图如下所示(点击查看大图):
现在注意流程的第17步,即执行BuildManagerCompliedView的Render函数,这个函数是View显示的灵魂:
public virtual void Render(ViewContext viewContext, TextWriter writer) { object instance = null; //这个ViewPath就是根据RazorViewEngine的模板位置得到的View具体路径,在RazorViewEngine创建ViewEngineResult传进来的 Type type = BuildManager.GetCompiledType(ViewPath); if (type != null) { instance = ViewPageActivator.Create(_controllerContext, type); } if (instance == null) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.CshtmlView_ViewCouldNotBeCreated, ViewPath)); } RenderView(viewContext, writer, instance); }
上面根据ViewPath生成的那个instance是什么?看看RazorView中RendView的实现就知道了:
protected override void RenderView(ViewContext viewContext, TextWriter writer, object instance) { WebViewPage webViewPage = instance as WebViewPage; //其它代码先不管 webViewPage.ExecutePageHierarchy(new WebPageContext(context: viewContext.HttpContext, page: null, model: null), writer, startPage); }
原来是个WebViewPage的对象,查一查MSDN就知道,所有View都是从WebViewPage的泛型WebViewPage<TMode>直接继承出来的。当一个ASP.NET MVC网站在编译时,会把所有的视图文件(如cshtml,aspx等)全部编译成基于WebViewPage<TMode>的类,生成的类成员函数ExecutePageHierarchy内部由N多的Response.Write/Response.WriteLiteral组成,实现html的输出。最终将WEB显示出来。
本以为这篇文章很好写,没想到断断续续地写了两天。还是那句话:希望新手看的明白,老手多提意义,谢谢!
接下来一篇会放出View显示的重要组成部分:Model Data的讲解(难道你们看着看着没觉得少了点什么吗?讲了半天流程,显示的数据如何显示?),敬请关注!