一:网站启动流程简介

前面两节我们有介绍管道处理模型,然后下图总结出了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然后拼接成字符串,然后遇到后台代码则正常执行。

下面我们通过一个方法来进行分析说明:

  /// <summary>
/// 一个展示当前网站的view文件的
/// </summary>
/// <param name="helper"></param>
/// <returns></returns>
public static MvcHtmlString ListViewAssemblies(this HtmlHelper helper)
{
TagBuilder ul = new TagBuilder("ul");
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName.StartsWith("App_Web_")))//view编辑后生成的都是以App_Web_开头的
{
TagBuilder li = new TagBuilder("li");
li.InnerHtml = string.Format("dll完整名称:{0}", assembly.FullName);
ul.InnerHtml += li.ToString(); TagBuilder li2 = new TagBuilder("li");
li2.InnerHtml = string.Format("dll地址:{0}", assembly.Location);
ul.InnerHtml += li2.ToString();
}
return MvcHtmlString.Create(ul.ToString());
}

然后新建一个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,然后统一输出。

四:根据mvc反编译,来扩展View视图

扩展的目标:不同的浏览器访问不同的view视图。具体做法如下:

1:新增类CustomViewEngine继承于RazorViewEngine(因为现在是mvc项目,所以选择继承于RazorViewEngine),代码如下:

 public class CustomViewEngine : RazorViewEngine
{
#region 构造函数
public CustomViewEngine() : this(null)
{
}
public CustomViewEngine(IViewPageActivator viewPageActivator) : base(viewPageActivator)
{
this.SetEngine();
}
#endregion public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
if (controllerContext.HttpContext.Request.UserAgent.Contains("Chrome/74.0.3729.169"))
{
this.SetEngine("Chrome");
}
else
{
this.SetEngine();//一定得有,因为只有一个Engine实例
}
return base.FindView(controllerContext, viewName, masterName, useCache);
} public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
{
if (controllerContext.HttpContext.Request.UserAgent.Contains("Chrome/74.0.3729.169"))
{
this.SetEngine("Chrome");
}
else
{
this.SetEngine();
}
return base.FindPartialView(controllerContext, partialViewName, useCache);
}
/// <summary>
/// 把模板给换了
/// </summary>
/// <param name="browser"></param>
private void SetEngine(string browser="")
{
base.AreaViewLocationFormats = new string[]
{
"~/Areas/{2}/"+browser+"Views/{1}/{0}.cshtml",
"~/Areas/{2}/"+browser+"Views/{1}/{0}.vbhtml",
"~/Areas/{2}/"+browser+"Views/Shared/{0}.cshtml",
"~/Areas/{2}/"+browser+"Views/Shared/{0}.vbhtml"
};
base.AreaMasterLocationFormats = new string[]
{
"~/Areas/{2}/"+browser+"Views/{1}/{0}.cshtml",
"~/Areas/{2}/"+browser+"Views/{1}/{0}.vbhtml",
"~/Areas/{2}/"+browser+"Views/Shared/{0}.cshtml",
"~/Areas/{2}/"+browser+"Views/Shared/{0}.vbhtml"
};
base.AreaPartialViewLocationFormats = new string[]
{
"~/Areas/{2}/"+browser+"Views/{1}/{0}.cshtml",
"~/Areas/{2}/"+browser+"Views/{1}/{0}.vbhtml",
"~/Areas/{2}/"+browser+"Views/Shared/{0}.cshtml",
"~/Areas/{2}/"+browser+"Views/Shared/{0}.vbhtml"
};
base.ViewLocationFormats = new string[]
{
"~/"+browser+"Views/{1}/{0}.cshtml",
"~/"+browser+"Views/{1}/{0}.vbhtml",
"~/"+browser+"Views/Shared/{0}.cshtml",
"~/"+browser+"Views/Shared/{0}.vbhtml"
};
base.MasterLocationFormats = new string[]
{
"~/"+browser+"Views/{1}/{0}.cshtml",
"~/"+browser+"Views/{1}/{0}.vbhtml",
"~/"+browser+"Views/Shared/{0}.cshtml",
"~/"+browser+"Views/Shared/{0}.vbhtml"
};
base.PartialViewLocationFormats = new string[]
{
"~/"+browser+"Views/{1}/{0}.cshtml",
"~/"+browser+"Views/{1}/{0}.vbhtml",
"~/"+browser+"Views/Shared/{0}.cshtml",
"~/"+browser+"Views/Shared/{0}.vbhtml"
};
}
}

2:新增views视图,可以把之前的views视图全部copy一份出来命名为:ChromeViews

3:在Global.asax类中Application_Start()方法新增配置,即把之前的视图引擎修改为自定义的CustomViewEngine。

 ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomViewEngine());

这样即实现了不同的chrom浏览器访问就会跳转到ChromeViews视图里面,其它的浏览器则默认走Views里面。可以仿照上面的代码来进行pc/APP端视图切换,多语言视图切换,即controller使用的是一套,但是View使用多个。

MVC的View本质和扩展的更多相关文章

  1. 在ASP.NET MVC中使用Knockout实践02,组合View Model成员、Select绑定、通过构造器创建View Model,扩展View Model方法

    本篇体验使用ko.computed(fn)计算.组合View Model成员.Select元素的绑定.使用构造器创建View Model.通过View Model的原型(Prototype)为View ...

  2. 白话学习MVC(九)View的呈现一

    一.概述 本节来看一下ASP.NET MVC[View的呈现]的内容,View的呈现是在Action执行之后进行,Action的执行生成一个ActionResult,[View的呈现]的功能就是:通过 ...

  3. 白话学习MVC(十)View的呈现二

    本节将接着<白话学习MVC(九)View的呈现一>来继续对ViewResult的详细执行过程进行分析! 9.ViewResult ViewResult将视图页的内容响应给客户端! 由于Vi ...

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

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

  5. ASP.NET MVC 之View

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

  6. [asp.net mvc 奇淫巧技] 05 - 扩展ScriptBundle,支持混淆加密javascript

    一.需求: 在web开发中,经常会处理javascript的一些问题,其中就包括js的压缩,合并,发布版本以及混淆加密等等问题.在asp.net 开发中我们使用ScriptBundle已经可以解决ja ...

  7. 关于MVC中View使用自定义方法

    今天学习到了在MVC的View中使用自定义方法,很简单,下面分享一下. 1.首先在项目下面建立一个文件夹,用于存我们写的自定义方法. 2.在新建文件夹中新增一个类,命名随便取(最好还是和自定义方法关联 ...

  8. Asp.net Mvc模块化开发之分区扩展框架

    对于一个企业级项目开发,模块化是非常重要的. 默认Mvc框架的AreaRegistration对模块化开发真的支持很好吗?真的有很多复杂系统在使用默认的分区开发的吗?我相信大部分asp.net的技术团 ...

  9. ASP.NET MVC 中 View 的设计

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

随机推荐

  1. vue实现点击图标,图标在2s中完成旋转

    <!-- 点击 vue实现点击图标,图标在2s中完成旋转 1==>如何让它在2s内完成旋转 使用动画 transform: rotate(-180deg); 动画的运动状态 transit ...

  2. ES3、ES5、ES6对象代理的写法差异

    ES3的对象代理写法: console.log('定义私有变量ES3写法:') // ES3 var Person = function (){ var data = { name:'ES3', ag ...

  3. 2018年蓝桥杯A组C/C++决赛题目

    2018年蓝桥杯A组C/C++决赛题目 2018年蓝桥杯A组C/C++决赛题解     1:三角形面积 已知三角形三个顶点在直角坐标系下的坐标分别为: (2.3, 2.5) (6.4, 3.1) (5 ...

  4. 【声明式事务】Spring事务特性(二)

    spring所有的事务管理策略类都继承自org.springframework.transaction.PlatformTransactionManager接口. 其中TransactionDefin ...

  5. mybatis中<include>标签的作用

    MyBatis中sql标签定义SQL片段,include标签引用,可以复用SQL片段 sql标签中id属性对应include标签中的refid属性.通过include标签将sql片段和原sql片段进行 ...

  6. Vue indent eslint缩进webstorm冲突解决

    参考教程 官方回复 ESlint设置 rules: { 'no-multiple-empty-lines': [1, {max: 3}], // 控制允许的最多的空行数量 'vue/script-in ...

  7. <Array> 277 243 244 245

    277. Find the Celebrity knows(i, j): By comparing a pair(i, j), we are able to discard one of them 1 ...

  8. mysql小知识点汇总---(时间与时间戳的转换, 修改mysql用户名密码, navicate 导入sql文件报错 1153)

    1. 时间与时间戳的转换 1.1 时间戳转时间 FROM_UNIXTIME(add_time, '%Y-%m-%d') 1.2 时间转时间戳 UNIX_TIMESTAMP('2015-04-29') ...

  9. VMware 自动开多台虚拟机脚本

    d:cd "D:\WinInstall\VMware\VMware Workstation"ECHO "start vm1"vmrun -T ws start ...

  10. 集成Azure DevOps Server(TFS) 与微软Teams

    1.概述 Microsoft Teams是Office 365中团队协作的中心.将团队的所有聊天.会议.文件和应用程序放在一个位置.软件开发团队可以在一个专门的协作中心中即时访问他们所需的所有内容,T ...