了解.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源码
[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。
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。
在PostResolveRequestCache根据上下文比对查找到路由表中对应的RouteData后,并通过routeHandler.GetHttpHandler(requestContext)获得MvcHandler的实例。
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。
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的代码
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。
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)) 反射执行的Action。requiredString参数为Action的名字
那这个Action被反射执行前后还有其它的业务吗? 记得我们在.net mvc中看到每个Action方法或者Controller基类被贴了一些属性标签,Action执行前后又触发了哪些方面呢?
· Action执行前 Controller的OnActionExecuting先触发
· Action执行后 Controller的OnActionExecuted被触发
· Action执行前后 贴有继承ActionFilterAttribute属性标签中的OnActionExecuting,OnActionExecuted被执行(还有其的Attribute如权限)
也就说ControllerActionInvoker 的InvokeAction方法在执行前还对Action和Controller进行了属性和Filter继承的扫描,来看InvokeAction的具体内容
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);
上面两段代码是读取Controller和Action的所有描述信息包括标签属性
FilterInfo filters = this.GetFilters(controllerContext, actionDescriptor); 这段代码是反射前面读到的所有具体的属性
ActionExecutedContext context2 = this.InvokeActionMethodWithFilters(controllerContext, filters.ActionFilters, actionDescriptor, parameterValues);
通过InvokeActionMethodWithFilters 先执行Action前置拦截的方法,然后Action,接着后置拦截的方法。 是不是实现了AOP的部分功能了?当然它没做到AOP的环绕拦截功能,但相比WebForm却是进步了很多。
其实上面的InvokeAction具体怎么实现前后拦截我也没看出来,我是通过实现了两个类的重写跟进断点查看的。
五、重写Controller与ControllerActionInvoker
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结构
当断点走到这个位置时
MyController及FilterAttribute属性的OnActionExecuting先被执行,接着Action被执行,然后MyController及FilterAttribute的OnActionExecuted也被执行了。
从客户端请求到Controller的具体Action执行的流程基本上走了一遍,对.net mvc机制的也有了一定的了解,相信对.net mvc开发有很大帮助,至少在理解的基础上才能充分发挥他的优点及特性。下一节我们继续通过Refletor去了解Action的返回结果ActionResult是如何返回绑定值到视图以及视图引擎的机制。
.net MVC3 加入了很多新特性,例如客户端远程验证RemoteAttribute,新的视图引擎Razor等。
补充:Filter被读取后,要按照一定的顺序来执行。我们看一下ControllerActionInvoker的InvokeAction读取所有标签属性的方法
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的参数是如何转变成对象的。