MVC控制器执行

通过前篇MVC处理程序&Asp.Net路由机制我们了解了MVC路由解析及控制器创建。

得到控制器之后就是执行了

// System.Web.Mvc.IController
/// <summary>Executes the specified request context.</summary>
/// <param name="requestContext">The request context.</param>
void Execute(RequestContext requestContext);

IController的接口,就一个方法Execute的方法,参数为RequestConext

graph LR
IController-->ControllerBase
ControllerBase-->Controller

以上是Controller的继承关系

IContrller接口的Execute被ControllerBase实现

// System.Web.Mvc.ControllerBase
/// <summary>Executes the specified request context.</summary>
/// <param name="requestContext">The request context.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="requestContext" /> parameter is null.</exception>
protected virtual void Execute(RequestContext requestContext)
{
	if (requestContext == null)
	{
		throw new ArgumentNullException("requestContext");
	}
	if (requestContext.HttpContext == null)
	{
		throw new ArgumentException(MvcResources.ControllerBase_CannotExecuteWithNullHttpContext, "requestContext");
	}
	this.VerifyExecuteCalledOnce();
	this.Initialize(requestContext);
	using (ScopeStorage.CreateTransientScope())
	{
		this.ExecuteCore();
	}
}
// System.Web.Mvc.ControllerBase
/// <summary>Initializes the specified request context.</summary>
/// <param name="requestContext">The request context.</param>
protected virtual void Initialize(RequestContext requestContext)
{
	this.ControllerContext = new ControllerContext(requestContext, this);
}
// System.Web.Mvc.Controller
/// <summary>Initializes data that might not be available when the constructor is called.</summary>
/// <param name="requestContext">The HTTP context and route data.</param>
protected override void Initialize(RequestContext requestContext)
{
	base.Initialize(requestContext);
	this.Url = new UrlHelper(requestContext);
}

Execute方法,VerifyExecuteCalledOnce()看方法名的意思应该是验证是否是第一次执行,Initialize()就是将请求的上下文和Controller做个了绑定,ScopeStorage.CreateTransientScope()然后using了一个作用域,最后交给子类的ExecuteCore方法

// System.Web.Mvc.Controller
/// <summary>Invokes the action in the current controller context.</summary>
protected override void ExecuteCore()
{
	this.PossiblyLoadTempData();
	try
	{
		string actionName = Controller.GetActionName(this.RouteData);
		if (!this.ActionInvoker.InvokeAction(base.ControllerContext, actionName))
		{
			this.HandleUnknownAction(actionName);
		}
	}
	finally
	{
		this.PossiblySaveTempData();
	}
}

PossiblyLoadTempData()看方法名就知道是加载TempData,PossiblySaveTempData()是保存TempData

// System.Web.Mvc.Controller
internal void PossiblyLoadTempData()
{
	if (!base.ControllerContext.IsChildAction)
	{
		base.TempData.Load(base.ControllerContext, this.TempDataProvider);
	}
}
// System.Web.Mvc.TempDataDictionary
/// <summary>Loads the specified controller context by using the specified data provider.</summary>
/// <param name="controllerContext">The controller context.</param>
/// <param name="tempDataProvider">The temporary data provider.</param>
public void Load(ControllerContext controllerContext, ITempDataProvider tempDataProvider)
{
	IDictionary<string, object> dictionary = tempDataProvider.LoadTempData(controllerContext);
	this._data = ((dictionary != null) ? new Dictionary<string, object>(dictionary, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase));
	this._initialKeys = new HashSet<string>(this._data.Keys, StringComparer.OrdinalIgnoreCase);
	this._retainedKeys.Clear();
}
// System.Web.Mvc.SessionStateTempDataProvider
/// <summary>Loads the temporary data by using the specified controller context.</summary>
/// <returns>The temporary data.</returns>
/// <param name="controllerContext">The controller context.</param>
/// <exception cref="T:System.InvalidOperationException">An error occurred when the session context was being retrieved.</exception>
public virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
	HttpSessionStateBase session = controllerContext.HttpContext.Session;
	if (session != null)
	{
		Dictionary<string, object> dictionary = session["__ControllerTempData"] as Dictionary<string, object>;
		if (dictionary != null)
		{
			session.Remove("__ControllerTempData");
			return dictionary;
		}
	}
	return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
}
// System.Web.Mvc.Controller
internal void PossiblySaveTempData()
{
	if (!base.ControllerContext.IsChildAction)
	{
		base.TempData.Save(base.ControllerContext, this.TempDataProvider);
	}
}
// System.Web.Mvc.TempDataDictionary
/// <summary>Saves the specified controller context by using the specified data provider.</summary>
/// <param name="controllerContext">The controller context.</param>
/// <param name="tempDataProvider">The temporary data provider.</param>
public void Save(ControllerContext controllerContext, ITempDataProvider tempDataProvider)
{
	this._data.RemoveFromDictionary(delegate(KeyValuePair<string, object> entry, TempDataDictionary tempData)
	{
		string key = entry.Key;
		return !tempData._initialKeys.Contains(key) && !tempData._retainedKeys.Contains(key);
	}
	, this);
	tempDataProvider.SaveTempData(controllerContext, this._data);
}
// System.Web.Mvc.SessionStateTempDataProvider
/// <summary>Saves the specified values in the temporary data dictionary by using the specified controller context.</summary>
/// <param name="controllerContext">The controller context.</param>
/// <param name="values">The values.</param>
/// <exception cref="T:System.InvalidOperationException">An error occurred the session context was being retrieved.</exception>
public virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
	if (controllerContext == null)
	{
		throw new ArgumentNullException("controllerContext");
	}
	HttpSessionStateBase session = controllerContext.HttpContext.Session;
	bool flag = values != null && values.Count > 0;
	if (session == null)
	{
		if (flag)
		{
			throw new InvalidOperationException(MvcResources.SessionStateTempDataProvider_SessionStateDisabled);
		}
	}
	else
	{
		if (flag)
		{
			session["__ControllerTempData"] = values;
			return;
		}
		if (session["__ControllerTempData"] != null)
		{
			session.Remove("__ControllerTempData");
		}
	}
}

回到Controller ExecuteCore()方法中

string actionName = Controller.GetActionName(this.RouteData);

通过路由获取actionName,调用ActionInvoker.InvokeAction如果返回false,就抛出异常

// System.Web.Mvc.Controller
/// <summary>Gets the action invoker for the controller.</summary>
/// <returns>The action invoker.</returns>
public IActionInvoker ActionInvoker
{
	get
	{
		if (this._actionInvoker == null)
		{
			this._actionInvoker = this.CreateActionInvoker();
		}
		return this._actionInvoker;
	}
	set
	{
		this._actionInvoker = value;
	}
}
// System.Web.Mvc.Controller
/// <summary>Creates an action invoker.</summary>
/// <returns>An action invoker.</returns>
protected virtual IActionInvoker CreateActionInvoker()
{
	IAsyncActionInvokerFactory service = this.Resolver.GetService<IAsyncActionInvokerFactory>();
	if (service != null)
	{
		return service.CreateInstance();
	}
	IActionInvokerFactory service2 = this.Resolver.GetService<IActionInvokerFactory>();
	if (service2 != null)
	{
		return service2.CreateInstance();
	}
	IAsyncActionInvoker arg_4F_0;
	if ((arg_4F_0 = this.Resolver.GetService<IAsyncActionInvoker>()) == null)
	{
		arg_4F_0 = (this.Resolver.GetService<IActionInvoker>() ?? new AsyncControllerActionInvoker());
	}
	return arg_4F_0;
}

ActionInvoker默认取异步IAsyncActionInvoker,没有则取IActionInvoker,最终InvokeAction是在ControllerActionInvoker里面实现的

// System.Web.Mvc.ControllerActionInvoker
/// <summary>Invokes the specified action by using the specified controller context.</summary>
/// <returns>The result of executing the action.</returns>
/// <param name="controllerContext">The controller context.</param>
/// <param name="actionName">The name of the action to invoke.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="controllerContext" /> parameter is null.</exception>
/// <exception cref="T:System.ArgumentException">The <paramref name="actionName" /> parameter is null or empty.</exception>
/// <exception cref="T:System.Threading.ThreadAbortException">The thread was aborted during invocation of the action.</exception>
/// <exception cref="T:System.Exception">An unspecified error occurred during invocation of the action.</exception>
public virtual bool InvokeAction(ControllerContext controllerContext, string actionName)
{
	if (controllerContext == null)
	{
		throw new ArgumentNullException("controllerContext");
	}
	if (string.IsNullOrEmpty(actionName) && !controllerContext.RouteData.HasDirectRouteMatch())
	{
		throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
	}
	ControllerDescriptor controllerDescriptor = this.GetControllerDescriptor(controllerContext);
	ActionDescriptor actionDescriptor = this.FindAction(controllerContext, controllerDescriptor, actionName);
	if (actionDescriptor != null)
	{
		FilterInfo filters = this.GetFilters(controllerContext, actionDescriptor);
		try
		{
			AuthenticationContext authenticationContext = this.InvokeAuthenticationFilters(controllerContext, filters.AuthenticationFilters, actionDescriptor);
			if (authenticationContext.Result != null)
			{
				AuthenticationChallengeContext authenticationChallengeContext = this.InvokeAuthenticationFiltersChallenge(controllerContext, filters.AuthenticationFilters, actionDescriptor, authenticationContext.Result);
				this.InvokeActionResult(controllerContext, authenticationChallengeContext.Result ?? authenticationContext.Result);
			}
			else
			{
				AuthorizationContext authorizationContext = this.InvokeAuthorizationFilters(controllerContext, filters.AuthorizationFilters, actionDescriptor);
				if (authorizationContext.Result != null)
				{
					AuthenticationChallengeContext authenticationChallengeContext2 = this.InvokeAuthenticationFiltersChallenge(controllerContext, filters.AuthenticationFilters, actionDescriptor, authorizationContext.Result);
					this.InvokeActionResult(controllerContext, authenticationChallengeContext2.Result ?? authorizationContext.Result);
				}
				else
				{
					if (controllerContext.Controller.ValidateRequest)
					{
						ControllerActionInvoker.ValidateRequest(controllerContext);
					}
					IDictionary<string, object> parameterValues = this.GetParameterValues(controllerContext, actionDescriptor);
					ActionExecutedContext actionExecutedContext = this.InvokeActionMethodWithFilters(controllerContext, filters.ActionFilters, actionDescriptor, parameterValues);
					AuthenticationChallengeContext authenticationChallengeContext3 = this.InvokeAuthenticationFiltersChallenge(controllerContext, filters.AuthenticationFilters, actionDescriptor, actionExecutedContext.Result);
					this.InvokeActionResultWithFilters(controllerContext, filters.ResultFilters, authenticationChallengeContext3.Result ?? actionExecutedContext.Result);
				}
			}
		}
		catch (ThreadAbortException)
		{
			throw;
		}
		catch (Exception exception)
		{
			ExceptionContext exceptionContext = this.InvokeExceptionFilters(controllerContext, filters.ExceptionFilters, exception);
			if (!exceptionContext.ExceptionHandled)
			{
				throw;
			}
			this.InvokeActionResult(controllerContext, exceptionContext.Result);
		}
		return true;
	}
	return false;
}

ControllerActionInvoker.InvokeAction()先获得了两个Descriptor

// System.Web.Mvc.ControllerActionInvoker
/// <summary>Retrieves information about the controller by using the specified controller context.</summary>
/// <returns>Information about the controller.</returns>
/// <param name="controllerContext">The controller context.</param>
protected virtual ControllerDescriptor GetControllerDescriptor(ControllerContext controllerContext)
{
	Type type = controllerContext.Controller.GetType();
	return this.DescriptorCache.GetDescriptor<Type>(type, (Type innerType) => new ReflectedControllerDescriptor(innerType), type);
}
// System.Web.Mvc.ControllerActionInvoker
/// <summary>Finds the information about the action method.</summary>
/// <returns>Information about the action method.</returns>
/// <param name="controllerContext">The controller context.</param>
/// <param name="controllerDescriptor">The controller descriptor.</param>
/// <param name="actionName">The name of the action.</param>
protected virtual ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
{
	if (!controllerContext.RouteData.HasDirectRouteMatch())
	{
		return controllerDescriptor.FindAction(controllerContext, actionName);
	}
	List<DirectRouteCandidate> directRouteCandidates = ControllerActionInvoker.GetDirectRouteCandidates(controllerContext);
	DirectRouteCandidate directRouteCandidate = DirectRouteCandidate.SelectBestCandidate(directRouteCandidates, controllerContext);
	if (directRouteCandidate == null)
	{
		return null;
	}
	controllerContext.RouteData = directRouteCandidate.RouteData;
	controllerContext.RequestContext.RouteData = directRouteCandidate.RouteData;
	directRouteCandidate.RouteData.Values.RemoveFromDictionary((KeyValuePair<string, object> entry) => entry.Value == UrlParameter.Optional);
	return directRouteCandidate.ActionDescriptor;
}

首先获得Controller的Type,然后从DescriptorCache(缓存)获取ControllerDescriptor。如果没有的话 就使用ReflectedControllerDescriptor

// System.Web.Mvc.ControllerActionInvoker
/// <summary>Finds the information about the action method.</summary>
/// <returns>Information about the action method.</returns>
/// <param name="controllerContext">The controller context.</param>
/// <param name="controllerDescriptor">The controller descriptor.</param>
/// <param name="actionName">The name of the action.</param>
protected virtual ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
{
	if (!controllerContext.RouteData.HasDirectRouteMatch())
	{
		return controllerDescriptor.FindAction(controllerContext, actionName);
	}
	List<DirectRouteCandidate> directRouteCandidates = ControllerActionInvoker.GetDirectRouteCandidates(controllerContext);
	DirectRouteCandidate directRouteCandidate = DirectRouteCandidate.SelectBestCandidate(directRouteCandidates, controllerContext);
	if (directRouteCandidate == null)
	{
		return null;
	}
	controllerContext.RouteData = directRouteCandidate.RouteData;
	controllerContext.RequestContext.RouteData = directRouteCandidate.RouteData;
	directRouteCandidate.RouteData.Values.RemoveFromDictionary((KeyValuePair<string, object> entry) => entry.Value == UrlParameter.Optional);
	return directRouteCandidate.ActionDescriptor;
}
// System.Web.Mvc.Routing.DirectRouteExtensions
public static bool HasDirectRouteMatch(this RouteData routeData)
{
	if (routeData == null)
	{
		throw Error.ArgumentNull("routeData");
	}
	return routeData.Values.ContainsKey("MS_DirectRouteMatches");
}
// System.Web.Mvc.ReflectedControllerDescriptor
/// <summary>Finds the specified action for the specified controller context.</summary>
/// <returns>The information about the action.</returns>
/// <param name="controllerContext">The controller context.</param>
/// <param name="actionName">The name of the action.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="controllerContext" /> parameter is null.</exception>
/// <exception cref="T:System.ArgumentException">The <paramref name="actionName" /> parameter is null or empty.</exception>
public override ActionDescriptor FindAction(ControllerContext controllerContext, string actionName)
{
	if (controllerContext == null)
	{
		throw new ArgumentNullException("controllerContext");
	}
	if (string.IsNullOrEmpty(actionName))
	{
		throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
	}
	MethodInfo methodInfo = this._selector.FindActionMethod(controllerContext, actionName);
	if (methodInfo == null)
	{
		return null;
	}
	return new ReflectedActionDescriptor(methodInfo, actionName, this);
}

接下来是获取ActionDescriptor,先检查路由是否有MS_DirectRouteMatches,没有则返回控制器中的ActionDescriptor没有就提供一个ReflectedActionDescriptor,有则从路由中取

// System.Web.Mvc.ControllerActionInvoker
private static List<DirectRouteCandidate> GetDirectRouteCandidates(ControllerContext controllerContext)
{
	List<DirectRouteCandidate> list = new List<DirectRouteCandidate>();
	RouteData routeData = controllerContext.RouteData;
	foreach (RouteData current in routeData.GetDirectRouteMatches())
	{
		if (current != null)
		{
			if (current.GetTargetControllerDescriptor() == null)
			{
				throw new InvalidOperationException(MvcResources.DirectRoute_MissingControllerDescriptor);
			}
			ActionDescriptor[] targetActionDescriptors = current.GetTargetActionDescriptors();
			if (targetActionDescriptors == null || targetActionDescriptors.Length == 0)
			{
				throw new InvalidOperationException(MvcResources.DirectRoute_MissingActionDescriptors);
			}
			ActionDescriptor[] array = targetActionDescriptors;
			for (int i = 0; i < array.Length; i++)
			{
				ActionDescriptor actionDescriptor = array[i];
				if (actionDescriptor != null)
				{
					list.Add(new DirectRouteCandidate
					{
						ActionDescriptor = actionDescriptor, 
						ActionNameSelectors = actionDescriptor.GetNameSelectors(), 
						ActionSelectors = actionDescriptor.GetSelectors(), 
						Order = current.GetOrder(), 
						Precedence = current.GetPrecedence(), 
						RouteData = current
					});
				}
			}
		}
	}
	return list;
}
// System.Web.Mvc.Routing.DirectRouteExtensions
public static ActionDescriptor[] GetTargetActionDescriptors(this RouteData routeData)
{
	return routeData.GetRouteDataTokenValue("MS_DirectRouteActions");
}

回到ControllerActionInvoker.InvokeAction()中,获取两个Descriptor之后,获取FilterInfo

FilterInfo filters = this.GetFilters(controllerContext, actionDescriptor);
// System.Web.Mvc.ControllerActionInvoker
/// <summary>Retrieves information about the action filters.</summary>
/// <returns>Information about the action filters.</returns>
/// <param name="controllerContext">The controller context.</param>
/// <param name="actionDescriptor">The action descriptor.</param>
protected virtual FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
	return new FilterInfo(this._getFiltersThunk(controllerContext, actionDescriptor));
}
// System.Web.Mvc.ControllerActionInvoker
private Func<ControllerContext, ActionDescriptor, IEnumerable<Filter>> _getFiltersThunk = new Func<ControllerContext, ActionDescriptor, IEnumerable<Filter>>(FilterProviders.Providers.GetFilters);

using System;
namespace System.Web.Mvc
{
	/// <summary>Provides a registration point for filters.</summary>
	public static class FilterProviders
	{
		/// <summary>Provides a registration point for filters.</summary>
		/// <returns>The collection of filters.</returns>
		public static FilterProviderCollection Providers
		{
			get;
			private set;
		}
		static FilterProviders()
		{
			FilterProviders.Providers = new FilterProviderCollection();
			FilterProviders.Providers.Add(GlobalFilters.Filters);
			FilterProviders.Providers.Add(new FilterAttributeFilterProvider());
			FilterProviders.Providers.Add(new ControllerInstanceFilterProvider());
		}
	}
}

实例化FilterInfo需要注册Filter,在静态类FilterProviders中分别对Global、FilterAttribute、ControllerInstance的Filter注册,然后对Filter分类整理

/// <summary>Initializes a new instance of the <see cref="T:System.Web.Mvc.FilterInfo" /> class using the specified filters collection.</summary>
/// <param name="filters">The filters collection.</param>
public FilterInfo(IEnumerable<Filter> filters)
{
	FilterInfo.OverrideFilterInfo info = FilterInfo.ProcessOverrideFilters(filters);
	this.SplitFilters(info);
}
// System.Web.Mvc.FilterInfo
private static FilterInfo.OverrideFilterInfo ProcessOverrideFilters(IEnumerable<Filter> filters)
{
	FilterInfo.OverrideFilterInfo result = new FilterInfo.OverrideFilterInfo
	{
		ActionOverrideScope = FilterScope.First, 
		AuthenticationOverrideScope = FilterScope.First, 
		AuthorizationOverrideScope = FilterScope.First, 
		ExceptionOverrideScope = FilterScope.First, 
		ResultOverrideScope = FilterScope.First, 
		Filters = new List<Filter>()
	};
	foreach (Filter current in filters)
	{
		if (current != null)
		{
			IOverrideFilter overrideFilter = current.Instance as IOverrideFilter;
			if (overrideFilter != null)
			{
				if (overrideFilter.FiltersToOverride == typeof(IActionFilter) && current.Scope >= result.ActionOverrideScope)
				{
					result.ActionOverrideScope = current.Scope;
				}
				else
				{
					if (overrideFilter.FiltersToOverride == typeof(IAuthenticationFilter) && current.Scope >= result.AuthenticationOverrideScope)
					{
						result.AuthenticationOverrideScope = current.Scope;
					}
					else
					{
						if (overrideFilter.FiltersToOverride == typeof(IAuthorizationFilter) && current.Scope >= result.AuthorizationOverrideScope)
						{
							result.AuthorizationOverrideScope = current.Scope;
						}
						else
						{
							if (overrideFilter.FiltersToOverride == typeof(IExceptionFilter) && current.Scope >= result.ExceptionOverrideScope)
							{
								result.ExceptionOverrideScope = current.Scope;
							}
							else
							{
								if (overrideFilter.FiltersToOverride == typeof(IResultFilter) && current.Scope >= result.ResultOverrideScope)
								{
									result.ResultOverrideScope = current.Scope;
								}
							}
						}
					}
				}
			}
			result.Filters.Add(current);
		}
	}
	return result;
}
// System.Web.Mvc.FilterInfo
private void SplitFilters(FilterInfo.OverrideFilterInfo info)
{
	foreach (Filter current in info.Filters)
	{
		IActionFilter actionFilter = current.Instance as IActionFilter;
		if (actionFilter != null && current.Scope >= info.ActionOverrideScope)
		{
			this._actionFilters.Add(actionFilter);
		}
		IAuthenticationFilter authenticationFilter = current.Instance as IAuthenticationFilter;
		if (authenticationFilter != null && current.Scope >= info.AuthenticationOverrideScope)
		{
			this._authenticationFilters.Add(authenticationFilter);
		}
		IAuthorizationFilter authorizationFilter = current.Instance as IAuthorizationFilter;
		if (authorizationFilter != null && current.Scope >= info.AuthorizationOverrideScope)
		{
			this._authorizationFilters.Add(authorizationFilter);
		}
		IExceptionFilter exceptionFilter = current.Instance as IExceptionFilter;
		if (exceptionFilter != null && current.Scope >= info.ExceptionOverrideScope)
		{
			this._exceptionFilters.Add(exceptionFilter);
		}
		IResultFilter resultFilter = current.Instance as IResultFilter;
		if (resultFilter != null && current.Scope >= info.ResultOverrideScope)
		{
			this._resultFilters.Add(resultFilter);
		}
	}
}

回到ControllerActionInvoker.InvokeAction()继续执行InvokeAuthenticationFilters

// System.Web.Mvc.ControllerActionInvoker
protected virtual AuthenticationContext InvokeAuthenticationFilters(ControllerContext controllerContext, IList<IAuthenticationFilter> filters, ActionDescriptor actionDescriptor)
{
	if (controllerContext == null)
	{
		throw new ArgumentNullException("controllerContext");
	}
	IPrincipal user = controllerContext.HttpContext.User;
	AuthenticationContext authenticationContext = new AuthenticationContext(controllerContext, actionDescriptor, user);
	foreach (IAuthenticationFilter current in filters)
	{
		current.OnAuthentication(authenticationContext);
		if (authenticationContext.Result != null)
		{
			break;
		}
	}
	IPrincipal principal = authenticationContext.Principal;
	if (principal != user)
	{
		authenticationContext.HttpContext.User = principal;
		Thread.CurrentPrincipal = principal;
	}
	return authenticationContext;
}
// System.Web.Mvc.Filters.IAuthenticationFilter
/// <summary>Authenticates the request.</summary>
/// <param name="filterContext">The context to use for authentication.</param>
void OnAuthentication(AuthenticationContext filterContext);

执行所有注册的IAuthenticationFilter.OnAuthentication()方法,如果返回有Result则跳出循环,执行ControllerActionInvoker.InvokeAuthenticationFiltersChallenge()并执行所有注册的IAuthenticationFilter.OnAuthenticationChallenge()方法,最后执行ControllerActionInvoker.InvokeActionResult()

// System.Web.Mvc.ControllerActionInvoker
protected virtual AuthenticationChallengeContext InvokeAuthenticationFiltersChallenge(ControllerContext controllerContext, IList<IAuthenticationFilter> filters, ActionDescriptor actionDescriptor, ActionResult result)
{
	AuthenticationChallengeContext authenticationChallengeContext = new AuthenticationChallengeContext(controllerContext, actionDescriptor, result);
	foreach (IAuthenticationFilter current in filters)
	{
		current.OnAuthenticationChallenge(authenticationChallengeContext);
	}
	return authenticationChallengeContext;
}
// System.Web.Mvc.Filters.IAuthenticationFilter
/// <summary>Adds an authentication challenge to the current <see cref="T:System.Web.Mvc.ActionResult" />.</summary>
/// <param name="filterContext">The context to use for the authentication challenge.</param>
void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext);
// System.Web.Mvc.ControllerActionInvoker
/// <summary>Invokes the specified action result by using the specified controller context.</summary>
/// <param name="controllerContext">The controller context.</param>
/// <param name="actionResult">The action result.</param>
protected virtual void InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
{
	actionResult.ExecuteResult(controllerContext);
}

没有返回Result,继续执行ControllerActionInvoker.InvokeAuthorizationFilters

// System.Web.Mvc.ControllerActionInvoker
/// <summary>Invokes the specified authorization filters by using the specified action descriptor and controller context.</summary>
/// <returns>The context for the <see cref="T:System.Web.Mvc.AuthorizeAttribute" /> object.</returns>
/// <param name="controllerContext">The controller context.</param>
/// <param name="filters">The authorization filters.</param>
/// <param name="actionDescriptor">The action descriptor.</param>
protected virtual AuthorizationContext InvokeAuthorizationFilters(ControllerContext controllerContext, IList<IAuthorizationFilter> filters, ActionDescriptor actionDescriptor)
{
	AuthorizationContext authorizationContext = new AuthorizationContext(controllerContext, actionDescriptor);
	foreach (IAuthorizationFilter current in filters)
	{
		current.OnAuthorization(authorizationContext);
		if (authorizationContext.Result != null)
		{
			break;
		}
	}
	return authorizationContext;
}
// System.Web.Mvc.IAuthorizationFilter
/// <summary>Called when authorization is required.</summary>
/// <param name="filterContext">The filter context.</param>
void OnAuthorization(AuthorizationContext filterContext);

和前面同理,一个是身份验证,一个是检测是否授权

下一步ValidateRequest,验证请求

if (controllerContext.Controller.ValidateRequest)
{
     ControllerActionInvoker.ValidateRequest(controllerContext);
}
// System.Web.Mvc.ControllerActionInvoker
internal static void ValidateRequest(ControllerContext controllerContext)
{
	if (controllerContext.IsChildAction)
	{
		return;
	}
	HttpContext current = HttpContext.Current;
	if (current != null)
	{
		ValidationUtility.EnableDynamicValidation(current);
	}
	controllerContext.HttpContext.Request.ValidateInput();
}

ControllerBase默认设置需要验证请求

// System.Web.Mvc.ControllerBase
/// <summary>Gets or sets a value that indicates whether request validation is enabled for this request.</summary>
/// <returns>true if request validation is enabled for this request; otherwise, false. The default is true.</returns>
public bool ValidateRequest
{
	get
	{
		return this._validateRequest;
	}
	set
	{
		this._validateRequest = value;
	}
}
private bool _validateRequest = true;

下一步 绑定Action参数

IDictionary<string, object> parameterValues = this.GetParameterValues(controllerContext, actionDescriptor);

下一步 带ActionFilters并执行方法

ActionExecutedContext actionExecutedContext = this.InvokeActionMethodWithFilters(controllerContext, filters.ActionFilters, actionDescriptor, parameterValues);
// System.Web.Mvc.ControllerActionInvoker
/// <summary>Invokes the specified action method by using the specified parameters, controller context, and action filters.</summary>
/// <returns>The context for the ActionExecuted method of the <see cref="T:System.Web.Mvc.ActionFilterAttribute" /> class.</returns>
/// <param name="controllerContext">The controller context.</param>
/// <param name="filters">The action filters.</param>
/// <param name="actionDescriptor">The action descriptor.</param>
/// <param name="parameters">The parameters.</param>
protected virtual ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
{
	ActionExecutingContext preContext = new ActionExecutingContext(controllerContext, actionDescriptor, parameters);
	Func<ActionExecutedContext> seed = () => new ActionExecutedContext(controllerContext, actionDescriptor, false, null)
	{
		Result = this.InvokeActionMethod(controllerContext, actionDescriptor, parameters)
	};
	Func<ActionExecutedContext> func = filters.Reverse<IActionFilter>().Aggregate(seed, (Func<ActionExecutedContext> next, IActionFilter filter) => () => ControllerActionInvoker.InvokeActionMethodFilter(filter, preContext, next));
	return func();
}
// System.Web.Mvc.ControllerActionInvoker
/// <summary>Invokes the specified action method by using the specified parameters and the controller context.</summary>
/// <returns>The result of executing the action method.</returns>
/// <param name="controllerContext">The controller context.</param>
/// <param name="actionDescriptor">The action descriptor.</param>
/// <param name="parameters">The parameters.</param>
protected virtual ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
{
	object actionReturnValue = actionDescriptor.Execute(controllerContext, parameters);
	return this.CreateActionResult(controllerContext, actionDescriptor, actionReturnValue);
}
// System.Linq.Enumerable
[__DynamicallyInvokable]
public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func)
{
	if (source == null)
	{
		throw Error.ArgumentNull("source");
	}
	if (func == null)
	{
		throw Error.ArgumentNull("func");
	}
	TAccumulate tAccumulate = seed;
	foreach (TSource current in source)
	{
		tAccumulate = func(tAccumulate, current);
	}
	return tAccumulate;
}
// System.Web.Mvc.ControllerActionInvoker
internal static ActionExecutedContext InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func<ActionExecutedContext> continuation)
{
	filter.OnActionExecuting(preContext);
	if (preContext.Result != null)
	{
		return new ActionExecutedContext(preContext, preContext.ActionDescriptor, true, null)
		{
			Result = preContext.Result
		};
	}
	bool flag = false;
	ActionExecutedContext actionExecutedContext = null;
	try
	{
		actionExecutedContext = continuation();
	}
	catch (ThreadAbortException)
	{
		actionExecutedContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false, null);
		filter.OnActionExecuted(actionExecutedContext);
		throw;
	}
	catch (Exception exception)
	{
		flag = true;
		actionExecutedContext = new ActionExecutedContext(preContext, preContext.ActionDescriptor, false, exception);
		filter.OnActionExecuted(actionExecutedContext);
		if (!actionExecutedContext.ExceptionHandled)
		{
			throw;
		}
	}
	if (!flag)
	{
		filter.OnActionExecuted(actionExecutedContext);
	}
	return actionExecutedContext;
}

这里Filter执行流程类似于俄罗斯套娃

graph TD
A(ActionFilter_1.OnActionExecuting)-->B(ActionFilter_2.OnActionExecuting)
B-->|... ...|C(ActionFilter_n.OnActionExecuting)
C-->D(InvokeActionMethod)
D-->E(ActionFilter_n.OnActionExecuted)
E-->|... ...|F(ActionFilter_2.OnActionExecuted)
F-->G(ActionFilter_1.OnActionExecuted)

本文参考文档:

MVC控制器执行

上一篇:(2)ASP.NET Core3.1 Ocelot路由


下一篇:生成微博授权URL