• [ASP.NET]分析MVC5源码,并实现一个ASP.MVC


    本节内容不是MVC入门教程,主要讲MVC原理,实现一个和ASP.NET MVC类似基本原理的项目.

    MVC原理是依赖于ASP.NET管道事件基础之上的.对于这块,可阅读上节内容

    [ASP.NET]谈谈IIS与ASP.NET管道

    本节目录:

    MVC简介

    随着技术的发展,现在已经将MVC模式等同于三层模式。

    如果要严格区分的话,UI层指View和Controller,BLL,DAL层和模型层都属于Model中。

    在建立MVC项目的时候,选择空的项目,会建立一个如下的项目结构

    由于MVC具有以下优点

    1. 性能高,不需要经过复杂的控件生命周期
    2. SEO,页面干净,没有ViewState,url地址没后缀名
    3. 扩展多,ActionResult各种子类,轻松返回JSON,string
    4. Razor视图引擎
    5. ....

    所以MVC不得不成为ASP.NET的首选开发

    扩展

    Action的本质就是方法,只要是public的方法,外部都能访问到

    MVC原理

    路由系统

    类图

    代码图

    路由对象

    路由系统

    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源码

      public abstract class ActionResult
      {
        public abstract void ExecuteResult(ControllerContext context);
      }
    
      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请求流程

    1. 到达URLModule的第7个Application事件
    2. 首先根据URL,找到并创建MVCHandle(继承IHttpHandle),
    3. 映射IHttpHandlehttpContext.RemapHandler(handler)
    4. 在第11个Application事件后,执行MVCHandle的PR方法
    5. 根据URL,创建指定Controller(继承Controller,ControllerBase,IController),调用IController的Execute的方法.
    6. 在ControllerBase的Execute方法的调用抽象方法ExecuteCore
    7. 在Controller的ExecuteCore方法调用ActionInvoker(这个属性实现类是ControllerActionInvoker)的InvokeAction方法
    8. 执行MVC过滤器
    9. 调用控制器的方法,得到ActionResult
    10. 调用ActionResult的ExecuteResult方法
    11. Response输出

    IController

      public interface IController
      {
        void Execute(RequestContext requestContext);
      }
    

    ControllerBase(精简源码)

        protected virtual void Execute(RequestContext requestContext)
        {
          this.Initialize(requestContext);
          using (ScopeStorage.CreateTransientScope())
            this.ExecuteCore();
        }
    

    Controller

        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

    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方法实际就是调用

        protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
        {
          actionResult.ExecuteResult(controllerContext);
        }

    也就到达我们最上面的ActionResult的抽象方法中了.

    至此MVC核心源码分析结束了.

    实现MVC

    看完MVC源码,实现一个MVC源码也很简单,这里我们干脆把路由系统和MVC用到的类都实现出来,完全脱离System.MVC和System.Web.Routing2个程序集

    代码效果

    Global文件

        public class Global : HttpApplication
        {
            protected void Application_Start(object sender, EventArgs e)
            {
                RouteTable.Routes.Add("default", new Route());
            }
        }

    HomeController

        public class HomeController : Controller
        {
            public ActionResult Index()
            {
                return Content("Hello World");
            }
        }
    

    运行效果

    性能

    后台代码

    点击下载 

    说明:本实现代码主要偏MVCHandle一块

    扩展

    从微软的源码中可以看出微软偏爱于AOP和面向接口的编程方式.

  • 相关阅读:
    Resource和Autowired区别
    mybatisplus 分页查询+ dao层抽象
    Error attempting to get column from result set
    第一模块经济学核心原理,第一模块经济学核心原理
    springboot 优雅的启动类
    maven把依赖打进jar包
    第一章:第1课 经济学世界观(上)
    AutomicBoolean
    java异步转同步
    接口作为方法的返回值
  • 原文地址:https://www.cnblogs.com/neverc/p/4823497.html
Copyright © 2020-2023  润新知