ASP.NET MVC (Model View Controller, 模型 视图 控制器)

创建时间:
2014-02-27 21:02
最近更新:
2018-09-17 09:13

Overview

  • MVC 是 一种 软件架构模式。该模式把软件系统分为 MVC 三个部分。最早由 Trygve Reenskaug 在 1978 年提出,并应用在 Smalltalk 系统中。
  • ASP.NET MVC 是 一种 Web 开发框架。
  • V: 用户界面。例如 由 HTML 元素组成的网页界面,或者软件的客户端界面。
  • C: 处理请求 并 响应。与 M 交互。
  • M: 封装数据与逻辑。
  • 使用的 MVC 的目的: 将 M 和 V 的实现代码分离,从而使同一个程序可以使用不同的表现形式。例如 Windows 资源管理器 文件夹内容的显示方式,详细信息显示方式 与 中等图标显示方式,文件的内容并没有改变,改变的是显示的方式。不管用户使用何种类型的显示方式,文件的内容并没有改变,达到 M 和 V 分离的目的。

Resource - MSDN

  1. System.Web.Mvc.Html 命名空间 - 该命名空间下共有 18 个类: EnumHelper、MvcForm 以及 16 个 XxxExtensions 类

Resource - 扩展点

  1. 玩转 ASP.NET MVC 的八个扩展点
  1. http://zzk.cnblogs.com/s?w=blog:xfrog+扩展
  2. MVC 中的扩展点 (一) 路由系统简介
  3. MVC 中的扩展点 (二) 路由上的扩展
  4. MVC 中的扩展点 (三) 控制器工厂
  5. MVC 中的扩展点 (四) 过滤器
  6. MVC 中的扩展点 (五) 方法选择器
  7. MVC 中的扩展点 (六) ActionResult
  8. MVC 中的扩展点 (七) 视图及视图引擎
  9. MVC 中的扩展点 (八) 模型绑定
  10. MVC 中的扩展点 (九) 验证
  11. MVC 中的扩展点 (十) 辅助方法

Resource

  1. How ASP.NET MVC Works - 蒋金楠 MVC 目录
  2. MVC5 入门教程
  3. ASP.NET MVC 较早版本
  4. 修改 view 的搜索次序
  5. 视图模型 (ViewModel) 到底是什么
  6. 实战: 把 ASP.NET MVC 中的 Views 下面的视图放到 Views 文件夹外
  7. ASP.NET MVC 小牛之路 - 理解MVC模式
  8. ASP.NET MVC 4 系列 - 进阶篇之 Helper
  9. 七天学会 ASP.NET MVC (一) ——深入理解 ASP.NET MVC
  10. 从零开始学习 ASP.NET MVC 1.0
  11. ASP.NET MVC 是如何运行的 1: 建立在 "伪" MVC 框架上的 Web 应用
  12. 用网站 (WebSite 而不是 WebProject) 项目构建 ASP.NET MVC 网站

System.Web.Mvc.dll

2015-10-26 用 Everything 在 FtnDwjPc 上找到以下 .dll,在 JetBrains dotPeek 1.0 打开,用于研究 System.Web.Mvc.TagBuilder

C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Stack 5\Packages\Microsoft.AspNet.Mvc.5.0.0\lib\net45\System.Web.Mvc.dll

2018-01-20 用 Everything 在 VM0 上找到以下 .dll,在 JetBrains dotPeek 1.0 打开,用于研究 System.Web.Mvc.TagBuilder

C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Web.Mvc\v4.0_4.0.0.0__31bf3856ad364e35\System.Web.Mvc.dll

指定视图

public class HomeController : Controller {
	public ActionResult Index(){
		return View("NotIndex"); //Call "~/Views/Home/NotIndex.cshtml".
		//return View("~/NotViews/NotHome/ExtensionIsRequired.cshtml");
	}
}

视图引擎

  1. ASP.NET MVC 的 Razor 引擎: RazorViewEngine (继承 IViewEngine 自定义视图引擎)
  2. 自定义视图引擎,实现 MVC 主题快速切换
  3. ASP.NET MVC: 自定义 RazorViewEngine 视图引擎 - 吐槽

注册视图引擎

//摘自《ASP.NET MVC 3 高级编程》 Page 59
protected void Application_Start(){
	ViewEngines.Engines.Clear();
	ViewEngines.Engines.Add(new MyViewEngine());
	RegisterRoutes(RouteTable.Routes);
}

控件 - Telerik Extensions for ASP.NET MVC

Telerik Extensions for ASP.NET MVC 是 Telerik 公司专门针对 ASP.NET MVC 开发的开源的的轻量级的 UI 组件。简单来说,就是 ASP.NET MVC 下的控件,借助它就可以像 ASP.NET WEB FORM 一样快速轻松的开发 WEB 项目。

Telerik Extensions for ASP.NET MVC 中文教程

预设常用命名空间 (namespace)

对于在视图中经常使用的名称空间,可在 Views/Web.config 文件中预设:

<system.web.webPages.razor>
	<pages pageBaseType="System.Web.Mvc.WebViewPage">
		<namespaces>
			<add namespace="System.Web.Mvc" />
			<add namespace="System.Web.Mvc.Ajax" />
			<add namespace="System.Web.Mvc.Html" />
			<add namespace="System.Web.Optimization"/>
			<add namespace="System.Web.Routing" />
			<add namespace="Tl.ExtensionMethods" />
		</namespaces>
	</pages>
</system.web.webPages.razor>

测试 System.Web.Mvc.ActionFilterAttribute 的 4 个虚方法的执行顺序

using System.Web.Mvc;


//与ActionFilterAttribute相关的执行顺序:
//OnActionExecuting
//Action
//OnActionExecuted
//OnResultExecuting
//返回的ActionRsult中的ExecuteResult
//OnResultExecuted


public class TestActionFilterAttribute : ActionFilterAttribute
{


	public string Message { get; set; }

	public override void OnActionExecuting(ActionExecutingContext filterContext)
	{
		base.OnActionExecuting(filterContext);
		filterContext.HttpContext.Response.Write("Action执行之前" + Message + "<br />");
	}

	public override void OnActionExecuted(ActionExecutedContext filterContext)
	{
		base.OnActionExecuted(filterContext);
		filterContext.HttpContext.Response.Write("Action执行之后" + Message + "<br />");
	}

	public override void OnResultExecuting(ResultExecutingContext filterContext)
	{
		base.OnResultExecuting(filterContext);
		filterContext.HttpContext.Response.Write("返回Result之前" + Message + "<br />");
	}

	public override void OnResultExecuted(ResultExecutedContext filterContext)
	{
		base.OnResultExecuted(filterContext);
		filterContext.HttpContext.Response.Write("返回Result之后" + Message + "<br />");
	}


}


//上述类调用示例:
public class TestTestActionFilterAttribute : Controller
{


	//如果我们将此Attribute附在Controller上,将作用于其下的所有的Action。
	[TestActionFilterAttribute(Message = "Action")]
	public ActionResult Index()
	{
		HttpContext.Response.Write("Action正在执行...<br />");
		return Content("正在返回Result...<br />");
	}
	//上述Action执行结果:
	//Action执行之前Action
	//Action正在执行...
	//Action执行之后Action
	//返回Result之前Action
	//正在返回Result...
	//返回Result之后Action


}

ASP.NET MVC 的执行流程

MVC在底层和传统的asp.net是一致的,在底层之上,相关流程如下:

1)Global.asax里,MvcApplication对象的Application_Start()事件中,调用 RouteConfig.RegisterRoutes(RouteTable.Routes); 来注册路由规则。

2)RouteConfig.RegisterRoutes()方法里,给出的默认规则为 {controller}/{action}/{id} .

a. 在有特别需要的时候,到这里来修改规则。

b. 未指明Controller默认为HomeContoller,未指定Action默认为Index, 也就是说,直接访问站点时,会默认导航到HomeController下的Index Action.

c. Action后跟的内容会被映射为名为id的参数。 比如: User/Delete/1 会匹配到 UserController的 Delete(int id) 方法。

d. Action分为[HttpGet][HttpPost]两种,与HTTP的GET和POST方式对应。 即直接请求的URL只会匹配[HttpGet]方法,而未注明[HttpPost]的方法即默认为[HttpGet]. Post的Action一般会有一个Model参数,MVC会自动将表单里的数据按约定的规则填充到Model实体对象里。 这个约定的规则,就是表单的Name与属性名称有个对应规则。

3)MVC负责按上述规则将执行流程导入合适的Action中,即Controller的某个方法中。

4)Action中,可以直接返回字符串,输出到浏览器, 也可以返回到ActionResult对象, 该对象指向一个View页面,并且附带了一些属性作为传递数据的容器。

ActionResult通常通过调用Controller对象的View(…)方法得到,也可以通过Redirect()方法或RedirectToAction()方法得到, 不过后面两种方法主要用于跳转到其它Controller,因为后面的方法不能传递Model对象。 Action向View传递数据,可以通过Model、ViewBag或ViewData三种容器,其中Model是强类型的,所以是最优先的方式。

5)View中,会先执行本身,再执行模板页和子页, 但是执行顺序和页面内容的输出顺序并不一致。模板中后输出的内容可以在页面中先输出的内容的前面.

在View中,需要提交数据,或跳转页面里,均会通过Route导向某个Controller的Action。 View中向Action中传递数据,其最终原理是通过http的GET和POST等方式(含AJAX的), POST方式会通过上面提到的约定规则,将表单中的数据填充到Model参数对象中。 在Action中,可以访问通过参数传递进来的Model对象,也可以访问原始的Request对象中的QueryString参数或Form集合, 当然Model对象是优先的方式。

ASP.NET MVC 的处理流程

asp.net MVC 分为主要的Controller 、Action、以及Views

下面来分析Asp.net MVC的处理流程:

Controller 负责将获取Model数据并将Model传递个View对象,通知View对象显示。

处理流程:
1.用户发起请求---》UrlRouting获取请求—》MvcRouteHandler.GetHttpHandler()—>MvcHandler.ProcessRequest()
2.UrlRouting获取浏览器发起的请求

将RoutData与HttpContext合并成为RequestContext传递到IRoutHandler接口,IRoutHandler接口的实现类MvcRouteHandler接口到RequestContext参数,返回一个MvcHandler对象,并且为这个对象赋值RequestContext
3.MvcHandler对象

根据RequestContext参数解析出RouteData以及HttpContext,根据RouteData来查找出Controller以及对象的Action及其Parameters
4.MvcHandler .ProcessRequest()方法的处理流程

MvcHandler对象根据工厂方法获取到具体的Controller-->Controller.Execute()->释放Controller对象

这其中最主要的是MvcHandler对象根据传入的RequestContext参数来获取到具体的Controller,并执行其Execute()方法
5.Controller.Execute()方法处理流程 查找Action

获取Action--->调用ActionResult(Abstract方法)的ActionResult.ExecuteResult()方法
6.ActionResult.ExecuteResult()方法

获取到IView对象,》根据Iview对象的页面路径获取到具体的Page,->调用IView.RenderView()方法显示页面

IView对象中存储的是页面的路径地址,最终通过页面引擎(View Engine)使用该路径生成具体的页面类,ViewPage(System.Web.Mvc.ViewPage)是实现了IView接口的对象。
7,最终页面就可以正确的显示。

ViewPage.RenderPartialView() 显示.ascx文件或者是ViewPage.RenderView() 显示.aspx文件。

现在MVC 3中使用的是Razor视图引擎,和WebFormViewEngine一样的处理流程

下面附上Pro Asp.net MVC 的作者的一副图:

1

ASP.NET MVC 的执行流程 的 另一种总结

Asp.Net MVC 4 中,从客户端请求发出、到服务器处理数据、到数据返回到客户端,经历过程如下:
(Client) HTTP Request --> (Server)Routing -->Controller -->ViewResult -->ViewEngine -->View -->Response

每步大致工作步骤:
(Client)HTTP Request
(Server)Global.asax, web.config[ Routing定义的URL规则等等 ]
对应Controller [例如HomeController.cs,借助各个DLL资源框架等]
对应Action [如HomeController.cs里对应的方法名为Index的Action]
ViewResult [根据Action返回的视图的名称/位置/内容, 如view对象无参数则默认视图名和Action方法名相同]
ViewEngine [Controller中的数据处理反映到视图中,用Razor(大致语法风格@xxxx)或webForms(大致语法风格: <%: xxx %>)引擎处理]
View对应视图 [数据处理完成后的页面]
Response [返回数据到客户端]

视图 生成 字符串

以下为 VM0_D:\Test\Cms\trunk\Cms\Cms\Code\GeneralDuty\Mvc.cs 的完整备份:

using System.IO;
using System.Web.Mvc;


namespace T
{


	public class Mvc
	{


		#region Render View to HTML:

		/// <summary>
		/// 指定ViewName与MasterName,获取其HTML字符串输出。
		/// </summary>
		public string RenderView(Controller controller, string viewName, string masterName)
		{
			IView view = ViewEngines.Engines.FindView(controller.ControllerContext, viewName, masterName).View;
			return RenderViewInterface(controller, view);
		}

		/// <summary>
		/// 指定PartialViewName,获取其HTML字符串输出。
		/// </summary>
		public string RenderPartial(Controller controller, string partialViewName)
		{
			IView view = ViewEngines.Engines.FindPartialView(controller.ControllerContext, partialViewName).View;
			return RenderViewInterface(controller, view);
		}

		/// <summary>
		/// 指定IView,获取其HTML字符串输出。
		/// </summary>
		public string RenderViewInterface(Controller controller, IView view)
		{
			using(StringWriter writer = new StringWriter()) {
				ViewContext viewContext = new ViewContext(controller.ControllerContext, view, controller.ViewData, controller.TempData, writer);
				viewContext.View.Render(viewContext, writer);
				return writer.ToString();
			}
		}

		#endregion Render View to HTML.


	}


}

把 PartialView、View 转换成 HTML字符串

在开发中有时要在后台获得某个 View 或者 PartialView 生成的字符串,只要你熟悉 ASP.NET MVC 生命周期就能理解和敲出下面的代码。

输出 View HTML 字符串

/// <summary>
/// 输出 View HTML 字符串。
/// </summary>
/// <param name="controller"></param>
/// <param name="viewName">视图文件名</param>
/// <param name="masterName">母板页文件名</param>
/// <returns></returns>
protected static string RenderViewToString(Controller controller, string viewName, string masterName)
{
    IView view = ViewEngines.Engines.FindView(controller.ControllerContext, viewName, masterName).View;
    using (StringWriter writer = new StringWriter()) {
        ViewContext viewContext = new ViewContext(controller.ControllerContext, view, controller.ViewData, controller.TempData, writer);
        viewContext.View.Render(viewContext, writer);
        return writer.ToString();
    }
}

输出 PartialView HTML 字符串

/// <summary>
/// 输出 PartialView HTML 字符串。
/// </summary>
/// <param name="controller"></param>
/// <param name="partialViewName">部分视图文件名</param>
/// <returns></returns>
protected static string RenderPartialViewToString(Controller controller, string partialViewName)
{
    IView view = ViewEngines.Engines.FindPartialView(controller.ControllerContext, partialViewName).View;
    using (StringWriter writer = new StringWriter()) {
        ViewContext viewContext = new ViewContext(controller.ControllerContext, view, controller.ViewData, controller.TempData, writer);
        viewContext.View.Render(viewContext, writer);
        return writer.ToString();
    }
}

传递数据的方式 - 由 Controller 至 View

ViewData

只能在一个动作方法中设置,在相关视图页面读取,说得再白一点就是只能在一个页面中使用。

//In Controller:
ViewData["Message"] = "Test";
//In View:
@ViewData["Message"]

TempData

TempData 和 ViewData 的主要区别: ViewData 只是单个视图有效,而 TempData 则是两个视图有效,就是说 TempData 可以在两个视图中传递,如果中间没有更新 TempData 的值的话,那么从第二个视图显示之后,TempData 所保存的数据就会丢失。也就是说,只有更新过的、以及新添加的键值对才能在下次 request 中继续使用。微软这样设计的原因 应该是为了节省资源,因为 http 是无状态的,每次请求的时候,他都要重新保存 TempData 的值 (利用 session 保存,顺便提一下,ASP.NET MVC 里面的 session 对象和 Web From 是一样的),但为了节省资源,所以就只保存一次。
TempData 和 ViewData 使用方式一致,把 ViewData 关键字换成 TempData 即可。

ViewBag

//In Controller:
ViewBag.Message = something;
//In View:
@ViewBag.Message

Model

Model 是强类型,ViewData 和 TempData 都是弱类型。

//In Controller:
return View(model);
//In View:
@Model.message

传递数据的方式 - 由 View 至 Controller

In View:

@using(Html.BeginForm()){
    @Html.TextBox("Name")
    <input type="submit"/>
}

In Controller:

//方式一: 通过 Request.Form 读取表单数据:
Request.Form["Name"];

//方式二: 通过 FormCollection 读取表单数据:
public ActionResult About(FormCollection form){
    string s = form["Name"];
    return View();
}

传递数据的方式 - 向 View 传递动态对象

public ActionResult UsingExpando()
{
    dynamic viewModel = new ExpandoObject();
    viewModel.TestString = "This is a test string";
    return View(viewModel);
}