了解.net MVC的实现原理Controller/Action

通过Reflector反编译,我们对IIS分发请求至w3wp.exe进程之后交由HttpRuntime处理过程的分析了解HttpApplication,HttpModule,HttpHandler,HttpContext的生成机制。那我们继续来了解.net MVC 路由的如何实现URL跳转到指定的Controller/Action过程。

  • UrlRoutingModule
  • MvcRoutingHandler
  • MvcHandler
  • Controller

一、UrlRoutingModule

当所有HttpModule被HttpApplication首次加载后,每个HttpModule会在自己的Init方法中注册HttpApplication事件实现对HttpRequest请求的拦截。当然UrlRoutingModule也不例外。我们反编译一下UrlRoutingModule源码 

View Code
   
   
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal), AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
public class UrlRoutingModule : IHttpModule
{
// Fields
private static readonly object _requestDataKey = new object ();
private RouteCollection _routeCollection;

// Methods
protected virtual void Dispose()
{
}

protected virtual void Init(HttpApplication application)
{
application.PostResolveRequestCache
+= new EventHandler( this .OnApplicationPostResolveRequestCache);
application.PostMapRequestHandler
+= new EventHandler( this .OnApplicationPostMapRequestHandler);
}

private void OnApplicationPostMapRequestHandler( object sender, EventArgs e)
{
HttpContextBase context
= new HttpContextWrapper(((HttpApplication) sender).Context);
this .PostMapRequestHandler(context);
}

private void OnApplicationPostResolveRequestCache( object sender, EventArgs e)
{
HttpContextBase context
= new HttpContextWrapper(((HttpApplication) sender).Context);
this .PostResolveRequestCache(context);
}

public virtual void PostMapRequestHandler(HttpContextBase context)
{
RequestData data
= (RequestData) context.Items[_requestDataKey];
if (data != null )
{
context.RewritePath(data.OriginalPath);
context.Handler
= data.HttpHandler;
}
}

public virtual void PostResolveRequestCache(HttpContextBase context)
{
RouteData routeData
= this .RouteCollection.GetRouteData(context);
if (routeData != null )
{
IRouteHandler routeHandler
= routeData.RouteHandler;
if (routeHandler == null )
{
throw new InvalidOperationException( string .Format(CultureInfo.CurrentUICulture, RoutingResources.UrlRoutingModule_NoRouteHandler, new object [ 0 ]));
}
if ( ! (routeHandler is StopRoutingHandler))
{
RequestContext requestContext
= new RequestContext(context, routeData);
IHttpHandler httpHandler
= routeHandler.GetHttpHandler(requestContext);
if (httpHandler == null )
{
throw new InvalidOperationException( string .Format(CultureInfo.CurrentUICulture, RoutingResources.UrlRoutingModule_NoHttpHandler, new object [] { routeHandler.GetType() }));
}
RequestData data2
= new RequestData();
data2.OriginalPath
= context.Request.Path;
data2.HttpHandler
= httpHandler;
context.Items[_requestDataKey]
= data2;
context.RewritePath(
" ~/UrlRouting.axd " );
}
}
}

void IHttpModule.Dispose()
{
this .Dispose();
}

void IHttpModule.Init(HttpApplication application)
{
this .Init(application);
}

// Properties
public RouteCollection RouteCollection
{
get
{
if ( this ._routeCollection == null )
{
this ._routeCollection = RouteTable.Routes;
}
return this ._routeCollection;
}
set
{
this ._routeCollection = value;
}
}

// Nested Types
private class RequestData
{
// Fields
[CompilerGenerated]
private IHttpHandler < HttpHandler > k__BackingField;
[CompilerGenerated]
private string < OriginalPath > k__BackingField;

// Properties
public IHttpHandler HttpHandler
{
[CompilerGenerated]
get
{
return this . < HttpHandler > k__BackingField;
}
[CompilerGenerated]
set
{
this . < HttpHandler > k__BackingField = value;
}
}

public string OriginalPath
{
[CompilerGenerated]
get
{
return this . < OriginalPath > k__BackingField;
}
[CompilerGenerated]
set
{
this . < OriginalPath > k__BackingField = value;
}
}
}
}

可以看到UrlRoutingModule订阅的两个 HttpApplication 事件。其中PostResolveRequestCache 要比 PostMapRequestHandler 先执行,先看PostResolveRequestCache 事件,他首先从首先从 RouteCollection 中获取一个 RouteData 对象。而RouteCollection是通过路由表创建的

  
  
// Properties
public RouteCollection RouteCollection
{
get
{
if ( this ._routeCollection == null )
{
this ._routeCollection = RouteTable.Routes;
}
return this ._routeCollection;
}
set
{
this ._routeCollection = value;
}
}

我们在Global.asax.cs注册路由的时候(即HttpApplication),就已经往RouteTable.Routes添加了新的路由信息了,因此HttpModule可以从自己的RouteCollection查找到对应请求的路由。RouteTable只有一个静态集合属性RouteCollection。

View Code
   
   
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute(
" {resource}.axd/{*pathInfo} " );

routes.MapRoute(
" Default " , // Route name
" {controller}/{action}/{id} " , // URL with parameters
new { controller = " Home " , action = " Index " , id = UrlParameter.Optional } // Parameter defaults
);

}

protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);

}
}

UrlRoutingModule 在 PostResolveRequestCache方法读取  RouteData routeData = this.RouteCollection.GetRouteData(context);

RouteData是包含了我们注册的每个Route的信息。在global中注册路由的时候,MapRote是RouteCollection扩展方法,默认使用了MvcRouteHandler。

  
  
public static Route MapRoute( this RouteCollection routes, string name, string url, object defaults, object constraints, string [] namespaces)
{
if (routes == null )
{
throw new ArgumentNullException( " routes " );
}
if (url == null )
{
throw new ArgumentNullException( " url " );
}
Route route2
= new Route(url, new MvcRouteHandler());
route2.Defaults
= new RouteValueDictionary(defaults);
route2.Constraints
= new RouteValueDictionary(constraints);
route2.DataTokens
= new RouteValueDictionary();
Route item
= route2;
if ((namespaces != null ) && (namespaces.Length > 0 ))
{
item.DataTokens[
" Namespaces " ] = namespaces;
}
routes.Add(name, item);
return item;
}

二、MvcRoutingHandler 

RouteData结构除了包含的Route信息还有默认的IRouteHandler即MvcRouteHandler。

了解.net MVC的实现原理Controller/Action_第1张图片

在PostResolveRequestCache根据上下文比对查找到路由表中对应的RouteData后,并通过routeHandler.GetHttpHandler(requestContext)获得MvcHandler的实例。

View Code
   
   
public class MvcRouteHandler : IRouteHandler
{
// Methods
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new MvcHandler(requestContext);
}

IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
{
return this .GetHttpHandler(requestContext);
}
}

三、 MvcHandler

       从上面的过程可以看到UrlRouteModule每次拦截HttpApplication管线请求,先从RouteCollection找到与HttpContext请求对应的RouteData(包含MvcRouteHandler),并将HttpContextBase与RouteData封装为RequestContext,通过构造注入MvcHandler之中。MvcRouteHandler其实只起了一个过渡的作用,真正的工作是HttpApplication管线主导,在UrlRouteModule与MvcHandler之间进行的。

 UrlRouteModule在 PostResolveRequestCache方法中通过MvcRouteModule获取到MvcHandler的实例后,

  
  
RequestData data2 = new RequestData();
data2.OriginalPath
= context.Request.Path;
data2.HttpHandler
= httpHandler;
context.Items[_requestDataKey]
= data2;
context.RewritePath(
" ~/UrlRouting.axd " );

MvcHandler开始接管默认的WebForm的HttpHandler,注入到RequestData中并装入context.Items传递到管线中。而这个context对象之前也一并封装为RequestContext注入到了MvcHandler中,因此我们在MvcHandler后续过程中也可以取到上下文(Page或Controller等)。这个RequestData是UrlRouteModule的内部私有类,所以重载的时候没法实现它,如果想重写UrlRouteModule就要自己实现这个类了。

      至此,了解从UrlRouteModule到MvcRouteHandler再到MvcHandler的创建过程,但是最重要的问题MvcHandler是如何工作的?UrlRouteModule在拦截了HttpApplication的管线请求后,根据路由表找到了对由的RouteData(路由信息与MvcHandler),替换掉始终贯穿整个过程HttpContext中处理请求的HttpHandler,即MvcHandler。这些工作完成后,HttpApplication管线要继续处理Step事件,最终调用了MvcHandler 的ProcessRequest或BeginProcessRequest方法,从而进入到Controller的过程中。

      MvcHandler的触发过程:->HttpRuntime获取到HttpApplication->加载HttpModule,生成StepManager管线并加载MvcHandler->HttpRuntime调用HttpApplication.BeginProcessRequest->HttpApplication.StepManager.ResumeSteps

->执行StepManager中的HttpApplication.StepManager._execSteps[i].Execute ->MvcHandlers.ProcessRequest或BeginProcessRequest

下面是MvcHandler的反编译源码,可以看到除了要继承IHttpHandler,还继承了IRequireSeesionState 。自定义的HttpHandler想处理Session的话必须继承IRequireSeesionState。

View Code
   
   
public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
{
// Fields
private ControllerBuilder _controllerBuilder;
private static readonly object _processRequestTag = new object ();
[CompilerGenerated]
private static bool < DisableMvcResponseHeader > k__BackingField;
[CompilerGenerated]
private RequestContext < RequestContext > k__BackingField;
internal static readonly string MvcVersion = GetMvcVersionString();
public static readonly string MvcVersionHeaderName = " X-AspNetMvc-Version " ;

// Methods
public MvcHandler(RequestContext requestContext)
{
if (requestContext == null )
{
throw new ArgumentNullException( " requestContext " );
}
this .RequestContext = requestContext;
}

protected internal virtual void AddVersionHeader(HttpContextBase httpContext)
{
if ( ! DisableMvcResponseHeader)
{
httpContext.Response.AppendHeader(MvcVersionHeaderName, MvcVersion);
}
}

protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state)
{
HttpContextBase base2
= new HttpContextWrapper(httpContext);
return this .BeginProcessRequest(base2, callback, state);
}

protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state)
{
BeginInvokeDelegate delegate4
= null ;
EndInvokeDelegate delegate5
= null ;
Action action2
= null ;
IController controller;
IControllerFactory factory;
this .ProcessRequestInit(httpContext, out controller, out factory);
IAsyncController asyncController
= controller as IAsyncController;
if (asyncController != null )
{
if (delegate4 == null )
{
delegate4
= delegate (AsyncCallback asyncCallback, object asyncState) {
IAsyncResult result;
try
{
result
= asyncController.BeginExecute( this .RequestContext, asyncCallback, asyncState);
}
catch
{
factory.ReleaseController(asyncController);
throw ;
}
return result;
};
}
BeginInvokeDelegate beginDelegate
= delegate4;
if (delegate5 == null )
{
delegate5
= delegate (IAsyncResult asyncResult) {
try
{
asyncController.EndExecute(asyncResult);
}
finally
{
factory.ReleaseController(asyncController);
}
};
}
EndInvokeDelegate endDelegate
= delegate5;
SynchronizationContext synchronizationContext
= SynchronizationContextUtil.GetSynchronizationContext();
return AsyncResultWrapper.Begin(AsyncUtil.WrapCallbackForSynchronizedExecution(callback, synchronizationContext), state, beginDelegate, endDelegate, _processRequestTag);
}
if (action2 == null )
{
action2
= delegate {
try
{
controller.Execute(
this .RequestContext);
}
finally
{
factory.ReleaseController(controller);
}
};
}
Action action
= action2;
return AsyncResultWrapper.BeginSynchronous(callback, state, action, _processRequestTag);
}

protected internal virtual void EndProcessRequest(IAsyncResult asyncResult)
{
AsyncResultWrapper.End(asyncResult, _processRequestTag);
}

private static string GetMvcVersionString()
{
return new AssemblyName( typeof (MvcHandler).Assembly.FullName).Version.ToString( 2 );
}

protected virtual void ProcessRequest(HttpContext httpContext)
{
HttpContextBase base2
= new HttpContextWrapper(httpContext);
this .ProcessRequest(base2);
}

protected internal virtual void ProcessRequest(HttpContextBase httpContext)
{
IController controller;
IControllerFactory factory;
this .ProcessRequestInit(httpContext, out controller, out factory);
try
{
controller.Execute(
this .RequestContext);
}
finally
{
factory.ReleaseController(controller);
}
}

private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
this .AddVersionHeader(httpContext);
this .RemoveOptionalRoutingParameters();
string requiredString = this .RequestContext.RouteData.GetRequiredString( " controller " );
factory
= this .ControllerBuilder.GetControllerFactory();
controller
= factory.CreateController( this .RequestContext, requiredString);
if (controller == null )
{
throw new InvalidOperationException( string .Format(CultureInfo.CurrentUICulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object [] { factory.GetType(), requiredString }));
}
}

private void RemoveOptionalRoutingParameters()
{
RouteValueDictionary values
= this .RequestContext.RouteData.Values;
foreach ( string str in values.Where < KeyValuePair < string , object >> ( delegate (KeyValuePair < string , object > entry) {
return (entry.Value == UrlParameter.Optional);
}).Select
< KeyValuePair < string , object > , string > ( delegate (KeyValuePair < string , object > entry) {
return entry.Key;
}).ToArray
< string > ())
{
values.Remove(str);
}
}

IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb,
object extraData)
{
return this .BeginProcessRequest(context, cb, extraData);
}

void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)
{
this .EndProcessRequest(result);
}

void IHttpHandler.ProcessRequest(HttpContext httpContext)
{
this .ProcessRequest(httpContext);
}

// Properties
internal ControllerBuilder ControllerBuilder
{
get
{
if ( this ._controllerBuilder == null )
{
this ._controllerBuilder = ControllerBuilder.Current;
}
return this ._controllerBuilder;
}
set
{
this ._controllerBuilder = value;
}
}

public static bool DisableMvcResponseHeader
{
[CompilerGenerated]
get
{
return < DisableMvcResponseHeader > k__BackingField;
}
[CompilerGenerated]
set
{
< DisableMvcResponseHeader > k__BackingField = value;
}
}

protected virtual bool IsReusable
{
get
{
return false ;
}
}

public RequestContext RequestContext
{
[CompilerGenerated]
get
{
return this . < RequestContext > k__BackingField;
}
[CompilerGenerated]
private set
{
this . < RequestContext > k__BackingField = value;
}
}

bool IHttpHandler.IsReusable
{
get
{
return this .IsReusable;
}
}
}


四、Controller登场

看下MvcHandler的ProcessRequest或BeginProcessRequest, 我们看一下ProcessRequest方法的细节

  
  
protected internal virtual void ProcessRequest(HttpContextBase httpContext)
{
IController controller;
IControllerFactory factory;
this .ProcessRequestInit(httpContext, out controller, out factory);
try
{
controller.Execute(
this .RequestContext);
}
finally
{
factory.ReleaseController(controller);
}
}

private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
this .AddVersionHeader(httpContext);
this .RemoveOptionalRoutingParameters();
string requiredString = this .RequestContext.RouteData.GetRequiredString( " controller " );
factory
= this .ControllerBuilder.GetControllerFactory();
controller
= factory.CreateController( this .RequestContext, requiredString);
if (controller == null )
{
throw new InvalidOperationException( string .Format(CultureInfo.CurrentUICulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object [] { factory.GetType(), requiredString }));
}
}

主要是通过ProcessRequestInit 获取IControllerFactory 工厂,工厂再通过上下文中的地址反射Controller实例。那这个工厂哪里来的呢?

factory = this.ControllerBuilder.GetControllerFactory(); 看一下这个ControllerBuilder 是不是很熟悉? 看看Global.asax.cs的代码

View Code
   
   
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Threading;

namespace MvcApplication
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
#region
public static void RegisterRoutes(RouteCollection routes)
{
// routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(
" Default " , // Route name
" {controller}/{action}/{id} " , // URL with parameters
new { controller = " Home " , action = " Index " , id = UrlParameter.Optional } // Parameter defaults
);
}
#endregion

protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
// DefaultControllerFactory 每次都是根据上下文中的Action字符串反射Controller实例
// 可以重写DefaultControllerFactory 利用IOC容器实现Controller实例化工作(提高效率)
ControllerBuilder.Current.SetControllerFactory( new DefaultControllerFactory()); // Controller的反射工厂

}
}
}

一般大家会在这个时候把ControllerFactory注入,即使用默认的DefaultControllerFactory或自己利用IOC容器实现新的ControllerFactory工厂。
在ControllerFactory获取Controller并执行了 controller.Execute(this.RequestContext),就进入了Controller的具体业务了。

IController接口只有一个方法,ControllerBase继承IController,Controller又继承实现ControllerBase,而我们的具体业务的Controller名都是以文件夹名+Controller,并且继承了Controller。

  
  
public class AccountController : Controller
public class HomeController : Controller

如果想实现一现共用的业务比如:验证用户,验证权限等。我们可以再实现一个MyController继承基类Controller,然后其它的XXXController都继承这个MyController。当然也可以利用MVC提供的Filter机制(类似AOP拦截)来实现这些基本的业务验证,是不是比WebForm方便多了?

Execute方法只在ControllerBase中实现了,而Controller是直接继承的,我们的Controller也是直接继承的Controller。

View Code
   
   
protected virtual void Execute(RequestContext requestContext)
{
if (requestContext == null )
{
throw new ArgumentNullException( " requestContext " );
}
this .VerifyExecuteCalledOnce();
this .Initialize(requestContext);
this .ExecuteCore();
}

protected virtual void Initialize(RequestContext requestContext)
{
this .ControllerContext = new ControllerContext(requestContext, this );
}

protected abstract void ExecuteCore();

Execute主要的业务在放在了ExecuteCore中了,而这个ExecuteCore没有任何代码,只是在Controller中又被重载了业务。(看来ExecuteCore的业务还是被下放到了Controller中了,而Initalize只是RequestContext封装成ControllerContext,微软很有意思每一个环节都重新封装个上下文)。

在看Controller的ExecuteCore的业务

  
  
protected override void ExecuteCore()
{
this .PossiblyLoadTempData();
try
{
string requiredString = this .RouteData.GetRequiredString( " action " );
if ( ! this .ActionInvoker.InvokeAction( base .ControllerContext, requiredString))
{
this .HandleUnknownAction(requiredString);
}
}
finally
{
this .PossiblySaveTempData();
}
}

public IActionInvoker ActionInvoker
{
get
{
if ( this ._actionInvoker == null )
{
this ._actionInvoker = this .CreateActionInvoker();
}
return this ._actionInvoker;
}
set
{
this ._actionInvoker = value;
}
}

protected virtual IActionInvoker CreateActionInvoker()
{
return new ControllerActionInvoker();
}

ExectueCore调用this.ActionInvoker.InvokeAction(base.ControllerContext, requiredString)) 反射执行的ActionrequiredString参数为Action的名字

那这个Action被反射执行前后还有其它的业务吗? 记得我们在.net mvc中看到每个Action方法或者Controller基类被贴了一些属性标签,Action执行前后又触发了哪些方面呢?

·     Action执行前 ControllerOnActionExecuting先触发

·     Action执行后 ControllerOnActionExecuted被触发

·     Action执行前后 贴有继承ActionFilterAttribute属性标签中的OnActionExecutingOnActionExecuted被执行(还有其的Attribute如权限)

也就说ControllerActionInvoker InvokeAction方法在执行前还对ActionController进行了属性和Filter继承的扫描,来看InvokeAction的具体内容


View Code
   
   
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 actionDescriptor
= this .FindAction(controllerContext, controllerDescriptor, actionName);
if (actionDescriptor == null )
{
return false ;
}
FilterInfo filters
= this .GetFilters(controllerContext, actionDescriptor);
try
{
AuthorizationContext context
= this .InvokeAuthorizationFilters(controllerContext, filters.AuthorizationFilters, actionDescriptor);
if (context.Result != null )
{
this .InvokeActionResult(controllerContext, context.Result);
}
else
{
if (controllerContext.Controller.ValidateRequest)
{
ValidateRequest(controllerContext);
}
IDictionary
< string , object > parameterValues = this .GetParameterValues(controllerContext, actionDescriptor);
ActionExecutedContext context2
= this .InvokeActionMethodWithFilters(controllerContext, filters.ActionFilters, actionDescriptor, parameterValues);
this .InvokeActionResultWithFilters(controllerContext, filters.ResultFilters, context2.Result);
}
}
catch (ThreadAbortException)
{
throw ;
}
catch (Exception exception)
{
ExceptionContext context3
= this .InvokeExceptionFilters(controllerContext, filters.ExceptionFilters, exception);
if ( ! context3.ExceptionHandled)
{
throw ;
}
this .InvokeActionResult(controllerContext, context3.Result);
}
return true ;
}

ControllerDescriptor controllerDescriptor = this.GetControllerDescriptor(controllerContext);
ActionDescriptor actionDescriptor =
this.FindAction(controllerContext, controllerDescriptor, actionName);
上面两段代码是读取ControllerAction的所有描述信息包括标签属性

FilterInfo filters = this.GetFilters(controllerContext, actionDescriptor); 这段代码是反射前面读到的所有具体的属性

ActionExecutedContext context2 = this.InvokeActionMethodWithFilters(controllerContext, filters.ActionFilters, actionDescriptor, parameterValues);

通过InvokeActionMethodWithFilters 先执行Action前置拦截的方法,然后Action,接着后置拦截的方法。 是不是实现了AOP的部分功能了?当然它没做到AOP的环绕拦截功能,但相比WebForm却是进步了很多。

其实上面的InvokeAction具体怎么实现前后拦截我也没看出来,我是通过实现了两个类的重写跟进断点查看的。

五、重写ControllerControllerActionInvoker

下面是重写的代码  

View Code
     
     
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.IO;

namespace MvcApplication.Module
{
public class MyController:Controller
{
public MyController()
{
// this.ActionInvoker = new MyControllerActionInvoker();
}

protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base .OnActionExecuting(filterContext);
}

protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
base .OnActionExecuted(filterContext);
}

/// <summary>
/// 具本的Action及前后拦截方法的调用者
/// </summary>
/// <returns></returns>
protected override IActionInvoker CreateActionInvoker()
{
return new MyControllerActionInvoker();
}
}

public class MyControllerActionInvoker : ControllerActionInvoker
{
public override bool InvokeAction(System.Web.Mvc.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 actionDescriptor
= this .FindAction(controllerContext, controllerDescriptor, actionName);
if (actionDescriptor == null )
{
return false ;
}
// 注意这里
FilterInfo filters = this .GetFilters(controllerContext, actionDescriptor);
try
{
AuthorizationContext context
= this .InvokeAuthorizationFilters(controllerContext, filters.AuthorizationFilters, actionDescriptor);
if (context.Result != null )
{
this .InvokeActionResult(controllerContext, context.Result);
}
else
{
if (controllerContext.Controller.ValidateRequest)
{
// ValidateRequest(controllerContext);
}
IDictionary
< string , object > parameterValues = this .GetParameterValues(controllerContext, actionDescriptor);
// 这里方法执行后,OnActionExecuting,OnActionExecuted,Action分别相应被触发
ActionExecutedContext context2 = this .InvokeActionMethodWithFilters(controllerContext, filters.ActionFilters, actionDescriptor, parameterValues);
this .InvokeActionResultWithFilters(controllerContext, filters.ResultFilters, context2.Result);
}
}
catch (IOException ex)
{
throw ;
}
catch (Exception exception)
{
ExceptionContext context3
= this .InvokeExceptionFilters(controllerContext, filters.ExceptionFilters, exception);
if ( ! context3.ExceptionHandled)
{
throw ;
}
this .InvokeActionResult(controllerContext, context3.Result);
}
return true ;

}
}

/// <summary>
/// 自定义拦截标签属性用于测试
/// </summary>
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base .OnActionExecuted(filterContext);
}

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base .OnActionExecuting(filterContext);
}
}
}

调试后进到MyControllerActionInvoker.InvokeAction 观察filters结构

了解.net MVC的实现原理Controller/Action_第2张图片

 

当断点走到这个位置时

MyControllerFilterAttribute属性的OnActionExecuting先被执行,接着Action被执行,然后MyControllerFilterAttributeOnActionExecuted也被执行了。

从客户端请求到Controller的具体Action执行的流程基本上走了一遍,对.net mvc机制的也有了一定的了解,相信对.net mvc开发有很大帮助,至少在理解的基础上才能充分发挥他的优点及特性。下一节我们继续通过Refletor去了解Action的返回结果ActionResult是如何返回绑定值到视图以及视图引擎的机制。

.net MVC3 加入了很多新特性,例如客户端远程验证RemoteAttribute,新的视图引擎Razor等。

补充:Filter被读取后,要按照一定的顺序来执行。我们看一下ControllerActionInvoker的InvokeAction读取所有标签属性的方法

View Code
                 
                 
protected virtual FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
FilterInfo filters
= actionDescriptor.GetFilters();
ControllerBase controller
= controllerContext.Controller;
AddControllerToFilterList
< IActionFilter > (controller, filters.ActionFilters);
AddControllerToFilterList
< IResultFilter > (controller, filters.ResultFilters);
AddControllerToFilterList
< IAuthorizationFilter > (controller, filters.AuthorizationFilters);
AddControllerToFilterList
< IExceptionFilter > (controller, filters.ExceptionFilters);
return filters;
}

分别读取了IActionFilter,IAuthorizationFilter,IResultFilter,IExceptionFilter 属性标签,当然也包括Controller本身。

然后继续看InvokeAction执行的代码:

最先执行的是IAuthorizationFilter

   AuthorizationContext context = this.InvokeAuthorizationFilters(controllerContext, filters.AuthorizationFilters, actionDescriptor);

其次是IActionFilter(Action也发生在这个位置)

   ActionExecutedContext context2 = this.InvokeActionMethodWithFilters(controllerContext, filters.ActionFilters, actionDescriptor

仔细找找发现IResultFilter 被放在context2后面的这个方法里执行的

   this.InvokeActionResultWithFilters(controllerContext, filters.ResultFilters, context2.Result);

  最后是IExceptionFilter

   ExceptionContext context3 = this.InvokeExceptionFilters(controllerContext, filters.ExceptionFilters, exception);

上面就是Filter与Action的执行顺序。以前鄙视人家变量这A1,A2,A3的,微软也这么干,我也不好说啥了(-_-|||)。

还剩下一个问题? 传给Action的参数是如何转换成对象的? 这个我还没有看到代码是怎么实现的。因为MVC源码,就不必再Refletor看了,明天下了源码去分析传给Action的参数是如何转变成对象的。

你可能感兴趣的:(controller)