[ASP.NET]分析MVC5源码,并实现一个ASP.MVC
本节内容不是MVC入门教程,主要讲MVC原理,实现一个和ASP.NET MVC类似基本原理的项目.
MVC原理是依赖于ASP.NET管道事件基础之上的.对于这块,可阅读上节内容
[ASP.NET]谈谈IIS与ASP.NET管道
本节目录:
随着技术的发展,现在已经将MVC模式等同于三层模式。
如果要严格区分的话,UI层指View和Controller,BLL,DAL层和模型层都属于Model中。
在建立MVC项目的时候,选择空的项目,会建立一个如下的项目结构
由于MVC具有以下优点
- 性能高,不需要经过复杂的控件生命周期
- SEO,页面干净,没有ViewState,url地址没后缀名
- 扩展多,ActionResult各种子类,轻松返回JSON,string
- Razor视图引擎
- ....
所以MVC不得不成为ASP.NET的首选开发
扩展
Action的本质就是方法,只要是public的方法,外部都能访问到
路由系统
类图
代码图
路由对象
路由系统
RouteTable
路由表,有个RouteDictionary属性,存放RouteBase的实现类Route。通过Route能返回RouteData.
RouteData中包括
路由系统原理
首先添加一条路由对象,路由对象相当于定制一个url模板
然后创建一个Controller工厂,用来反射调用Controller方法,并缓存所有Controller Type,将其赋值给ControllerBuilder,这个是一个单例对象.
UrlRoutingModule
注册第7个事件,并且根据HttpContext(实际就是读取URL),从RouteTable中获取到RouteData,
然后通过RouteData获取IHttpHandler
扩展:
路由系统依赖UrlRoutingModule,而这个在默认配置的Web.config中已经配置,所以路由并不是ASP.Net MVC专属,而是Asp.Net必经之路.
ActionResult
我们的Action实际上就是返回一个ActionResult.
实际上ActionResult是HttpHandle中PR方法最终输出也是最核心的方法.
这里看下ActionResult源码和JsonResult源码
1
2
3
4
|
public abstract class ActionResult { public abstract void ExecuteResult(ControllerContext context); } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public class JsonResult : ActionResult { public object Data { get ; set ; } public JsonRequestBehavior JsonRequestBehavior { get ; set ; } public JsonResult() { this .JsonRequestBehavior = JsonRequestBehavior.DenyGet; } public override void ExecuteResult(ControllerContext context) { JavaScriptSerializer scriptSerializer = new JavaScriptSerializer(); if ( this .MaxJsonLength.HasValue) scriptSerializer.MaxJsonLength = this .MaxJsonLength.Value; if ( this .RecursionLimit.HasValue) scriptSerializer.RecursionLimit = this .RecursionLimit.Value; response.Write(scriptSerializer.Serialize( this .Data)); } } |
MVC请求流程
- 到达URLModule的第7个Application事件
- 首先根据URL,找到并创建MVCHandle(继承IHttpHandle),
- 映射IHttpHandlehttpContext.RemapHandler(handler)
- 在第11个Application事件后,执行MVCHandle的PR方法
- 根据URL,创建指定Controller(继承Controller,ControllerBase,IController),调用IController的Execute的方法.
- 在ControllerBase的Execute方法的调用抽象方法ExecuteCore
- 在Controller的ExecuteCore方法调用ActionInvoker(这个属性实现类是ControllerActionInvoker)的InvokeAction方法
- 执行MVC过滤器
- 调用控制器的方法,得到ActionResult
- 调用ActionResult的ExecuteResult方法
- Response输出
IController
1
2
3
4
|
public interface IController { void Execute(RequestContext requestContext); } |
ControllerBase(精简源码)
1
2
3
4
5
6
|
protected virtual void Execute(RequestContext requestContext) { this .Initialize(requestContext); using (ScopeStorage.CreateTransientScope()) this .ExecuteCore(); } |
Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
protected override void ExecuteCore() { this .PossiblyLoadTempData(); try { string requiredString = this .RouteData.GetRequiredString( "action" ); if ( this .ActionInvoker.InvokeAction( this .ControllerContext, requiredString)) return ; this .HandleUnknownAction(requiredString); } finally { this .PossiblySaveTempData(); } } |
ControllerActionInvoker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public virtual bool InvokeAction(ControllerContext controllerContext, string actionName) { if (controllerContext == null ) throw new ArgumentNullException( "controllerContext" ); if ( string .IsNullOrEmpty(actionName)) throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName" ); ControllerDescriptor controllerDescriptor = this .GetControllerDescriptor(controllerContext); ActionDescriptor action = this .FindAction(controllerContext, controllerDescriptor, actionName); if (action == null ) return false ; FilterInfo filters = this .GetFilters(controllerContext, action); try { AuthorizationContext authorizationContext = this .InvokeAuthorizationFilters(controllerContext, filters.AuthorizationFilters, action); if (authorizationContext.Result != null ) { this .InvokeActionResult(controllerContext, authorizationContext.Result); } else { if (controllerContext.Controller.ValidateRequest) ControllerActionInvoker.ValidateRequest(controllerContext); IDictionary< string , object > parameterValues = this .GetParameterValues(controllerContext, action); ActionExecutedContext actionExecutedContext = this .InvokeActionMethodWithFilters(controllerContext, filters.ActionFilters, action, parameterValues); this .InvokeActionResultWithFilters(controllerContext, filters.ResultFilters, actionExecutedContext.Result); } } catch (ThreadAbortException ex) { throw ; } catch (Exception ex) { ExceptionContext exceptionContext = this .InvokeExceptionFilters(controllerContext, filters.ExceptionFilters, ex); if (!exceptionContext.ExceptionHandled) throw ; else this .InvokeActionResult(controllerContext, exceptionContext.Result); } return true ; } |
从这个方法中,也可以看出MVC过滤器的执行顺序.
(MVC没有WebForm的控件生命周期,但是提供过滤器实现类似效果性能更高.)
这个方法中的InvokeActionResult方法实际就是调用
1
2
3
4
|
protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) { actionResult.ExecuteResult(controllerContext); } |
也就到达我们最上面的ActionResult的抽象方法中了.
至此MVC核心源码分析结束了.
看完MVC源码,实现一个MVC源码也很简单,这里我们干脆把路由系统和MVC用到的类都实现出来,完全脱离System.MVC和System.Web.Routing2个程序集
代码效果
Global文件
1
2
3
4
5
6
7
|
public class Global : HttpApplication { protected void Application_Start( object sender, EventArgs e) { RouteTable.Routes.Add( "default" , new Route()); } } |
HomeController
1
2
3
4
5
6
7
|
public class HomeController : Controller { public ActionResult Index() { return Content( "Hello World" ); } } |
运行效果
性能
后台代码
说明:本实现代码主要偏MVCHandle一块
扩展
从微软的源码中可以看出微软偏爱于AOP和面向接口的编程方式.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)