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

 

题外话

一周之前写的《仅此一文让你明白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说起:

//执行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时(文章开始部分介绍的代码):

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

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

现在注意流程的第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的讲解(难道你们看着看着没觉得少了点什么吗?讲了半天流程,实际的数据如何传递?如何显示?),敬请关注!

ASP.NET MVC 之View的更多相关文章

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

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

  2. ASP.NET MVC 中 View 的设计

    1. 前言  感觉有好长时间没有接触View 了,周末闲来无事,翻翻书桌上的书来回顾回顾ASP.NET MVC中View的相关内容. 2. View概述  View 通过应用程序在Action 中返回 ...

  3. 预热ASP.NET MVC 的View

    ASP.NET MVC 的View 预设是Load on Demand(按需加载),也就是说View 第一次要Render 的时候才会去载入跟编译,这个就会造成一个现象,即使Web 应用程式已经完成启 ...

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

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

  5. Asp.Net MVC向视图View传值的三种方法

    本文将总结Asp.Net MVC向视图View传值的三种常见的方法: ----------------------------------------------------------------- ...

  6. 仅此一文让你明白ASP.NET MVC 之View的显示(仅此一文系列二)

    题外话 一周之前写的<仅此一文让你明白ASP.NET MVC原理>受到了广大学习ASP.NET MVC同学的欢迎,于是下定决心准备把它写成一个系列,以满足更多求知若渴的同学们.蒋金楠老师已 ...

  7. Asp.net MVC在View里动态捆绑压缩引用的js

    前言 Asp.net MVC 4以上版本多了BundleConfig.RegisterBundles方法,可以把要捆绑的脚本或样式进行捆绑压缩,以减少客户端的请求次数从而提高了客户端的访问速度. 问题 ...

  8. ASP.NET MVC 前端(View)向后端(Controller)中传值

    在MVC中,要把前端View中的值传递给后端Controller, 主要有两种方法 1. 利用Request.Form 或者 Request.QueryString public ActionResu ...

  9. 【ASP.NET MVC】View与Controller之间传递数据

    1   概述 本篇文章主要从操作上简要分析Controller<=>View之间相互传值,关于页面之间传值,如果感兴趣,可参考我另外一篇文章ASP.NET 页面之间传值的几种方式 . Co ...

随机推荐

  1. Microsoft Build 2015

    Microsoft Build 2015 汇总   简要概括(GitHub 完成约 45%): Visual Studio Code Preview Visual Studio 2015 RC Vis ...

  2. 2014年辛星jquery解读第二节

    *************jquery的语法****************** 1.jquery是通过选取HTML元素,而且对选取的元素运行某些操作,从而完毕某些特效的. 2.因此,我们在使用jQu ...

  3. HDU 2070 Fibbonacci Number

    Fibbonacci Number Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others ...

  4. IOS中TableView的用法

    一.UITableView 1.数据展示的条件 1> UITableView的所有数据都是由数据源(dataSource)提供的,所以要想在UITableView展示数据,必须设置UITable ...

  5. CSS3实战开发: 纯CSS实现图片过滤分类显示特效

    原文:CSS3实战开发: 纯CSS实现图片过滤分类显示特效 各位网友大家好,今天我要带领大家开发一个纯CSS的图片分类显示的网址导航,单纯看标题大家可能有些困惑,依照以往惯例,我先给大家演示一下实际运 ...

  6. ActiveReports 9实战教程(2): 准备数据源(设计时、运行时)

    原文:ActiveReports 9实战教程(2): 准备数据源(设计时.运行时) 在上讲中<ActiveReports 9实战教程(1): 手把手搭建环境Visual Studio 2013 ...

  7. .net使用cefsharp开源库开发chrome

    .net使用cefsharp开源库开发chrome 离上篇写介绍pc端的混合开发和为什么以cefsharp入手研究混合开发已经有好几天,一直忙,抽不出时间继续写怎么搭建cefsharp开发环境.其实没 ...

  8. 华硕K55DR体验 - 显卡就是坑

    朋友拿来电脑,本来他室友已经把他电脑重做完了,但还是卡,非要给我再搞一遍,难道?我就是传说中的大神?咳咳...YY一下,适可而止 华硕K55DR的配置来看,似乎应付CF没什么问题,可是,FPS各种不稳 ...

  9. Spring MVC 的 研发之路

    翻译器:intellij idea 一个.创建spring mvcproject   一个. 二. 三. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcX ...

  10. VS2015安装

    VS2015安装 Secondary Installer Setup Failed求解决方案 看到微软最近的一系列变化,着实让我等兴奋不已.VS2015下载地址就不说了.先来记录一下微软的几个变化吧. ...