一:网站启动流程简介
前面两节我们有介绍管道处理模型,然后下图总结出了mvc启动的整个流程
二:MVC返回的三种结果
从之前的流程已经反编译源码我们晓的,mvc最终都会返回一个结果,其中大概分为以下三种:
1:返回ActionResult:是一个抽象类,实现了ExecuteResult,源码如下:
2:EmptyResult:返回void的,然后该类继承于ActionResult
3:其他类型:比如JsonResult,String,DateTime,XML,File等,其实最终结果都是ToString()然后写入response,下图我们以JsonResult的源码中的ExecuteResult()方法中的片段代码来说明
三:MVC中ViewResult视图
在新建一个mvc项目中,我新建一个视图,当访问时,一般大家都会默认约定俗成的去找:Views-》控制器的名字 -》视图名字,然后浏览器访问的时候会直接访问:控制器名字/视图名字,这样即可找到了对应的视图。
实例如下:
那现在我们看一下View()这是什么?为什么直接这样写就能访问到对应的cshtml页面呢?在不看源码之前我们可以做如下猜想,第一步一定会先找view,第二步会输出对应的内容到页面上面,那我们的猜想究竟是否正确呢,我们以源码来一步一步的解释说明。
点击View类一步一步的查看,发现View--》ViewResult--》--》ViewResultBase--》ActionResult--》ExecuteResult。
1:找视图:ViewEngineCollection来寻找的,但是通过源码找到的是IView,之前我们不是寻找的cshtml的吗?那IView是怎么转换成cshtml的呢?带着介个疑问,我们来打开我们的cshtml,之前我们都有了解过,cshtml中是可以写html代码也是可以后台代码,那这两者是怎么结合在一起的呢?原因如下:
A:cshtml为啥能写后台代码
cshtml的基类是System.Web.Mvc.WebViewPage,具体体现在:views文件夹下面的web.config,如下:
所以这就解释了为啥我们cshtml里面没有引用任何的命名空间,二却可以写@Html,@ViewBag,@model,@base等后台代码的变量,原来这些通用的命名空间全部是在views下面的web.config中配置的。webViewPage类中都有Html,ViewBag,ViewData等这些变量。
了解了这些后,我们可以对介个基类WebViewPage进行扩展,
比如我们可以新建一个类然后继承于WebViewPage这个类,然后新类里面可以增加我们自己特有的属性,比如登录用户的一些信息等,这样到时候把webconfig中的pageBaseType修改我们新建类,以后所有的cshtml都可以直接使用新增的特性了。
比如我们可以把所有的cshtml通用的命名空间统一放在namespaces介个命名空间,然后不需要每个cshtml都重复引用了。
B:cshtml类中的前台代码跟后台代码怎么融合在一起的。很多人会想到模板方法然后字符串替换,但是cshtml类不是这样实现的。cshtml是把整个cshtml方法当成一个字符串,然后以后台代码为主,就是遇到html然后拼接成字符串,然后遇到后台代码则正常执行。
下面我们通过一个方法来进行分析说明:
1 /// <summary> 2 /// 一个展示当前网站的view文件的 3 /// </summary> 4 /// <param name="helper"></param> 5 /// <returns></returns> 6 public static MvcHtmlString ListViewAssemblies(this HtmlHelper helper) 7 { 8 TagBuilder ul = new TagBuilder("ul"); 9 foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName.StartsWith("App_Web_")))//view编辑后生成的都是以App_Web_开头的 10 { 11 TagBuilder li = new TagBuilder("li"); 12 li.InnerHtml = string.Format("dll完整名称:{0}", assembly.FullName); 13 ul.InnerHtml += li.ToString(); 14 15 TagBuilder li2 = new TagBuilder("li"); 16 li2.InnerHtml = string.Format("dll地址:{0}", assembly.Location); 17 ul.InnerHtml += li2.ToString(); 18 } 19 return MvcHtmlString.Create(ul.ToString()); 20 }
然后新建一个view如下:
@using Ruanmou.Web.Core.Extension; @{ ViewBag.Title = "ViewShow"; } <h2>ViewShowUpdate</h2> <h2>ViewShow2</h2> <div>当前View类型:@this.GetType().AssemblyQualifiedName</div> <div>@{base.Response.Write("这里是直接write");}</div> <div>BuildManager:@ViewBag.ViewClass</div> <div>当前加载的View程序集1111111:</div> @Html.ListViewAssemblies()
预览发现:
这样我们找到dll地址:C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\root\c746e81f\cd88313e\,然后打开介个地址,使用反编译看一下App_Web_dzfxeipk.dll,会发现:
即每个controller会对应一个dll类,然后里面所有的view都会生成一个类。然后该cshtml用到的views全部会统一生成dll。
C:为啥cshtml最终解析的是后台代码,但是我们修改了cshtml代码后,不需要编译,而直接能访问修改呢。这是因为cshtml是即时编译的,它会有一个文件监控,当你访问的时候,如果视图发生改变,则及时编译成新的dll,如果没有改变,则直接以原先的编译的dll为主。
编译是通过下面一个类来实现的:
Type type = System.Web.Compilation.BuildManager.GetCompiledType("~/Views/Pipe/ViewShow.cshtml"); ViewBag.ViewClass = type.FullName; //它调用BuildManager的静态方法GetCompiledType根据指定的View文件虚拟路径得到编译后的WebPageView类型, //然后将该类型交给ViewPageActivator激活一个具体的WebPageView对象,并调用其Render方法完成对View的最终呈现
编译的过程总结如下:
1:ASP.NET MVC对View文件进行动态编译生成的类型名称基于View文件的虚拟路径,(比如文件路径为“~/Views/Pipe/Action1.cshtml”的View对应的类型为“ASP._Page_Views_Pipe_Action1_cshtml”)。
2:ASP.NET MVC是按照目录进行编译的(“~/Views/Pipe/”下的View文件最终都被编译到一个程序集“App_Web_j04xtjsy”中)。
3:程序集按需加载,即第一次访问“~/View/Pipe/”目录下的View并不会加载针对“~/View/Home/”目录的程序集(实际上此时该程序集尚未生成)。
2:绘画成Html代码
最终cshtml会变成一个后台类,然后在execute中把html代码转换为类,最终Resonse来输出到页面上面。
然后反编译webViewPage找到方法write,即是组装output,然后统一输出。
接下来我们扩展view视图
原文:https://www.cnblogs.com/loverwangshan/p/11215050.html