Asp.net MVC-3-執行過程
本篇主要講述MVC處理請求時創建Controller和執行Action的完整過程。
創建Controller先查看MvcHandler中處理請求的方法BeginProcessRequest:
protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
{
IController controller;
IControllerFactory factory;
ProcessRequestInit(httpContext, out controller, out factory);
IAsyncController asyncController = controller as IAsyncController;
if (asyncController != null)
{
……
}
else
{
……
}
}
再查看其中創建Controller的方法ProcessRequestInit
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
HttpContext currentContext = HttpContext.Current;
……
AddVersionHeader(httpContext);
RemoveOptionalRoutingParameters;
string controllerName = RequestContext.RouteData.GetRequiredString("controller");
factory = ControllerBuilder.GetControllerFactory;
controller = factory.CreateController(RequestContext, controllerName);
……
}
Controller通過ControllerBuilder屬性的方法GetControllerFactory獲取到ControllerFactory對象然後由ControllerFactory創建,ControllerBuilder屬性定義如下。
internal ControllerBuilder ControllerBuilder
{
get
{
if (_controllerBuilder == null)
{
_controllerBuilder = ControllerBuilder.Current;
}
return _controllerBuilder;
}
set { _controllerBuilder = value; }
}
可知ControllerBuilder默認使用ControllerBuilder.Current,查看ControllerBuilder類的代碼:
public IControllerFactory GetControllerFactory
{
return _serviceResolver.Current;
}
internal ControllerBuilder(IResolver
{
_serviceResolver = serviceResolver ?? new SingleServiceResolver
}
可知默認返回的ControllerFactory為DefaultControllerFactory(當然我們也可以註冊默認的自定義的ControllerFactory),這裡我們可繼續查看DefaultControllerFactory
internal DefaultControllerFactory(……)
{
……
_activatorResolver = activatorResolver ?? new SingleServiceResolver
=> null,
new DefaultControllerActivator(dependencyResolver),
"DefaultControllerFactory constructor");
}
}
private IControllerActivator ControllerActivator
{
get
{
if (_controllerActivator != null)
{
return _controllerActivator;
}
_controllerActivator = _activatorResolver.Current;
return _controllerActivator;
}
}
public virtual IController CreateController(RequestContext requestContext, string controllerName)
{
……
Type controllerType = GetControllerType(requestContext, controllerName);
IController controller = GetControllerInstance(requestContext, controllerType);
return controller;
}
protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
……
return ControllerActivator.Create(requestContext, controllerType);
}
可以看到最終Controller由DefaultControllerActivator來創建
public DefaultControllerActivator(IDependencyResolver resolver)
{
if (resolver == null)
{
_resolverThunk = => DependencyResolver.Current;
}
else
{
_resolverThunk = => resolver;
}
}
public IController Create(RequestContext requestContext, Type controllerType)
{
try
{
return (IController)(_resolverThunk.GetService(controllerType) ?? Activator.CreateInstance(controllerType));
}
catch (Exception ex)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentCulture,
MvcResources.DefaultControllerFactory_ErrorCreatingController,
controllerType),
ex);
}
}
到此為止Controller就被創建出來了,這裡我們看到了DependencyResolver.Current,這是MVC的默認注入容器,如果設置了容器,Controller的創建就可以通過容器(GetService)來完成了,如果未實現DependencyResolver或DependencyResolver未註冊該Controller則通過反射來創建(使用Activator.CreateInstance,必須存在無參構造函數)。
Controller注入下面來看對框架的第一個擴展也是最基本的擴展:為框架提供Ioc容器並註冊Controller。對於Ioc容器的起源和作用這裡就不多講了。目前Ioc的思想已經普遍地應用於各種開發實踐當中了,特別是企業應用開發中,Spring已經是Java開發事實上的基礎框架。Asp.net Mvc也深受影響,雖然框架本身沒有提供Ioc容器的實現,但是提供了很方便的擴展方式,通過擴展我們不僅可以使用容器管理自定義的對象,甚至可以將對象注入到Mvc框架當中(簡單的比如對Controller的注入)。因為Mvc框架默認首先通過容器來獲取對象,然後才是框架提供的方式(一般是一種默認實現),在以後的分析中可以看到許多的源碼都可以證實這一點。
下面我們通過添加Autofac來實現依賴注入。
首先通過nuget添加Autofac和Autofac.Mvc5的引用。
在App_Start目錄下添加Autofac的初始化類如下:
public class AutofacConfig
{
public static IDependencyResolver GeResolver
{
var builder = new ContainerBuilder;
Registers(builder);
builder.RegisterControllers(Assembly.GetExecutingAssembly);
return new AutofacDependencyResolver(builder.Build);
}
private static void Registers(ContainerBuilder builder)
{
}
}
然後在Global.asax的Application_Start方法中添加代碼如下:
DependencyResolver.SetResolver(AutofacConfig.GeResolver);
這樣我們就完成了依賴注入(目前只是Controller的注入,當然以後可以AutofacConfig 的Registers方法中把我們需要注入的對象注入到Autofac容器中)。關於依賴注入需要注意的主要有兩點:一個是ContainerBuilder的擴展方法RegisterControllers,其參數類型為params Assembly可以傳入一個或多個Assembly然後註冊這些Assembly里的Controller,這樣我們就可以將Controller放到不同的工程里;另一個是DependencyResolver.SetResolver,這樣會替換掉默認的DependencyResolver實現,我們正是通過這樣把Autofac容器作為了Mvc的容器從而完成依賴注入。
下面通過一個簡單的例子說明Ioc容器的應用。首先創建一個簡單的介面及其實現的類
public interface IIocTest
{
string Say;
}
public class IocTest: IIocTest
{
public string Say
{
return "IocTest";
}
}
然後再HomeController中添加一個該介面的欄位,並創建一個構造函數用於注入(AutoFac默認只支持構造函數注入,但也可以開啟屬性注入)。
private IIocTest iocTestInsatnce;
public HomeController(IIocTest iocTest)
{
iocTestInsatnce = iocTest;
}
然後再一個Action中調用介面方法,然後通過ViewBag(或其它方法)將其傳到頁面並顯示,頁面顯示有多種方法,這裡直接加到標題中:
public ActionResult Index
{
ViewBag.iocSays = iocTestInsatnce.Say;
return View;
}
@{
ViewBag.Title = "Home Page"+ ViewBag.iocSays;
}
然後運行程序,查看相應頁面的標題,驗證注入是否成功。
執行Action創建好Controller之後我們再來看Controller是如何執行請求的。
首先看ControllerBase,這是所有Controller的基類,查看它的處理方法BeginExecuteCore。
protected virtual IAsyncResult BeginExecuteCore(AsyncCallback callback, object state)
{
……
try
{
string actionName = GetActionName(RouteData);
IActionInvoker invoker = ActionInvoker;
IAsyncActionInvoker asyncInvoker = invoker as IAsyncActionInvoker;
if (asyncInvoker != null)
{
BeginInvokeDelegate
{
return innerState.AsyncInvoker.BeginInvokeAction(innerState.Controller.ControllerContext, innerState.ActionName, asyncCallback, asyncState);
};
EndInvokeVoidDelegate
{
if (!innerState.AsyncInvoker.EndInvokeAction(asyncResult))
{
innerState.Controller.HandleUnknownAction(innerState.ActionName);
}
};
ExecuteCoreState executeState = new ExecuteCoreState { Controller = this, AsyncInvoker = asyncInvoker, ActionName = actionName };
return AsyncResultWrapper.Begin(callback, state, beginDelegate, endDelegate, executeState, _executeCoreTag);
}
else
{
……
}
}
……
}
可以看到Action的執行通過ActionInvoker. BeginInvokeAction實現,ActionInvoker的獲取方式如下:
protected virtual IActionInvoker CreateActionInvoker
{
return Resolver.GetService
}
這裡也看到了Resolver即可以通過Ioc容器來提供IAsyncActionInvoker或IActionInvoker,如果未提供則使用默認的AsyncControllerActionInvoker,再查看默認的實現AsyncControllerActionInvoker的處理方法BeginInvokeAction。
public virtual IAsyncResult BeginInvokeAction(ControllerContext controllerContext, string actionName, AsyncCallback callback, object state)
{
if (controllerContext == null)
{
throw new ArgumentNullException("controllerContext");
}
Contract.Assert(controllerContext.RouteData != null);
if (String.IsNullOrEmpty(actionName) && !controllerContext.RouteData.HasDirectRouteMatch)
{
throw Error.ParameterCannotBeNullOrEmpty("actionName");
}
ControllerDescriptor controllerDescriptor = GetControllerDescriptor(controllerContext);
ActionDescriptor actionDescriptor = FindAction(controllerContext, controllerDescriptor, actionName);
if (actionDescriptor != null)
{
FilterInfo filterInfo = GetFilters(controllerContext, actionDescriptor);
Action continuation = null;
BeginInvokeDelegate beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState)
{
try
{
AuthenticationContext authenticationContext = InvokeAuthenticationFilters(controllerContext,
filterInfo.AuthenticationFilters, actionDescriptor);
if (authenticationContext.Result != null)
{
AuthenticationChallengeContext challengeContext =
InvokeAuthenticationFiltersChallenge(controllerContext,
filterInfo.AuthenticationFilters, actionDescriptor, authenticationContext.Result);
continuation = => InvokeActionResult(controllerContext,
challengeContext.Result ?? authenticationContext.Result);
}
else
{
AuthorizationContext authorizationContext = InvokeAuthorizationFilters(controllerContext, filterInfo.AuthorizationFilters, actionDescriptor);
if (authorizationContext.Result != null)
{
AuthenticationChallengeContext challengeContext =
InvokeAuthenticationFiltersChallenge(controllerContext,
filterInfo.AuthenticationFilters, actionDescriptor, authorizationContext.Result);
continuation = => InvokeActionResult(controllerContext,
challengeContext.Result ?? authorizationContext.Result);
}
else
{
if (controllerContext.Controller.ValidateRequest)
{
ValidateRequest(controllerContext);
}
IDictionary
IAsyncResult asyncResult = BeginInvokeActionMethodWithFilters(controllerContext, filterInfo.ActionFilters, actionDescriptor, parameters, asyncCallback, asyncState);
continuation = =>
{
ActionExecutedContext postActionContext = EndInvokeActionMethodWithFilters(asyncResult);
AuthenticationChallengeContext challengeContext =
InvokeAuthenticationFiltersChallenge(controllerContext,
filterInfo.AuthenticationFilters, actionDescriptor,
postActionContext.Result);
InvokeActionResultWithFilters(controllerContext, filterInfo.ResultFilters,
challengeContext.Result ?? postActionContext.Result);
};
return asyncResult;
}
}
}
catch (ThreadAbortException)
{
throw;
}
catch (Exception ex)
{
ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
if (!exceptionContext.ExceptionHandled)
{
throw;
}
continuation = => InvokeActionResult(controllerContext, exceptionContext.Result);
}
return BeginInvokeAction_MakeSynchronousAsyncResult(asyncCallback, asyncState);
};
EndInvokeDelegate
{
try
{
continuation;
}
catch (ThreadAbortException)
{
throw;
}
catch (Exception ex)
{
ExceptionContext exceptionContext = InvokeExceptionFilters(controllerContext, filterInfo.ExceptionFilters, ex);
if (!exceptionContext.ExceptionHandled)
{
throw;
}
InvokeActionResult(controllerContext, exceptionContext.Result);
}
return true;
};
return AsyncResultWrapper.Begin(callback, state, beginDelegate, endDelegate, _invokeActionTag);
}
else
{
return BeginInvokeAction_ActionNotFound(callback, state);
}
}
這是一個非常重要的方法,也是我們後續分析的基礎,它描述了我們主要的處理流程。此方法中關聯了相當多的類,但是其主要處理流程是容易清晰明白的。大致是先獲取ControllerDescriptor和ActionDescriptor,然後依次執行Action中的過濾器AuthenticationFilters、AuthorizationFilters來進行驗證和授權檢查,然後是頁面安全檢查、獲取參數,然後執行ActionFilter和Action,整個過程中如果設置了Result會直接執行AuthenticationFilters的AuthenticationFiltersChallenge,然後執行ResultFilters對結果進行過濾,當然如果處理過程中拋出異常會調用ExceptionFilter進行處理。如果要理解處理的細節需要查看大量的源碼,這裡提供一點小小的參考ReflectedActionDescriptor、ReflectedControllerDescriptor為ActionDescriptor和ControllerDescriptor的默認實現,Action、過濾器等的查找實際都是通過反射類獲取的。
下面為BeginInvokeAction的處理過程圖
※Java 逆變與協變的名詞說明
※Jenkins的安裝配置
※webpack的Hot Module Replacement運行機制
TAG:科技優家 |
※MongoDB系統CentOS 7.1 crash的排障過程
※Nature Chemistry:Na2/3Mg0.28Mn0.72O2中無過量鹼金屬離子的氧的氧化還原化學過程
※《idol MOMS》Kang Ta公開H.O.T.重組籌備過程
※NBA 2019 全明星賽 Team LeBron 及 Team Giannis 隊長選人過程完整公開
※SHINee Key-log6日出擊 公開專輯製作過程
※OceanBase 0.4 自舉時庫表創建過程
※HttpURLConnection使用過程中踩的坑
※MapReduce中源碼分析(map端的過程)
※HackTheBox Writeup之拿下Mantis主機許可權過程
※NASA將直播OSIRIS-REx在小行星Bennu著陸過程
※SAP Fiori實施之Transactional Apps實施全過程
※IPhone X在升級IOS 12.1的過程中 爆! 炸! 了!
※Biomaterials:肺上皮再生過程中,Fibrillin-2和Tenascin-C消除了年齡差距的影響
※IPhone X在升級IOS 12.1的過程中 爆!炸!了!
※四方框框|Lidia Springer~「GIRL IN MASK」 精彩插圖繪製過程
※DeepLab v2及調試過程
※89年巴西小哥Tullius Heuer的創作過程
※Ultimaker Cura集成了HP 3D掃描軟體,以簡化3D列印過程!
※Ryan Reynolds 談論《Pokémon: Detective Pikachu》角色 Pikachu 學習過程
※Magic Leap 首次對外講述產品演化過程 | GDC 2018