分析MVC5原始碼,並實現一個ASP.MVC

風靈使發表於2019-01-14

本節內容不是MVC入門教程,主要講MVC原理,實現一個和ASP.NET MVC類似基本原理的專案.

MVC原理是依賴於ASP.NET管道事件基礎之上的.對於這塊,可閱讀上節內容

MVC簡介

隨著技術的發展,現在已經將MVC模式等同於三層模式。

如果要嚴格區分的話,UI層指ViewControllerBLL,DAL層和模型層都屬於Model中。

在建立MVC專案的時候,選擇空的專案,會建立一個如下的專案結構
在這裡插入圖片描述

由於MVC具有以下優點

  1. 效能高,不需要經過複雜的控制元件生命週期
  2. SEO,頁面乾淨,沒有ViewState,url地址沒字尾名
  3. 擴充套件多,ActionResult各種子類,輕鬆返回JSON,string
  4. Razor檢視引擎

所以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.

實際上ActionResultHttpHandlePR方法最終輸出也是最核心的方法.

這裡看下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事件後,執行MVCHandlePR方法
  5. 根據URL,建立指定Controller(繼承Controller,ControllerBase,IController),呼叫IControllerExecute的方法.
  6. ControllerBaseExecute方法的呼叫抽象方法ExecuteCore
  7. ControllerExecuteCore方法呼叫ActionInvoker(這個屬性實現類是ControllerActionInvoker)的InvokeAction方法
  8. 執行MVC過濾器
  9. 呼叫控制器的方法,得到ActionResult
  10. 呼叫ActionResultExecuteResult方法
  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.MVCSystem.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和麵向介面的程式設計方式.

相關文章