做ASP.NET WebForm开发都知道,ASP.NET有复杂的生命周期,学习ASP.NET MVC就要深入理解它的生命周期。今天从CodePlex上下载了ASP.NET Preview 2的源代码,还有两个程序集Routing与Abstractions并未发布,不过这两个程序集的类并不多,可以用NET反编译工具 Reflector解开来看看,可惜这两个程序集用的是VS2008使用.net 3.5开发的,用了c# 3.0的很多特性,Reflector反编译不完全。
ASP.NET MVC通过HttpModule(UrlRoutingModule)开始他的执行流程
<httpModules>
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing" />
</httpModules>
代码如下:
namespace System.Web.Routing
{
using System;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Security.Permissions;
using System.Web;
using System.Web.Resources;
[AspNetHostingPermission(SecurityAction.InheritanceDemand,Level=AspNetHostingPermissionLevel.Minimal),AspNetHostingPermission(SecurityAction.LinkDemand,Level=AspNetHostingPermissionLevel.Minimal)]
public class UrlRoutingModule : IHttpModule
{
private static readonly object _requestDataKey = new object();
private System.Web.Routing.RouteCollection _routeCollection;
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 HttpContextWrapper2(((HttpApplication) sender).Context);
this.PostMapRequestHandler(context);
}
private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
{
HttpContextBase context = new HttpContextWrapper2(((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 newInvalidOperationException(string.Format(CultureInfo.CurrentUICulture,RoutingResources.UrlRoutingModule_NoRouteHandler, new object[0]));
}
RequestContext requestContext = new RequestContext(context, routeData);
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
if (httpHandler == null)
{
throw newInvalidOperationException(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);
}
public System.Web.Routing.RouteCollection RouteCollection
{
get
{
if (this._routeCollection == null)
{
this._routeCollection = RouteTable.Routes;
}
return this._routeCollection;
}
set
{
this._routeCollection = value;
}
}
}
里面还定义了一个RequestData,主要就是当前处理的HttpHandler和URL原始路径。来看看ASP.NET 的HttpApplication 管线会依次处理下面的请求:
public System.Web.Routing.RouteCollection RouteCollection
{
get
{
if (this._routeCollection == null)
{
this._routeCollection = RouteTable.Routes;
}
return this._routeCollection;
}
set
{
this._routeCollection = value;
}
}
看到了来自RouteTable,这不正是在Global.asax.cs 中添加的 Route 集合:
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
CreateDefaultUserIfNotExists();
}
public static void RegisterRoutes(RouteCollection routes)
{
int iisVersion =Convert.ToInt32(ConfigurationManager.AppSettings["IISVersion"],System.Globalization.CultureInfo.InvariantCulture);
if (iisVersion >= 7)
{
RegisterRoutesForNewIIS(routes);
}
else
{
RegisterRoutesForOldIIS(routes);
}
}
private static void RegisterRoutesForNewIIS(ICollection<RouteBase> routes)
{
routes.Add(new Route("User/Login", new RouteValueDictionary(new {c, action = "Login" }), new MvcRouteHandler()));
routes.Add(new Route("User/Logout", new RouteValueDictionary(new {c, action = "Logout" }), new MvcRouteHandler()));
routes.Add(new Route("User/Signup", new RouteValueDictionary(new {c, action = "Signup" }), new MvcRouteHandler()));
routes.Add(new Route("User/SendPassword", new RouteValueDictionary(new{ c, action = "SendPassword" }), newMvcRouteHandler()));
routes.Add(newRoute("Story/Detail/{id}", new RouteValueDictionary(new { c, action = "Detail" }), new MvcRouteHandler()));
routes.Add(new Route("Story/Upcoming/{page}", newRouteValueDictionary(new { c, action = "Upcoming"}), new MvcRouteHandler()));
routes.Add(newRoute("Story/Search/{q}/{page}", new RouteValueDictionary(new {c, action = "Search" }), new MvcRouteHandler()));
var defaults = new RouteValueDictionary (
new
{
c,
action = "Category",
name = (string)null,
page = (int?)null
}
);
routes.Add(new Route("Story/Category/{page}", defaults, new MvcRouteHandler()));
routes.Add(new Route("Story/{action}/{name}/{page}", defaults, new MvcRouteHandler()));
routes.Add(new Route("{controller}/{action}/{id}", defaults, new MvcRouteHandler()));
routes.Add(new Route("Default.aspx", defaults, new MvcRouteHandler()));
}
private static void RegisterRoutesForOldIIS(ICollection<RouteBase> routes)
{
routes.Add(new Route("User.mvc/Login", new RouteValueDictionary(new {c, action = "Login" }), new MvcRouteHandler()));
routes.Add(new Route("User.mvc/Logout", new RouteValueDictionary(new {c, action = "Logout" }), new MvcRouteHandler()));
routes.Add(new Route("User.mvc/Signup", new RouteValueDictionary(new {c, action = "Signup" }), new MvcRouteHandler()));
routes.Add(new Route("User.mvc/SendPassword", newRouteValueDictionary(new { c, action = "SendPassword"}), new MvcRouteHandler()));
routes.Add(newRoute("Story.mvc/Detail/{id}", new RouteValueDictionary(new {c, action = "Detail" }), new MvcRouteHandler()));
routes.Add(new Route("Story.mvc/Upcoming/{page}", newRouteValueDictionary(new { c, action = "Upcoming"}), new MvcRouteHandler()));
routes.Add(newRoute("Story.mvc/Search/{q}/{page}", new RouteValueDictionary(new {c, action = "Search" }), new MvcRouteHandler()));
var defaults = new RouteValueDictionary(
new
{
c,
action = "Category",
name = (string)null,
page = (int?)null
}
);
routes.Add(new Route("Story.mvc/Category/{page}", defaults, new MvcRouteHandler()));
routes.Add(new Route("Story.mvc/{action}/{name}/{page}", defaults, new MvcRouteHandler()));
routes.Add(new Route("{controller}.mvc/{action}/{id}", defaults, new MvcRouteHandler()));
routes.Add(new Route("Default.aspx", defaults, new MvcRouteHandler()));
}
上述代码来自[翻译]使用asp.net mvc再造一个digg 第一部分的kigg。回到上文,在获取 RoteCollection 之后,通过调用 GetRouteData(context) 返回一个 RouteData对象,该对象内部包含了我们注册 Route 时的相关设置,包括下面所需要的 MvcRouteHandler。接下来,该方法将routeData 和上下文一起打包成 RequestContext,这就是为相关处理准备的上下文环境。通过调用IRouteHandler.GetHttpHandler()方法,终于到达流程的关键IHttpHandler(MvcHandler)。在WebForm中我们知道每一个页面都是一个HttpHandler,Asp.net mvc也不例外。
先来看看MvcRouteHandler:
namespace System.Web.Mvc {
using System.Web.Routing;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class MvcRouteHandler : IRouteHandler {
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {
return new MvcHandler(requestContext);
}
#region IRouteHandler Members
IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) {
return GetHttpHandler(requestContext);
}
#endregion
}
}
这里得到了MvcHandler:
namespace System.Web.Mvc {
using System.Globalization;
using System.Web.Mvc.Resources;
using System.Web.Routing;
using System.Web.SessionState;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class MvcHandler : IHttpHandler, IRequiresSessionState {
private ControllerBuilder _controllerBuilder;
public MvcHandler(RequestContext requestContext) {
if (requestContext == null) {
throw new ArgumentNullException("requestContext");
}
RequestContext = requestContext;
}
protected virtual bool IsReusable {
get {
// REVIEW: What's this?
return false;
}
}
internal ControllerBuilder ControllerBuilder {
get {
if (_controllerBuilder == null) {
_controllerBuilder = ControllerBuilder.Current;
}
return _controllerBuilder;
}
set {
_controllerBuilder = value;
}
}
public RequestContext RequestContext {
get;
private set;
}
protected virtual void ProcessRequest(HttpContext httpContext) {
HttpContextBase iHttpContext = new HttpContextWrapper2(httpContext);
ProcessRequest(iHttpContext);
}
protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
// Get the controller type
string controllerName = RequestContext.RouteData.GetRequiredString("controller");
// Instantiate the controller and call Execute
IControllerFactory factory = ControllerBuilder.GetControllerFactory();
IController controller = factory.CreateController(RequestContext, controllerName);
if (controller == null) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.ControllerBuilder_FactoryReturnedNull,
factory.GetType(),
controllerName));
}
try {
ControllerContext controllerContext = new ControllerContext(RequestContext, controller);
controller.Execute(controllerContext);
}
finally {
factory.DisposeController(controller);
}
}
#region IHttpHandler Members
bool IHttpHandler.IsReusable {
get {
return IsReusable;
}
}
void IHttpHandler.ProcessRequest(HttpContext httpContext) {
ProcessRequest(httpContext);
}
#endregion
}
}
到了这一步,MVC 框架已经准备好了相应的执行场景,接下来就是修改默认(指WebForm)的执行流程了。 RequestData data2 = new RequestData();
data2.OriginalPath = context.Request.Path;
data2.HttpHandler = httpHandler;
context.Items[_requestDataKey] = data2;
context.RewritePath("~/UrlRouting.axd");
OnApplicationPostMapRequestHandler被执行。在 PostMapRequestHandler 中,它提取了前面预先准备好的上下文,并修改了HttpContext.Handler,使得 MvcHandler 接管默认的WebForm的HttpHandler,才是执行ASP.NETMVC的流程。现在来继续看MvcHandler。
首先从 RouteData 中提取 Controller的名字(这个名字是我们在 Global.asax.cs RegisterRoutes 中注册 Route 时提供的),然后获取ControllerFactory,只不过这里面专门提供了一个 ControllerBuilder。
internal ControllerBuilder ControllerBuilder {
get {
if (_controllerBuilder == null) {
_controllerBuilder = ControllerBuilder.Current;
}
return _controllerBuilder;
}
set {
_controllerBuilder = value;
}
}
如果我们自定义MvcHandler,则需要好好的看看ControllerBuilder.Current:
namespace System.Web.Mvc {
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Web;
using System.Web.Mvc.Resources;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class ControllerBuilder {
private static ControllerBuilder _instance = new ControllerBuilder();
private Func<IControllerFactory> _factoryThunk;
public ControllerBuilder() {
SetControllerFactory(new DefaultControllerFactory());
}
public static ControllerBuilder Current {
get {
return _instance;
}
}
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "Calling method multiple times might return different objects.")]
public IControllerFactory GetControllerFactory() {
IControllerFactory controllerFactoryInstance = _factoryThunk();
return controllerFactoryInstance;
}
public void SetControllerFactory(IControllerFactory controllerFactory) {
if (controllerFactory == null) {
throw new ArgumentNullException("controllerFactory");
}
_factoryThunk = () => controllerFactory;
}
public void SetControllerFactory(Type controllerFactoryType) {
if (controllerFactoryType == null) {
throw new ArgumentNullException("controllerFactoryType");
}
if (!typeof(IControllerFactory).IsAssignableFrom(controllerFactoryType)) {
throw new ArgumentException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.ControllerBuilder_MissingIControllerFactory,
controllerFactoryType),
"controllerFactoryType");
}
_factoryThunk = delegate() {
try {
return (IControllerFactory) Activator.CreateInstance(controllerFactoryType);
}
catch (Exception ex) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.ControllerBuilder_ErrorCreatingControllerFactory,
controllerFactoryType),
ex);
}
};
}
}
}
(1) 通常情况下,返回一个默认的 DefaultControllerFactory 实例。
(2) 我们可以在 Application_Start 中通过 ControllerBuilder.Current.SetControllerFactory 方法来注册一个我们自定义的工厂。
(3) 核心代码: factory = (IControllerFactory)Activator.CreateInstance(controllerFactoryType),通过反射创建IControllerFactory;
回到MvcHandler的 ProcessRequest,DefaultControllerFactory.CreateController(RequestContext,requiredString) 来返回 IController 实例。下面看看DefaultControllerFactory的代码:
namespace System.Web.Mvc {
using System;
using System.Collections;
using System.Globalization;
using System.Reflection;
using System.Web;
using System.Web.Mvc.Resources;
using System.Web.Routing;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class DefaultControllerFactory : IControllerFactory {
private static object _controllerTypeCacheLock = new object();
private static ControllerTypeCache _controllerTypeCache = new ControllerTypeCache();
private ControllerTypeCache _currentControllerTypeCache;
private IBuildManager _buildManager;
internal IBuildManager BuildManager {
get {
if (_buildManager == null) {
_buildManager = new BuildManagerWrapper();
}
return _buildManager;
}
set {
_buildManager = value;
}
}
internal ControllerTypeCache ControllerTypeCache {
get {
if (_currentControllerTypeCache == null) {
_currentControllerTypeCache = _controllerTypeCache;
}
return _currentControllerTypeCache;
}
set {
_currentControllerTypeCache = value;
}
}
public RequestContext RequestContext {
get;
set;
}
protected internal virtual IController CreateController(RequestContext requestContext, string controllerName) {
if (requestContext == null) {
throw new ArgumentNullException("requestContext");
}
if (String.IsNullOrEmpty(controllerName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
RequestContext = requestContext;
Type controllerType = GetControllerType(controllerName);
IController controller = GetControllerInstance(controllerType);
return controller;
}
protected internal virtual void DisposeController(IController controller) {
IDisposable disposable = controller as IDisposable;
if (disposable != null) {
disposable.Dispose();
}
}
protected internal virtual IController GetControllerInstance(Type controllerType) {
if (controllerType == null) {
throw new ArgumentNullException(
"controllerType",
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.DefaultControllerFactory_NoControllerFound,
RequestContext.HttpContext.Request.Path));
}
if (!typeof(IController).IsAssignableFrom(controllerType)) {
throw new ArgumentException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.DefaultControllerFactory_MissingIController,
controllerType),
"controllerType");
}
try {
return Activator.CreateInstance(controllerType) as IController;
}
catch (Exception ex) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.DefaultControllerFactory_ErrorCreatingController,
controllerType),
ex);
}
}
protected internal virtual Type GetControllerType(string controllerName) {
if (String.IsNullOrEmpty(controllerName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
if (!ControllerTypeCache.Initialized) {
lock (_controllerTypeCacheLock) {
if (!ControllerTypeCache.Initialized) {
ControllerTypeCache.Initialize();
// Go through all assemblies referenced by the application and search for
// controllers and controller factories.
ICollection assemblies = BuildManager.GetReferencedAssemblies();
foreach (Assembly asm in assemblies) {
try {
Type[] typesInAsm = asm.GetTypes();
foreach (Type t in typesInAsm) {
if (IsController(t)) {
string foundControllerName = t.Name.Substring(0, t.Name.Length - 10);
if (!ControllerTypeCache.ContainsController(foundControllerName)) {
ControllerTypeCache.AddControllerType(foundControllerName, t);
}
else {
// A null value indicates a conflict for this key (i.e. more than one match)
ControllerTypeCache.AddControllerType(foundControllerName, null);
}
}
}
}
catch {
// TODO: Remove this try-catch. This is a temporary fix until we add a new feature.
}
}
}
}
}
// Once the master list of controllers has been created we can quickly index into it
Type controllerType;
if (ControllerTypeCache.TryGetControllerType(controllerName, out controllerType)) {
if (controllerType == null) {
// A null value indicates a conflict for this key (i.e. more than one match)
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.DefaultControllerFactory_DuplicateControllers,
controllerName));
}
return controllerType;
}
return null;
}
private static bool IsController(Type t) {
return
t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
(t != typeof(IController)) &&
(t != typeof(Controller)) &&
typeof(IController).IsAssignableFrom(t);
}
#region IControllerFactory Members
IController IControllerFactory.CreateController(RequestContext context, string controllerName) {
return CreateController(context, controllerName);
}
void IControllerFactory.DisposeController(IController controller) {
DisposeController(controller);
}
#endregion
}
}
通过反射来创建 Controller 实例,GetControllerType 里面做了些缓存处理,以此来避免频繁使用反射造成的性能问题。继续 MvcHandler.ProcessRequest(),在得到控制器实例后,MvcHandler 开始了调用 Controller.Execute()来进一步后续操作,同时对其上下文进一步封装,除了前面创建的 RequestContext,还加上了当前这个 Controller对象的引用,类名叫ControllerContext。
namespace System.Web.Mvc {
using System.Web.Routing;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class ControllerContext : RequestContext {
public ControllerContext(HttpContextBase httpContext, RouteData routeData, IController controller)
: base(httpContext, routeData) {
if (controller == null) {
throw new ArgumentNullException("controller");
}
Controller = controller;
}
public ControllerContext(RequestContext requestContext, IController controller)
: this(GetRequestContext(requestContext).HttpContext, GetRequestContext(requestContext).RouteData, controller) {
}
public IController Controller {
get;
private set;
}
internal static RequestContext GetRequestContext(RequestContext requestContext) {
if (requestContext == null) {
throw new ArgumentNullException("requestContext");
}
return requestContext;
}
}
}
继续看IController的默认实现类Controller:
namespace System.Web.Mvc {
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc.Resources;
using System.Web.Routing;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class Controller : IController {
private RouteCollection _routeCollection;
private IDictionary<string, object> _viewData;
private IViewEngine _viewEngine;
public ControllerContext ControllerContext {
get;
set;
}
public HttpContextBase HttpContext {
get {
return ControllerContext == null ? null : ControllerContext.HttpContext;
}
}
public HttpRequestBase Request {
get {
return HttpContext == null ? null : HttpContext.Request;
}
}
public HttpResponseBase Response {
get {
return HttpContext == null ? null : HttpContext.Response;
}
}
internal RouteCollection RouteCollection {
get {
if (_routeCollection == null) {
_routeCollection = RouteTable.Routes;
}
return _routeCollection;
}
set {
_routeCollection = value;
}
}
public RouteData RouteData {
get {
return ControllerContext == null ? null : ControllerContext.RouteData;
}
}
public HttpServerUtilityBase Server {
get {
return HttpContext == null ? null : HttpContext.Server;
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
Justification = "This property is settable so that unit tests can provide mock implementations.")]
public TempDataDictionary TempData {
get;
set;
}
public IPrincipal User {
get {
return HttpContext == null ? null : HttpContext.User;
}
}
public IDictionary<string, object> ViewData {
get {
if (_viewData == null) {
_viewData = new Dictionary<string, object>();
}
return _viewData;
}
}
public IViewEngine ViewEngine {
get {
return _viewEngine ?? new WebFormViewEngine();
}
set {
if (value == null) {
throw new ArgumentNullException("value");
}
_viewEngine = value;
}
}
private static object ConvertParameterType(object value, TypedestinationType, string parameterName, string actionName) {
if (value == null || value.GetType() == destinationType) {
return value;
}
TypeConverter converter = TypeDescriptor.GetConverter(destinationType);
bool canConvertFrom = converter.CanConvertFrom(value.GetType());
if (!canConvertFrom) {
converter = TypeDescriptor.GetConverter(value.GetType());
}
if (!(canConvertFrom || converter.CanConvertTo(destinationType))) {
throw new InvalidOperationException(String.Format(
CultureInfo.CurrentUICulture,
MvcResources.Controller_CannotConvertParameter,
parameterName, actionName, value, destinationType));
}
try {
return canConvertFrom ? converter.ConvertFrom(value) : converter.ConvertTo(value, destinationType);
}
catch (Exception ex) {
throw new InvalidOperationException(String.Format(
CultureInfo.CurrentUICulture,
MvcResources.Controller_CannotConvertParameter,
parameterName, actionName, value, destinationType),
ex);
}
}
protected internal virtual void Execute(ControllerContext controllerContext) {
if (controllerContext == null) {
throw new ArgumentNullException("controllerContext");
}
ControllerContext = controllerContext;
TempData = new TempDataDictionary(controllerContext.HttpContext);
string actionName = RouteData.GetRequiredString("action");
if (!InvokeAction(actionName)) {
HandleUnknownAction(actionName);
}
}
protected internal virtual void HandleUnknownAction(string actionName) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_UnknownAction, actionName));
}
protected internal bool InvokeAction(string actionName) {
return InvokeAction(actionName, new RouteValueDictionary());
}
protected internal virtual bool InvokeAction(string actionName, RouteValueDictionary values) {
if (String.IsNullOrEmpty(actionName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
// We have to loop through all the methods to make sure there isn't
// a conflict. If we stop the loop the first time we find a match
// we might miss some error cases.
MemberInfo[] membInfos = GetType().GetMember(actionName, MemberTypes.Method,
BindingFlags.IgnoreCase | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
MethodInfo foundMatch = null;
foreach (MemberInfo memberInfo in membInfos) {
MethodInfo mi = (MethodInfo)memberInfo;
// 1) Action methods must not have the non-action attribute in their inheritance chain, and
// 2) special methods like constructors, property accessors, and event accessors cannot be action methods, and
// 3) methods originally defined on Object (like ToString) or Controller cannot be action methods.
if (!mi.IsDefined(typeof(NonActionAttribute), true) &&
!mi.IsSpecialName &&
mi.DeclaringType.IsSubclassOf(typeof(Controller))) {
if (foundMatch != null) {
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MoreThanOneAction, actionName, GetType()));
}
foundMatch = mi;
}
}
if (foundMatch != null) {
InvokeActionMethod(foundMatch, values);
return true;
}
return false;
}
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
Justification = "We use MethodInfo since it represents only methods and not constructors." +
"This method only makes sense for use with methods.")]
protected internal virtual void InvokeActionMethod(MethodInfo methodInfo, RouteValueDictionary values) {
if (methodInfo == null) {
throw new ArgumentNullException("methodInfo");
}
if (values == null) {
values = new RouteValueDictionary();
}
if (methodInfo.ContainsGenericParameters) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_ActionCannotBeGeneric, methodInfo.Name));
}
ParameterInfo[] methodParameters = methodInfo.GetParameters();
object[] parameterValues = null;
if (methodParameters.Length > 0) {
parameterValues = new object[methodParameters.Length];
for (int i = 0; i < methodParameters.Length; i++) {
ParameterInfo pi = methodParameters;
if (pi.IsOut || pi.ParameterType.IsByRef) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_ReferenceParametersNotSupported, pi.Name,methodInfo.Name));
}
bool valueRequired = true;
if (pi.ParameterType.IsClass) {
// Classes (ref types) don't require values since we can pass in null
valueRequired = false;
}
else {
if ((pi.ParameterType.IsGenericType && !pi.ParameterType.IsGenericTypeDefinition) &&
(pi.ParameterType.GetGenericTypeDefinition() == typeof(Nullable<>))) {
// Nullable types don't require values since we can pass in null
valueRequired = false;
}
}
// Try to get a value for the parameter. We use this order of precedence:
// 1. Explicitly-provided extra parameters in the call to InvokeAction()
// 2. Values from the RouteData (could be from the typed-in URL or from the route's default values)
// 3. Request values (query string, form post data, cookie)
object parameterValue = null;
if (!values.TryGetValue(methodParameters.Name, out parameterValue)) {
if (RouteData == null || !RouteData.Values.TryGetValue(methodParameters.Name, out parameterValue)) {
if (Request != null) {
parameterValue = Request[methodParameters.Name];
}
}
}
if (parameterValue == null && valueRequired) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name));
}
try {
parameterValues = ConvertParameterType(parameterValue,methodParameters.ParameterType, methodParameters.Name,methodInfo.Name);
}
catch (Exception ex) {
// Parameter value conversion errors are acceptable unless the value is required
if (valueRequired) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name),ex);
}
}
}
}
InvokeActionMethodFilters(methodInfo, () => methodInfo.Invoke(this, parameterValues));
}
private void InvokeActionMethodFilters(MethodInfo methodInfo, Action continuation) {
// filters should execute in this order:
// controller virtual overrides -> controller base attributes -> controller type attributes ->
// base action method attributes -> action method attributes
List<ActionFilterAttribute> filters = new List<ActionFilterAttribute>() {
new ControllerActionFilter(this)
};
Stack<MemberInfo> memberChain = new Stack<MemberInfo>();
Type curType = GetType();
while (curType != null) {
memberChain.Push(curType);
curType = curType.BaseType;
}
List<ActionFilterAttribute> sortedClassFilters = SortActionFilters(memberChain);
filters.AddRange(sortedClassFilters);
List<ActionFilterAttribute> sortedMethodFilters = PrepareMethodActionFilters(methodInfo);
filters.AddRange(sortedMethodFilters);
FilterContext context = new FilterContext(ControllerContext, methodInfo);
ActionFilterExecutor executor = new ActionFilterExecutor(filters, context, continuation);
executor.Execute();
}
protected virtual void OnActionExecuted(FilterExecutedContext filterContext) {
}
protected virtual void OnActionExecuting(FilterExecutingContext filterContext) {
}
internal static List<ActionFilterAttribute> PrepareMethodActionFilters(MethodInfo methodInfo) {
Stack<MemberInfo> memberChain = new Stack<MemberInfo>();
memberChain.Push(methodInfo);
MethodInfo baseMethod = methodInfo.GetBaseDefinition();
Type curType = methodInfo.DeclaringType.BaseType;
while (true) {
MemberInfo[] membInfos = curType.GetMember(methodInfo.Name, MemberTypes.Method,
BindingFlags.IgnoreCase | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
MethodInfo foundMatch = null;
foreach (MemberInfo memberInfo in membInfos) {
MethodInfo mi = (MethodInfo)memberInfo;
if (mi.GetBaseDefinition() == baseMethod && mi.DeclaringType == curType) {
foundMatch = mi;
break;
}
}
if (foundMatch == null) {
break;
}
memberChain.Push(foundMatch);
curType = curType.BaseType;
}
return SortActionFilters(memberChain);
}
protected virtual void RedirectToAction(RouteValueDictionary values) {
VirtualPathData vpd = RouteCollection.GetVirtualPath(ControllerContext, values);
string target = null;
if (vpd != null) {
target = vpd.VirtualPath;
}
HttpContext.Response.Redirect(target);
}
protected void RedirectToAction(string actionName) {
if (String.IsNullOrEmpty(actionName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
RouteValueDictionary valuesDictionary = new RouteValueDictionary();
valuesDictionary.Add("action", actionName);
RedirectToAction(valuesDictionary);
}
protected void RedirectToAction(string actionName, string controllerName) {
if (String.IsNullOrEmpty(actionName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
if (String.IsNullOrEmpty(controllerName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
RouteValueDictionary valuesDictionary = new RouteValueDictionary();
valuesDictionary.Add("action", actionName);
valuesDictionary.Add("controller", controllerName);
RedirectToAction(valuesDictionary);
}
protected void RenderView(string viewName) {
RenderView(viewName, String.Empty, ViewData);
}
protected void RenderView(string viewName, string masterName) {
RenderView(viewName, masterName, ViewData);
}
protected void RenderView(string viewName, object viewData) {
RenderView(viewName, String.Empty, viewData);
}
protected virtual void RenderView(string viewName, string masterName, object viewData) {
ViewContext viewContext = new ViewContext(ControllerContext, viewName, masterName, viewData, TempData);
ViewEngine.RenderView(viewContext);
}
private static List<ActionFilterAttribute> SortActionFilters(Stack<MemberInfo> memberChain) {
List<ActionFilterAttribute> filters = new List<ActionFilterAttribute>();
foreach (MemberInfo member in memberChain) {
ActionFilterAttribute[] attrs =(ActionFilterAttribute[])member.GetCustomAttributes(typeof(ActionFilterAttribute),false /* inherit */);
SortedList<int,ActionFilterAttribute> orderedFilters = new SortedList<int,ActionFilterAttribute>();
foreach (ActionFilterAttribute filter in attrs) {
// filters are allowed to have the same order only if the order is -1. in that case,
// they are processed before explicitly ordered filters but in no particular order in
// relation to one another.
if (filter.Order >= 0) {
if (orderedFilters.ContainsKey(filter.Order)) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.ActionFilter_DuplicateOrder,
member,
filter.Order));
}
orderedFilters.Add(filter.Order, filter);
}
else {
filters.Add(filter);
}
}
filters.AddRange(orderedFilters.Values);
}
return filters;
}
#region IController Members
void IController.Execute(ControllerContext controllerContext) {
Execute(controllerContext);
}
#endregion
private sealed class ControllerActionFilter : ActionFilterAttribute {
private Controller _controller;
public ControllerActionFilter(Controller controller) {
_controller = controller;
}
public override void OnActionExecuted(FilterExecutedContext filterContext) {
_controller.OnActionExecuted(filterContext);
}
public override void OnActionExecuting(FilterExecutingContext filterContext) {
_controller.OnActionExecuting(filterContext);
}
}
}
}
获取 Action 的名字,然后开始执行 InvokeAction,如果找不到Action,则调用HandleUnknownAction,这是一个虚拟方法,可以在子类中重写,默认是抛出一个异常InvalidOperationException。
protected internal virtual void Execute(ControllerContext controllerContext) {
if (controllerContext == null) {
throw new ArgumentNullException("controllerContext");
}
ControllerContext = controllerContext;
TempData = new TempDataDictionary(controllerContext.HttpContext);
string actionName = RouteData.GetRequiredString("action");
if (!InvokeAction(actionName)) {
HandleUnknownAction(actionName);
}
}
详细看看InvokeAction方式的执行:
protected internal bool InvokeAction(string actionName) {
return InvokeAction(actionName, new RouteValueDictionary());
}
protected internal virtual bool InvokeAction(string actionName, RouteValueDictionary values) {
if (String.IsNullOrEmpty(actionName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
// We have to loop through all the methods to make sure there isn't
// a conflict. If we stop the loop the first time we find a match
// we might miss some error cases.
MemberInfo[] membInfos = GetType().GetMember(actionName, MemberTypes.Method,
BindingFlags.IgnoreCase | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
MethodInfo foundMatch = null;
foreach (MemberInfo memberInfo in membInfos) {
MethodInfo mi = (MethodInfo)memberInfo;
// 1) Action methods must not have the non-action attribute in their inheritance chain, and
// 2) special methods like constructors, property accessors, and event accessors cannot be action methods, and
// 3) methods originally defined on Object (like ToString) or Controller cannot be action methods.
if (!mi.IsDefined(typeof(NonActionAttribute), true) &&
!mi.IsSpecialName &&
mi.DeclaringType.IsSubclassOf(typeof(Controller))) {
if (foundMatch != null) {
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MoreThanOneAction, actionName, GetType()));
}
foundMatch = mi;
}
}
if (foundMatch != null) {
InvokeActionMethod(foundMatch, values);
return true;
}
return false;
}
它通过反射获取所有同名 Action 方法信息;其次,它过滤掉所有有 NonActionAttribute 和 IsSpecialName标记的方法;第三,当同名有效 Action被重载时它会抛出异常(提示Controller_MoreThanOneAction),继续调用InvokeActionMethod:
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
Justification = "We use MethodInfo since it represents only methods and not constructors." +
"This method only makes sense for use with methods.")]
protected internal virtual void InvokeActionMethod(MethodInfo methodInfo, RouteValueDictionary values) {
if (methodInfo == null) {
throw new ArgumentNullException("methodInfo");
}
if (values == null) {
values = new RouteValueDictionary();
}
if (methodInfo.ContainsGenericParameters) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_ActionCannotBeGeneric, methodInfo.Name));
}
ParameterInfo[] methodParameters = methodInfo.GetParameters();
object[] parameterValues = null;
if (methodParameters.Length > 0) {
parameterValues = new object[methodParameters.Length];
for (int i = 0; i < methodParameters.Length; i++) {
ParameterInfo pi = methodParameters;
if (pi.IsOut || pi.ParameterType.IsByRef) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_ReferenceParametersNotSupported, pi.Name,methodInfo.Name));
}
bool valueRequired = true;
if (pi.ParameterType.IsClass) {
// Classes (ref types) don't require values since we can pass in null
valueRequired = false;
}
else {
if ((pi.ParameterType.IsGenericType && !pi.ParameterType.IsGenericTypeDefinition) &&
(pi.ParameterType.GetGenericTypeDefinition() == typeof(Nullable<>))) {
// Nullable types don't require values since we can pass in null
valueRequired = false;
}
}
// Try to get a value for the parameter. We use this order of precedence:
// 1. Explicitly-provided extra parameters in the call to InvokeAction()
// 2. Values from the RouteData (could be from the typed-in URL or from the route's default values)
// 3. Request values (query string, form post data, cookie)
object parameterValue = null;
if (!values.TryGetValue(methodParameters.Name, out parameterValue)) {
if (RouteData == null || !RouteData.Values.TryGetValue(methodParameters.Name, out parameterValue)) {
if (Request != null) {
parameterValue = Request[methodParameters.Name];
}
}
}
if (parameterValue == null && valueRequired) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name));
}
try {
parameterValues = ConvertParameterType(parameterValue,methodParameters.ParameterType, methodParameters.Name,methodInfo.Name);
}
catch (Exception ex) {
// Parameter value conversion errors are acceptable unless the value is required
if (valueRequired) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name),ex);
}
}
}
}
InvokeActionMethodFilters(methodInfo, () => methodInfo.Invoke(this, parameterValues));
}
这个方法很复杂,有大量的代码是参数的分解 ,只有最后一行是关键的。 InvokeActionMethodFilters(methodInfo, () => methodInfo.Invoke(this, parameterValues));
这行代码将 Action 的调用作为一个委托,连同反射信息传递给 InvokeActionMethodFilters。
private void InvokeActionMethodFilters(MethodInfo methodInfo, Action continuation) {
// filters should execute in this order:
// controller virtual overrides -> controller base attributes -> controller type attributes ->
// base action method attributes -> action method attributes
List<ActionFilterAttribute> filters = new List<ActionFilterAttribute>() {
new ControllerActionFilter(this)
};
Stack<MemberInfo> memberChain = new Stack<MemberInfo>();
Type curType = GetType();
while (curType != null) {
memberChain.Push(curType);
curType = curType.BaseType;
}
List<ActionFilterAttribute> sortedClassFilters = SortActionFilters(memberChain);
filters.AddRange(sortedClassFilters);
List<ActionFilterAttribute> sortedMethodFilters = PrepareMethodActionFilters(methodInfo);
filters.AddRange(sortedMethodFilters);
FilterContext context = new FilterContext(ControllerContext, methodInfo);
ActionFilterExecutor executor = new ActionFilterExecutor(filters, context, continuation);
executor.Execute();
}
这个方法首先将默认的过滤器 ControllerActionFilter加到列表,然后提取所有继承层次上基类的过滤器特性。最后将这些过滤器集合、过滤上下文,连同前一个方法传递进来的 Action执行委托(continuation) 再次转交给了一个 ActionFilterExecutor 对象实例,并调用其 Execute 方法。
namespace System.Web.Mvc {
using System;
using System.Collections;
using System.Collections.Generic;
internal sealed class ActionFilterExecutor {
private FilterContext _context;
private Action _continuation;
private List<ActionFilterAttribute> _filters;
public ActionFilterExecutor(List<ActionFilterAttribute> filters, FilterContext context, Action continuation) {
_filters = filters;
_context = context;
_continuation = continuation;
}
public void Execute() {
IEnumerator<ActionFilterAttribute> enumerator = _filters.GetEnumerator();
ExecuteRecursive(enumerator);
}
private FilterExecutedContext ExecuteRecursive(IEnumerator<ActionFilterAttribute> enumerator) {
if (enumerator.MoveNext()) {
ActionFilterAttribute filter = enumerator.Current;
FilterExecutingContext preContext = new FilterExecutingContext(_context);
filter.OnActionExecuting(preContext);
if (preContext.Cancel) {
return new FilterExecutedContext(_context, null /* exception */);
}
bool wasError = false;
FilterExecutedContext postContext = null;
try {
postContext = ExecuteRecursive(enumerator);
}
catch (Exception ex) {
wasError = true;
postContext = new FilterExecutedContext(_context, ex);
filter.OnActionExecuted(postContext);
if (!postContext.ExceptionHandled) {
throw;
}
}
if (!wasError) {
filter.OnActionExecuted(postContext);
}
return postContext;
}
else {
_continuation();
return new FilterExecutedContext(_context, null /* exception */);
}
}
}
}
ExecuteRecursive使用了递归算法,通过迭代器 MoveNext() 方法提取一个过滤器对象,执行其 OnActionExecuting 方法。 如果该方法设置了filterContext.Cancel =true,则放弃后续执行代码。这种机制为我们提供了更好的控制,例如用它来实现伪静态页。一层一层调用所有的ActionFilterAttribute.OnActionExecuting 方法,直到 MoveNext() == false。在最后一次递归调用时,由于 enumerator.MoveNext() == false, _continuation()方法被执行。这个就是前面给传递过来的 Action 方法委托,Action 方法总算是执行了。 在 Action委托执行完成后,递归调逐级往上回溯,直到最初那个方法堆栈。这样所有ActionFilterAttribute.OnActionExecuted也被执行完成。
到此开始进入最后的视图呈现阶段,可以把数据呈现到视图上,Controller 提供了几个重载的 RenderView() 来完成这个工作。
protected void RenderView(string viewName) {
RenderView(viewName, String.Empty, ViewData);
}
protected void RenderView(string viewName, string masterName) {
RenderView(viewName, masterName, ViewData);
}
protected void RenderView(string viewName, object viewData) {
RenderView(viewName, String.Empty, viewData);
}
protected virtual void RenderView(string viewName, string masterName, object viewData) {
ViewContext viewContext = new ViewContext(ControllerContext, viewName, masterName, viewData, TempData);
ViewEngine.RenderView(viewContext);
}
将一路传递过来的相关 "数据" (上下文)ControllerContext 再次包装成ViewContext。当然,这次依然会多出些东西,里面就有我们向视图传递的数据 —— viewData 和tempData。作为默认选择,MVC 创建WebForm 视图引擎来展示结果。其他的视图引擎可以去看mvccontrib,这个项目就是整合:Castle Windsor 、StructureMap 、Spring.NET 等IoC框架以及视图引擎,包括Castle MonoRail所用的NVelocityView视图引擎、NHamlView视图引擎、XsltViewEngine视图引擎等等。
继续看这个 WebFormViewEngine:
namespace System.Web.Mvc {
using System;
using System.Globalization;
using System.Web.Compilation;
using System.Web.Resources;
using System.Web.Routing;
using System.Web.Mvc.Resources;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class WebFormViewEngine : IViewEngine {
private IBuildManager _buildManager;
private IViewLocator _viewLocator;
internal IBuildManager BuildManager {
get {
if (_buildManager == null) {
_buildManager = new BuildManagerWrapper();
}
return _buildManager;
}
set {
_buildManager = value;
}
}
public IViewLocator ViewLocator {
get {
if (_viewLocator == null) {
_viewLocator = new WebFormViewLocator();
}
return _viewLocator;
}
set {
_viewLocator = value;
}
}
protected virtual void RenderView(ViewContext viewContext) {
if (viewContext == null) {
throw new ArgumentNullException("viewContext");
}
string viewPath = ViewLocator.GetViewLocation(viewContext, viewContext.ViewName);
if (String.IsNullOrEmpty(viewPath)) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.WebFormViewEngine_ViewNotFound,
viewContext.ViewName));
}
object viewInstance = BuildManager.CreateInstanceFromVirtualPath(viewPath, typeof(object));
if (viewInstance == null) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.WebFormViewEngine_ViewCouldNotBeCreated,
viewPath));
}
ViewPage viewPage = viewInstance as ViewPage;
if (viewPage != null) {
if (!String.IsNullOrEmpty(viewContext.MasterName)) {
string masterLocation = ViewLocator.GetMasterLocation(viewContext, viewContext.MasterName);
if (String.IsNullOrEmpty(masterLocation)) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.WebFormViewEngine_MasterNotFound,
viewContext.MasterName));
}
// We don't set the page's MasterPageFile directly since it will get
// overwritten by a statically-defined value. In ViewPage we wait until
// the PreInit phase until we set the new value.
viewPage.MasterLocation = masterLocation;
}
viewPage.SetViewData(viewContext.ViewData);
viewPage.RenderView(viewContext);
}
else {
ViewUserControl viewUserControl = viewInstance as ViewUserControl;
if (viewUserControl != null) {
if (!String.IsNullOrEmpty(viewContext.MasterName)) {
throw new InvalidOperationException(MvcResources.WebFormViewEngine_UserControlCannotHaveMaster);
}
viewUserControl.SetViewData(viewContext.ViewData);
viewUserControl.RenderView(viewContext);
}
else {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.WebFormViewEngine_WrongViewBase,
viewPath));
}
}
}
#region IViewEngine Members
void IViewEngine.RenderView(ViewContext viewContext) {
RenderView(viewContext);
}
#endregion
}
}
首先会创建一个 WebFormViewLocator 对象来获取视图存放路径。
namespace System.Web.Mvc {
public class WebFormViewLocator : ViewLocator {
public WebFormViewLocator() {
ViewLocationFormats = new[] {
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/Shared/{0}.aspx",
"~/Views/Shared/{0}.ascx"
};
MasterLocationFormats = new[] {
"~/Views/{1}/{0}.master",
"~/Views/Shared/{0}.master"
};
}
}
}
在获取路径后,WebFormViewEngine 通过一个包装类 BuildManagerWrapper 间接调用System.Web.Compilation.BuildManager 的静态方法CreateInstanceFromVirtualPath() 将视图进行编译,并返回一个对象实例。(System.Web.Compilation.BuildManagerBuildManager 类管理应用程序的程序集和页的编译过程),后面通过 as 转换结果来判断视图是 ViewPage 还是ViewUserControl。ViewPage 继承自我们所熟悉的 System.Web.UI.Page,它的 RenderView()方法也就是完成WebForm的 Page.ProcessRequest() 的处理而已,也就完成了对WebForm模型的置换。
下面看看ViewPage和ViewUserControl的代码:
namespace System.Web.Mvc {
using System.Web.UI;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class ViewPage : Page, IViewDataContainer {
private string _masterLocation;
private object _viewData;
public string MasterLocation {
get {
return _masterLocation ?? String.Empty;
}
set {
_masterLocation = value;
}
}
public AjaxHelper Ajax {
get;
set;
}
public HtmlHelper Html {
get;
set;
}
public TempDataDictionary TempData {
get {
return ViewContext.TempData;
}
}
public UrlHelper Url {
get;
set;
}
public ViewContext ViewContext {
get;
private set;
}
public ViewData ViewData {
get {
return new ViewData(_viewData);
}
}
public HtmlTextWriter Writer {
get;
private set;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")]
protected override void OnPreInit(EventArgs e) {
base.OnPreInit(e);
if (!String.IsNullOrEmpty(MasterLocation)) {
MasterPageFile = MasterLocation;
}
}
public virtual void RenderView(ViewContext viewContext) {
ViewContext = viewContext;
Ajax = new AjaxHelper(viewContext);
Html = new HtmlHelper(viewContext);
Url = new UrlHelper(viewContext);
ProcessRequest(HttpContext.Current);
}
protected override void Render(HtmlTextWriter writer) {
Writer = writer;
try {
base.Render(writer);
}
finally {
Writer = null;
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "There is already a ViewData property and it has a slightly different meaning.")]
protected internal virtual void SetViewData(object viewData) {
_viewData = viewData;
}
#region IViewDataContainer Members
object IViewDataContainer.ViewData {
get {
return _viewData;
}
}
#endregion
}
}
namespace System.Web.Mvc {
using System.ComponentModel;
using System.Globalization;
using System.Web.Resources;
using System.Web.UI;
using System.Web.Mvc.Resources;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class ViewUserControl : UserControl, IViewDataContainer {
private string _viewDataKey;
private object _viewData;
public AjaxHelper Ajax {
get {
return ViewPage.Ajax;
}
}
public HtmlHelper Html {
get {
return ViewPage.Html;
}
}
public TempDataDictionary TempData {
get {
return ViewPage.TempData;
}
}&n
ASP.NET MVC通过HttpModule(UrlRoutingModule)开始他的执行流程
<httpModules>
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing" />
</httpModules>
代码如下:
namespace System.Web.Routing
{
using System;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Security.Permissions;
using System.Web;
using System.Web.Resources;
[AspNetHostingPermission(SecurityAction.InheritanceDemand,Level=AspNetHostingPermissionLevel.Minimal),AspNetHostingPermission(SecurityAction.LinkDemand,Level=AspNetHostingPermissionLevel.Minimal)]
public class UrlRoutingModule : IHttpModule
{
private static readonly object _requestDataKey = new object();
private System.Web.Routing.RouteCollection _routeCollection;
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 HttpContextWrapper2(((HttpApplication) sender).Context);
this.PostMapRequestHandler(context);
}
private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
{
HttpContextBase context = new HttpContextWrapper2(((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 newInvalidOperationException(string.Format(CultureInfo.CurrentUICulture,RoutingResources.UrlRoutingModule_NoRouteHandler, new object[0]));
}
RequestContext requestContext = new RequestContext(context, routeData);
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
if (httpHandler == null)
{
throw newInvalidOperationException(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);
}
public System.Web.Routing.RouteCollection RouteCollection
{
get
{
if (this._routeCollection == null)
{
this._routeCollection = RouteTable.Routes;
}
return this._routeCollection;
}
set
{
this._routeCollection = value;
}
}
}
里面还定义了一个RequestData,主要就是当前处理的HttpHandler和URL原始路径。来看看ASP.NET 的HttpApplication 管线会依次处理下面的请求:
- 对请求进行验证,将检查浏览器发送的信息,并确定其是否包含潜在恶意标记。
- 如果已在 Web.config 文件的 UrlMappingsSection 节中配置了任何 URL,则执行 URL 映射。
- 引发 BeginRequest 事件。
- 引发 AuthenticateRequest 事件。
- 引发 PostAuthenticateRequest 事件。
- 引发 AuthorizeRequest 事件。
- 引发 PostAuthorizeRequest 事件。
- 引发 ResolveRequestCache 事件。
- 引发 PostResolveRequestCache 事件。
- 根据所请求资源的文件扩展名(在应用程序的配置文件中映射),选择实现 IHttpHandler 的类,对请求进行处理。如果该请求针对从 Page 类派生的对象(页),并且需要对该页进行编译,则 ASP.NET 会在创建该页的实例之前对其进行编译。
- 引发 PostMapRequestHandler 事件。
- 引发 AcquireRequestState 事件。
- 引发 PostAcquireRequestState 事件。
- 引发 PreRequestHandlerExecute 事件。
- 为该请求调用合适的 IHttpHandler 类的 ProcessRequest 方法(或异步版 BeginProcessRequest)。例如,如果该请求针对某页,则当前的页实例将处理该请求。
- 引发 PostRequestHandlerExecute 事件。
- 引发 ReleaseRequestState 事件。
- 引发 PostReleaseRequestState 事件。
- 如果定义了 Filter 属性,则执行响应筛选。
- 引发 UpdateRequestCache 事件。
- 引发 PostUpdateRequestCache 事件。
- 引发 EndRequest 事件。
public System.Web.Routing.RouteCollection RouteCollection
{
get
{
if (this._routeCollection == null)
{
this._routeCollection = RouteTable.Routes;
}
return this._routeCollection;
}
set
{
this._routeCollection = value;
}
}
看到了来自RouteTable,这不正是在Global.asax.cs 中添加的 Route 集合:
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
CreateDefaultUserIfNotExists();
}
public static void RegisterRoutes(RouteCollection routes)
{
int iisVersion =Convert.ToInt32(ConfigurationManager.AppSettings["IISVersion"],System.Globalization.CultureInfo.InvariantCulture);
if (iisVersion >= 7)
{
RegisterRoutesForNewIIS(routes);
}
else
{
RegisterRoutesForOldIIS(routes);
}
}
private static void RegisterRoutesForNewIIS(ICollection<RouteBase> routes)
{
routes.Add(new Route("User/Login", new RouteValueDictionary(new {c, action = "Login" }), new MvcRouteHandler()));
routes.Add(new Route("User/Logout", new RouteValueDictionary(new {c, action = "Logout" }), new MvcRouteHandler()));
routes.Add(new Route("User/Signup", new RouteValueDictionary(new {c, action = "Signup" }), new MvcRouteHandler()));
routes.Add(new Route("User/SendPassword", new RouteValueDictionary(new{ c, action = "SendPassword" }), newMvcRouteHandler()));
routes.Add(newRoute("Story/Detail/{id}", new RouteValueDictionary(new { c, action = "Detail" }), new MvcRouteHandler()));
routes.Add(new Route("Story/Upcoming/{page}", newRouteValueDictionary(new { c, action = "Upcoming"}), new MvcRouteHandler()));
routes.Add(newRoute("Story/Search/{q}/{page}", new RouteValueDictionary(new {c, action = "Search" }), new MvcRouteHandler()));
var defaults = new RouteValueDictionary (
new
{
c,
action = "Category",
name = (string)null,
page = (int?)null
}
);
routes.Add(new Route("Story/Category/{page}", defaults, new MvcRouteHandler()));
routes.Add(new Route("Story/{action}/{name}/{page}", defaults, new MvcRouteHandler()));
routes.Add(new Route("{controller}/{action}/{id}", defaults, new MvcRouteHandler()));
routes.Add(new Route("Default.aspx", defaults, new MvcRouteHandler()));
}
private static void RegisterRoutesForOldIIS(ICollection<RouteBase> routes)
{
routes.Add(new Route("User.mvc/Login", new RouteValueDictionary(new {c, action = "Login" }), new MvcRouteHandler()));
routes.Add(new Route("User.mvc/Logout", new RouteValueDictionary(new {c, action = "Logout" }), new MvcRouteHandler()));
routes.Add(new Route("User.mvc/Signup", new RouteValueDictionary(new {c, action = "Signup" }), new MvcRouteHandler()));
routes.Add(new Route("User.mvc/SendPassword", newRouteValueDictionary(new { c, action = "SendPassword"}), new MvcRouteHandler()));
routes.Add(newRoute("Story.mvc/Detail/{id}", new RouteValueDictionary(new {c, action = "Detail" }), new MvcRouteHandler()));
routes.Add(new Route("Story.mvc/Upcoming/{page}", newRouteValueDictionary(new { c, action = "Upcoming"}), new MvcRouteHandler()));
routes.Add(newRoute("Story.mvc/Search/{q}/{page}", new RouteValueDictionary(new {c, action = "Search" }), new MvcRouteHandler()));
var defaults = new RouteValueDictionary(
new
{
c,
action = "Category",
name = (string)null,
page = (int?)null
}
);
routes.Add(new Route("Story.mvc/Category/{page}", defaults, new MvcRouteHandler()));
routes.Add(new Route("Story.mvc/{action}/{name}/{page}", defaults, new MvcRouteHandler()));
routes.Add(new Route("{controller}.mvc/{action}/{id}", defaults, new MvcRouteHandler()));
routes.Add(new Route("Default.aspx", defaults, new MvcRouteHandler()));
}
上述代码来自[翻译]使用asp.net mvc再造一个digg 第一部分的kigg。回到上文,在获取 RoteCollection 之后,通过调用 GetRouteData(context) 返回一个 RouteData对象,该对象内部包含了我们注册 Route 时的相关设置,包括下面所需要的 MvcRouteHandler。接下来,该方法将routeData 和上下文一起打包成 RequestContext,这就是为相关处理准备的上下文环境。通过调用IRouteHandler.GetHttpHandler()方法,终于到达流程的关键IHttpHandler(MvcHandler)。在WebForm中我们知道每一个页面都是一个HttpHandler,Asp.net mvc也不例外。
先来看看MvcRouteHandler:
namespace System.Web.Mvc {
using System.Web.Routing;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class MvcRouteHandler : IRouteHandler {
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {
return new MvcHandler(requestContext);
}
#region IRouteHandler Members
IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) {
return GetHttpHandler(requestContext);
}
#endregion
}
}
这里得到了MvcHandler:
namespace System.Web.Mvc {
using System.Globalization;
using System.Web.Mvc.Resources;
using System.Web.Routing;
using System.Web.SessionState;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class MvcHandler : IHttpHandler, IRequiresSessionState {
private ControllerBuilder _controllerBuilder;
public MvcHandler(RequestContext requestContext) {
if (requestContext == null) {
throw new ArgumentNullException("requestContext");
}
RequestContext = requestContext;
}
protected virtual bool IsReusable {
get {
// REVIEW: What's this?
return false;
}
}
internal ControllerBuilder ControllerBuilder {
get {
if (_controllerBuilder == null) {
_controllerBuilder = ControllerBuilder.Current;
}
return _controllerBuilder;
}
set {
_controllerBuilder = value;
}
}
public RequestContext RequestContext {
get;
private set;
}
protected virtual void ProcessRequest(HttpContext httpContext) {
HttpContextBase iHttpContext = new HttpContextWrapper2(httpContext);
ProcessRequest(iHttpContext);
}
protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
// Get the controller type
string controllerName = RequestContext.RouteData.GetRequiredString("controller");
// Instantiate the controller and call Execute
IControllerFactory factory = ControllerBuilder.GetControllerFactory();
IController controller = factory.CreateController(RequestContext, controllerName);
if (controller == null) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.ControllerBuilder_FactoryReturnedNull,
factory.GetType(),
controllerName));
}
try {
ControllerContext controllerContext = new ControllerContext(RequestContext, controller);
controller.Execute(controllerContext);
}
finally {
factory.DisposeController(controller);
}
}
#region IHttpHandler Members
bool IHttpHandler.IsReusable {
get {
return IsReusable;
}
}
void IHttpHandler.ProcessRequest(HttpContext httpContext) {
ProcessRequest(httpContext);
}
#endregion
}
}
到了这一步,MVC 框架已经准备好了相应的执行场景,接下来就是修改默认(指WebForm)的执行流程了。 RequestData data2 = new RequestData();
data2.OriginalPath = context.Request.Path;
data2.HttpHandler = httpHandler;
context.Items[_requestDataKey] = data2;
context.RewritePath("~/UrlRouting.axd");
OnApplicationPostMapRequestHandler被执行。在 PostMapRequestHandler 中,它提取了前面预先准备好的上下文,并修改了HttpContext.Handler,使得 MvcHandler 接管默认的WebForm的HttpHandler,才是执行ASP.NETMVC的流程。现在来继续看MvcHandler。
首先从 RouteData 中提取 Controller的名字(这个名字是我们在 Global.asax.cs RegisterRoutes 中注册 Route 时提供的),然后获取ControllerFactory,只不过这里面专门提供了一个 ControllerBuilder。
internal ControllerBuilder ControllerBuilder {
get {
if (_controllerBuilder == null) {
_controllerBuilder = ControllerBuilder.Current;
}
return _controllerBuilder;
}
set {
_controllerBuilder = value;
}
}
如果我们自定义MvcHandler,则需要好好的看看ControllerBuilder.Current:
namespace System.Web.Mvc {
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Web;
using System.Web.Mvc.Resources;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class ControllerBuilder {
private static ControllerBuilder _instance = new ControllerBuilder();
private Func<IControllerFactory> _factoryThunk;
public ControllerBuilder() {
SetControllerFactory(new DefaultControllerFactory());
}
public static ControllerBuilder Current {
get {
return _instance;
}
}
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "Calling method multiple times might return different objects.")]
public IControllerFactory GetControllerFactory() {
IControllerFactory controllerFactoryInstance = _factoryThunk();
return controllerFactoryInstance;
}
public void SetControllerFactory(IControllerFactory controllerFactory) {
if (controllerFactory == null) {
throw new ArgumentNullException("controllerFactory");
}
_factoryThunk = () => controllerFactory;
}
public void SetControllerFactory(Type controllerFactoryType) {
if (controllerFactoryType == null) {
throw new ArgumentNullException("controllerFactoryType");
}
if (!typeof(IControllerFactory).IsAssignableFrom(controllerFactoryType)) {
throw new ArgumentException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.ControllerBuilder_MissingIControllerFactory,
controllerFactoryType),
"controllerFactoryType");
}
_factoryThunk = delegate() {
try {
return (IControllerFactory) Activator.CreateInstance(controllerFactoryType);
}
catch (Exception ex) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.ControllerBuilder_ErrorCreatingControllerFactory,
controllerFactoryType),
ex);
}
};
}
}
}
(1) 通常情况下,返回一个默认的 DefaultControllerFactory 实例。
(2) 我们可以在 Application_Start 中通过 ControllerBuilder.Current.SetControllerFactory 方法来注册一个我们自定义的工厂。
(3) 核心代码: factory = (IControllerFactory)Activator.CreateInstance(controllerFactoryType),通过反射创建IControllerFactory;
回到MvcHandler的 ProcessRequest,DefaultControllerFactory.CreateController(RequestContext,requiredString) 来返回 IController 实例。下面看看DefaultControllerFactory的代码:
namespace System.Web.Mvc {
using System;
using System.Collections;
using System.Globalization;
using System.Reflection;
using System.Web;
using System.Web.Mvc.Resources;
using System.Web.Routing;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class DefaultControllerFactory : IControllerFactory {
private static object _controllerTypeCacheLock = new object();
private static ControllerTypeCache _controllerTypeCache = new ControllerTypeCache();
private ControllerTypeCache _currentControllerTypeCache;
private IBuildManager _buildManager;
internal IBuildManager BuildManager {
get {
if (_buildManager == null) {
_buildManager = new BuildManagerWrapper();
}
return _buildManager;
}
set {
_buildManager = value;
}
}
internal ControllerTypeCache ControllerTypeCache {
get {
if (_currentControllerTypeCache == null) {
_currentControllerTypeCache = _controllerTypeCache;
}
return _currentControllerTypeCache;
}
set {
_currentControllerTypeCache = value;
}
}
public RequestContext RequestContext {
get;
set;
}
protected internal virtual IController CreateController(RequestContext requestContext, string controllerName) {
if (requestContext == null) {
throw new ArgumentNullException("requestContext");
}
if (String.IsNullOrEmpty(controllerName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
RequestContext = requestContext;
Type controllerType = GetControllerType(controllerName);
IController controller = GetControllerInstance(controllerType);
return controller;
}
protected internal virtual void DisposeController(IController controller) {
IDisposable disposable = controller as IDisposable;
if (disposable != null) {
disposable.Dispose();
}
}
protected internal virtual IController GetControllerInstance(Type controllerType) {
if (controllerType == null) {
throw new ArgumentNullException(
"controllerType",
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.DefaultControllerFactory_NoControllerFound,
RequestContext.HttpContext.Request.Path));
}
if (!typeof(IController).IsAssignableFrom(controllerType)) {
throw new ArgumentException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.DefaultControllerFactory_MissingIController,
controllerType),
"controllerType");
}
try {
return Activator.CreateInstance(controllerType) as IController;
}
catch (Exception ex) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.DefaultControllerFactory_ErrorCreatingController,
controllerType),
ex);
}
}
protected internal virtual Type GetControllerType(string controllerName) {
if (String.IsNullOrEmpty(controllerName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
if (!ControllerTypeCache.Initialized) {
lock (_controllerTypeCacheLock) {
if (!ControllerTypeCache.Initialized) {
ControllerTypeCache.Initialize();
// Go through all assemblies referenced by the application and search for
// controllers and controller factories.
ICollection assemblies = BuildManager.GetReferencedAssemblies();
foreach (Assembly asm in assemblies) {
try {
Type[] typesInAsm = asm.GetTypes();
foreach (Type t in typesInAsm) {
if (IsController(t)) {
string foundControllerName = t.Name.Substring(0, t.Name.Length - 10);
if (!ControllerTypeCache.ContainsController(foundControllerName)) {
ControllerTypeCache.AddControllerType(foundControllerName, t);
}
else {
// A null value indicates a conflict for this key (i.e. more than one match)
ControllerTypeCache.AddControllerType(foundControllerName, null);
}
}
}
}
catch {
// TODO: Remove this try-catch. This is a temporary fix until we add a new feature.
}
}
}
}
}
// Once the master list of controllers has been created we can quickly index into it
Type controllerType;
if (ControllerTypeCache.TryGetControllerType(controllerName, out controllerType)) {
if (controllerType == null) {
// A null value indicates a conflict for this key (i.e. more than one match)
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.DefaultControllerFactory_DuplicateControllers,
controllerName));
}
return controllerType;
}
return null;
}
private static bool IsController(Type t) {
return
t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
(t != typeof(IController)) &&
(t != typeof(Controller)) &&
typeof(IController).IsAssignableFrom(t);
}
#region IControllerFactory Members
IController IControllerFactory.CreateController(RequestContext context, string controllerName) {
return CreateController(context, controllerName);
}
void IControllerFactory.DisposeController(IController controller) {
DisposeController(controller);
}
#endregion
}
}
通过反射来创建 Controller 实例,GetControllerType 里面做了些缓存处理,以此来避免频繁使用反射造成的性能问题。继续 MvcHandler.ProcessRequest(),在得到控制器实例后,MvcHandler 开始了调用 Controller.Execute()来进一步后续操作,同时对其上下文进一步封装,除了前面创建的 RequestContext,还加上了当前这个 Controller对象的引用,类名叫ControllerContext。
namespace System.Web.Mvc {
using System.Web.Routing;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class ControllerContext : RequestContext {
public ControllerContext(HttpContextBase httpContext, RouteData routeData, IController controller)
: base(httpContext, routeData) {
if (controller == null) {
throw new ArgumentNullException("controller");
}
Controller = controller;
}
public ControllerContext(RequestContext requestContext, IController controller)
: this(GetRequestContext(requestContext).HttpContext, GetRequestContext(requestContext).RouteData, controller) {
}
public IController Controller {
get;
private set;
}
internal static RequestContext GetRequestContext(RequestContext requestContext) {
if (requestContext == null) {
throw new ArgumentNullException("requestContext");
}
return requestContext;
}
}
}
继续看IController的默认实现类Controller:
namespace System.Web.Mvc {
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc.Resources;
using System.Web.Routing;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class Controller : IController {
private RouteCollection _routeCollection;
private IDictionary<string, object> _viewData;
private IViewEngine _viewEngine;
public ControllerContext ControllerContext {
get;
set;
}
public HttpContextBase HttpContext {
get {
return ControllerContext == null ? null : ControllerContext.HttpContext;
}
}
public HttpRequestBase Request {
get {
return HttpContext == null ? null : HttpContext.Request;
}
}
public HttpResponseBase Response {
get {
return HttpContext == null ? null : HttpContext.Response;
}
}
internal RouteCollection RouteCollection {
get {
if (_routeCollection == null) {
_routeCollection = RouteTable.Routes;
}
return _routeCollection;
}
set {
_routeCollection = value;
}
}
public RouteData RouteData {
get {
return ControllerContext == null ? null : ControllerContext.RouteData;
}
}
public HttpServerUtilityBase Server {
get {
return HttpContext == null ? null : HttpContext.Server;
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
Justification = "This property is settable so that unit tests can provide mock implementations.")]
public TempDataDictionary TempData {
get;
set;
}
public IPrincipal User {
get {
return HttpContext == null ? null : HttpContext.User;
}
}
public IDictionary<string, object> ViewData {
get {
if (_viewData == null) {
_viewData = new Dictionary<string, object>();
}
return _viewData;
}
}
public IViewEngine ViewEngine {
get {
return _viewEngine ?? new WebFormViewEngine();
}
set {
if (value == null) {
throw new ArgumentNullException("value");
}
_viewEngine = value;
}
}
private static object ConvertParameterType(object value, TypedestinationType, string parameterName, string actionName) {
if (value == null || value.GetType() == destinationType) {
return value;
}
TypeConverter converter = TypeDescriptor.GetConverter(destinationType);
bool canConvertFrom = converter.CanConvertFrom(value.GetType());
if (!canConvertFrom) {
converter = TypeDescriptor.GetConverter(value.GetType());
}
if (!(canConvertFrom || converter.CanConvertTo(destinationType))) {
throw new InvalidOperationException(String.Format(
CultureInfo.CurrentUICulture,
MvcResources.Controller_CannotConvertParameter,
parameterName, actionName, value, destinationType));
}
try {
return canConvertFrom ? converter.ConvertFrom(value) : converter.ConvertTo(value, destinationType);
}
catch (Exception ex) {
throw new InvalidOperationException(String.Format(
CultureInfo.CurrentUICulture,
MvcResources.Controller_CannotConvertParameter,
parameterName, actionName, value, destinationType),
ex);
}
}
protected internal virtual void Execute(ControllerContext controllerContext) {
if (controllerContext == null) {
throw new ArgumentNullException("controllerContext");
}
ControllerContext = controllerContext;
TempData = new TempDataDictionary(controllerContext.HttpContext);
string actionName = RouteData.GetRequiredString("action");
if (!InvokeAction(actionName)) {
HandleUnknownAction(actionName);
}
}
protected internal virtual void HandleUnknownAction(string actionName) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_UnknownAction, actionName));
}
protected internal bool InvokeAction(string actionName) {
return InvokeAction(actionName, new RouteValueDictionary());
}
protected internal virtual bool InvokeAction(string actionName, RouteValueDictionary values) {
if (String.IsNullOrEmpty(actionName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
// We have to loop through all the methods to make sure there isn't
// a conflict. If we stop the loop the first time we find a match
// we might miss some error cases.
MemberInfo[] membInfos = GetType().GetMember(actionName, MemberTypes.Method,
BindingFlags.IgnoreCase | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
MethodInfo foundMatch = null;
foreach (MemberInfo memberInfo in membInfos) {
MethodInfo mi = (MethodInfo)memberInfo;
// 1) Action methods must not have the non-action attribute in their inheritance chain, and
// 2) special methods like constructors, property accessors, and event accessors cannot be action methods, and
// 3) methods originally defined on Object (like ToString) or Controller cannot be action methods.
if (!mi.IsDefined(typeof(NonActionAttribute), true) &&
!mi.IsSpecialName &&
mi.DeclaringType.IsSubclassOf(typeof(Controller))) {
if (foundMatch != null) {
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MoreThanOneAction, actionName, GetType()));
}
foundMatch = mi;
}
}
if (foundMatch != null) {
InvokeActionMethod(foundMatch, values);
return true;
}
return false;
}
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
Justification = "We use MethodInfo since it represents only methods and not constructors." +
"This method only makes sense for use with methods.")]
protected internal virtual void InvokeActionMethod(MethodInfo methodInfo, RouteValueDictionary values) {
if (methodInfo == null) {
throw new ArgumentNullException("methodInfo");
}
if (values == null) {
values = new RouteValueDictionary();
}
if (methodInfo.ContainsGenericParameters) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_ActionCannotBeGeneric, methodInfo.Name));
}
ParameterInfo[] methodParameters = methodInfo.GetParameters();
object[] parameterValues = null;
if (methodParameters.Length > 0) {
parameterValues = new object[methodParameters.Length];
for (int i = 0; i < methodParameters.Length; i++) {
ParameterInfo pi = methodParameters;
if (pi.IsOut || pi.ParameterType.IsByRef) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_ReferenceParametersNotSupported, pi.Name,methodInfo.Name));
}
bool valueRequired = true;
if (pi.ParameterType.IsClass) {
// Classes (ref types) don't require values since we can pass in null
valueRequired = false;
}
else {
if ((pi.ParameterType.IsGenericType && !pi.ParameterType.IsGenericTypeDefinition) &&
(pi.ParameterType.GetGenericTypeDefinition() == typeof(Nullable<>))) {
// Nullable types don't require values since we can pass in null
valueRequired = false;
}
}
// Try to get a value for the parameter. We use this order of precedence:
// 1. Explicitly-provided extra parameters in the call to InvokeAction()
// 2. Values from the RouteData (could be from the typed-in URL or from the route's default values)
// 3. Request values (query string, form post data, cookie)
object parameterValue = null;
if (!values.TryGetValue(methodParameters.Name, out parameterValue)) {
if (RouteData == null || !RouteData.Values.TryGetValue(methodParameters.Name, out parameterValue)) {
if (Request != null) {
parameterValue = Request[methodParameters.Name];
}
}
}
if (parameterValue == null && valueRequired) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name));
}
try {
parameterValues = ConvertParameterType(parameterValue,methodParameters.ParameterType, methodParameters.Name,methodInfo.Name);
}
catch (Exception ex) {
// Parameter value conversion errors are acceptable unless the value is required
if (valueRequired) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name),ex);
}
}
}
}
InvokeActionMethodFilters(methodInfo, () => methodInfo.Invoke(this, parameterValues));
}
private void InvokeActionMethodFilters(MethodInfo methodInfo, Action continuation) {
// filters should execute in this order:
// controller virtual overrides -> controller base attributes -> controller type attributes ->
// base action method attributes -> action method attributes
List<ActionFilterAttribute> filters = new List<ActionFilterAttribute>() {
new ControllerActionFilter(this)
};
Stack<MemberInfo> memberChain = new Stack<MemberInfo>();
Type curType = GetType();
while (curType != null) {
memberChain.Push(curType);
curType = curType.BaseType;
}
List<ActionFilterAttribute> sortedClassFilters = SortActionFilters(memberChain);
filters.AddRange(sortedClassFilters);
List<ActionFilterAttribute> sortedMethodFilters = PrepareMethodActionFilters(methodInfo);
filters.AddRange(sortedMethodFilters);
FilterContext context = new FilterContext(ControllerContext, methodInfo);
ActionFilterExecutor executor = new ActionFilterExecutor(filters, context, continuation);
executor.Execute();
}
protected virtual void OnActionExecuted(FilterExecutedContext filterContext) {
}
protected virtual void OnActionExecuting(FilterExecutingContext filterContext) {
}
internal static List<ActionFilterAttribute> PrepareMethodActionFilters(MethodInfo methodInfo) {
Stack<MemberInfo> memberChain = new Stack<MemberInfo>();
memberChain.Push(methodInfo);
MethodInfo baseMethod = methodInfo.GetBaseDefinition();
Type curType = methodInfo.DeclaringType.BaseType;
while (true) {
MemberInfo[] membInfos = curType.GetMember(methodInfo.Name, MemberTypes.Method,
BindingFlags.IgnoreCase | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
MethodInfo foundMatch = null;
foreach (MemberInfo memberInfo in membInfos) {
MethodInfo mi = (MethodInfo)memberInfo;
if (mi.GetBaseDefinition() == baseMethod && mi.DeclaringType == curType) {
foundMatch = mi;
break;
}
}
if (foundMatch == null) {
break;
}
memberChain.Push(foundMatch);
curType = curType.BaseType;
}
return SortActionFilters(memberChain);
}
protected virtual void RedirectToAction(RouteValueDictionary values) {
VirtualPathData vpd = RouteCollection.GetVirtualPath(ControllerContext, values);
string target = null;
if (vpd != null) {
target = vpd.VirtualPath;
}
HttpContext.Response.Redirect(target);
}
protected void RedirectToAction(string actionName) {
if (String.IsNullOrEmpty(actionName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
RouteValueDictionary valuesDictionary = new RouteValueDictionary();
valuesDictionary.Add("action", actionName);
RedirectToAction(valuesDictionary);
}
protected void RedirectToAction(string actionName, string controllerName) {
if (String.IsNullOrEmpty(actionName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
if (String.IsNullOrEmpty(controllerName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
RouteValueDictionary valuesDictionary = new RouteValueDictionary();
valuesDictionary.Add("action", actionName);
valuesDictionary.Add("controller", controllerName);
RedirectToAction(valuesDictionary);
}
protected void RenderView(string viewName) {
RenderView(viewName, String.Empty, ViewData);
}
protected void RenderView(string viewName, string masterName) {
RenderView(viewName, masterName, ViewData);
}
protected void RenderView(string viewName, object viewData) {
RenderView(viewName, String.Empty, viewData);
}
protected virtual void RenderView(string viewName, string masterName, object viewData) {
ViewContext viewContext = new ViewContext(ControllerContext, viewName, masterName, viewData, TempData);
ViewEngine.RenderView(viewContext);
}
private static List<ActionFilterAttribute> SortActionFilters(Stack<MemberInfo> memberChain) {
List<ActionFilterAttribute> filters = new List<ActionFilterAttribute>();
foreach (MemberInfo member in memberChain) {
ActionFilterAttribute[] attrs =(ActionFilterAttribute[])member.GetCustomAttributes(typeof(ActionFilterAttribute),false /* inherit */);
SortedList<int,ActionFilterAttribute> orderedFilters = new SortedList<int,ActionFilterAttribute>();
foreach (ActionFilterAttribute filter in attrs) {
// filters are allowed to have the same order only if the order is -1. in that case,
// they are processed before explicitly ordered filters but in no particular order in
// relation to one another.
if (filter.Order >= 0) {
if (orderedFilters.ContainsKey(filter.Order)) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.ActionFilter_DuplicateOrder,
member,
filter.Order));
}
orderedFilters.Add(filter.Order, filter);
}
else {
filters.Add(filter);
}
}
filters.AddRange(orderedFilters.Values);
}
return filters;
}
#region IController Members
void IController.Execute(ControllerContext controllerContext) {
Execute(controllerContext);
}
#endregion
private sealed class ControllerActionFilter : ActionFilterAttribute {
private Controller _controller;
public ControllerActionFilter(Controller controller) {
_controller = controller;
}
public override void OnActionExecuted(FilterExecutedContext filterContext) {
_controller.OnActionExecuted(filterContext);
}
public override void OnActionExecuting(FilterExecutingContext filterContext) {
_controller.OnActionExecuting(filterContext);
}
}
}
}
获取 Action 的名字,然后开始执行 InvokeAction,如果找不到Action,则调用HandleUnknownAction,这是一个虚拟方法,可以在子类中重写,默认是抛出一个异常InvalidOperationException。
protected internal virtual void Execute(ControllerContext controllerContext) {
if (controllerContext == null) {
throw new ArgumentNullException("controllerContext");
}
ControllerContext = controllerContext;
TempData = new TempDataDictionary(controllerContext.HttpContext);
string actionName = RouteData.GetRequiredString("action");
if (!InvokeAction(actionName)) {
HandleUnknownAction(actionName);
}
}
详细看看InvokeAction方式的执行:
protected internal bool InvokeAction(string actionName) {
return InvokeAction(actionName, new RouteValueDictionary());
}
protected internal virtual bool InvokeAction(string actionName, RouteValueDictionary values) {
if (String.IsNullOrEmpty(actionName)) {
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName");
}
// We have to loop through all the methods to make sure there isn't
// a conflict. If we stop the loop the first time we find a match
// we might miss some error cases.
MemberInfo[] membInfos = GetType().GetMember(actionName, MemberTypes.Method,
BindingFlags.IgnoreCase | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public);
MethodInfo foundMatch = null;
foreach (MemberInfo memberInfo in membInfos) {
MethodInfo mi = (MethodInfo)memberInfo;
// 1) Action methods must not have the non-action attribute in their inheritance chain, and
// 2) special methods like constructors, property accessors, and event accessors cannot be action methods, and
// 3) methods originally defined on Object (like ToString) or Controller cannot be action methods.
if (!mi.IsDefined(typeof(NonActionAttribute), true) &&
!mi.IsSpecialName &&
mi.DeclaringType.IsSubclassOf(typeof(Controller))) {
if (foundMatch != null) {
throw new InvalidOperationException(
String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MoreThanOneAction, actionName, GetType()));
}
foundMatch = mi;
}
}
if (foundMatch != null) {
InvokeActionMethod(foundMatch, values);
return true;
}
return false;
}
它通过反射获取所有同名 Action 方法信息;其次,它过滤掉所有有 NonActionAttribute 和 IsSpecialName标记的方法;第三,当同名有效 Action被重载时它会抛出异常(提示Controller_MoreThanOneAction),继续调用InvokeActionMethod:
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
Justification = "We use MethodInfo since it represents only methods and not constructors." +
"This method only makes sense for use with methods.")]
protected internal virtual void InvokeActionMethod(MethodInfo methodInfo, RouteValueDictionary values) {
if (methodInfo == null) {
throw new ArgumentNullException("methodInfo");
}
if (values == null) {
values = new RouteValueDictionary();
}
if (methodInfo.ContainsGenericParameters) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_ActionCannotBeGeneric, methodInfo.Name));
}
ParameterInfo[] methodParameters = methodInfo.GetParameters();
object[] parameterValues = null;
if (methodParameters.Length > 0) {
parameterValues = new object[methodParameters.Length];
for (int i = 0; i < methodParameters.Length; i++) {
ParameterInfo pi = methodParameters;
if (pi.IsOut || pi.ParameterType.IsByRef) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_ReferenceParametersNotSupported, pi.Name,methodInfo.Name));
}
bool valueRequired = true;
if (pi.ParameterType.IsClass) {
// Classes (ref types) don't require values since we can pass in null
valueRequired = false;
}
else {
if ((pi.ParameterType.IsGenericType && !pi.ParameterType.IsGenericTypeDefinition) &&
(pi.ParameterType.GetGenericTypeDefinition() == typeof(Nullable<>))) {
// Nullable types don't require values since we can pass in null
valueRequired = false;
}
}
// Try to get a value for the parameter. We use this order of precedence:
// 1. Explicitly-provided extra parameters in the call to InvokeAction()
// 2. Values from the RouteData (could be from the typed-in URL or from the route's default values)
// 3. Request values (query string, form post data, cookie)
object parameterValue = null;
if (!values.TryGetValue(methodParameters.Name, out parameterValue)) {
if (RouteData == null || !RouteData.Values.TryGetValue(methodParameters.Name, out parameterValue)) {
if (Request != null) {
parameterValue = Request[methodParameters.Name];
}
}
}
if (parameterValue == null && valueRequired) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name));
}
try {
parameterValues = ConvertParameterType(parameterValue,methodParameters.ParameterType, methodParameters.Name,methodInfo.Name);
}
catch (Exception ex) {
// Parameter value conversion errors are acceptable unless the value is required
if (valueRequired) {
throw newInvalidOperationException(String.Format(CultureInfo.CurrentUICulture,MvcResources.Controller_MissingParameter, pi.Name, methodInfo.Name),ex);
}
}
}
}
InvokeActionMethodFilters(methodInfo, () => methodInfo.Invoke(this, parameterValues));
}
这个方法很复杂,有大量的代码是参数的分解 ,只有最后一行是关键的。 InvokeActionMethodFilters(methodInfo, () => methodInfo.Invoke(this, parameterValues));
这行代码将 Action 的调用作为一个委托,连同反射信息传递给 InvokeActionMethodFilters。
private void InvokeActionMethodFilters(MethodInfo methodInfo, Action continuation) {
// filters should execute in this order:
// controller virtual overrides -> controller base attributes -> controller type attributes ->
// base action method attributes -> action method attributes
List<ActionFilterAttribute> filters = new List<ActionFilterAttribute>() {
new ControllerActionFilter(this)
};
Stack<MemberInfo> memberChain = new Stack<MemberInfo>();
Type curType = GetType();
while (curType != null) {
memberChain.Push(curType);
curType = curType.BaseType;
}
List<ActionFilterAttribute> sortedClassFilters = SortActionFilters(memberChain);
filters.AddRange(sortedClassFilters);
List<ActionFilterAttribute> sortedMethodFilters = PrepareMethodActionFilters(methodInfo);
filters.AddRange(sortedMethodFilters);
FilterContext context = new FilterContext(ControllerContext, methodInfo);
ActionFilterExecutor executor = new ActionFilterExecutor(filters, context, continuation);
executor.Execute();
}
这个方法首先将默认的过滤器 ControllerActionFilter加到列表,然后提取所有继承层次上基类的过滤器特性。最后将这些过滤器集合、过滤上下文,连同前一个方法传递进来的 Action执行委托(continuation) 再次转交给了一个 ActionFilterExecutor 对象实例,并调用其 Execute 方法。
namespace System.Web.Mvc {
using System;
using System.Collections;
using System.Collections.Generic;
internal sealed class ActionFilterExecutor {
private FilterContext _context;
private Action _continuation;
private List<ActionFilterAttribute> _filters;
public ActionFilterExecutor(List<ActionFilterAttribute> filters, FilterContext context, Action continuation) {
_filters = filters;
_context = context;
_continuation = continuation;
}
public void Execute() {
IEnumerator<ActionFilterAttribute> enumerator = _filters.GetEnumerator();
ExecuteRecursive(enumerator);
}
private FilterExecutedContext ExecuteRecursive(IEnumerator<ActionFilterAttribute> enumerator) {
if (enumerator.MoveNext()) {
ActionFilterAttribute filter = enumerator.Current;
FilterExecutingContext preContext = new FilterExecutingContext(_context);
filter.OnActionExecuting(preContext);
if (preContext.Cancel) {
return new FilterExecutedContext(_context, null /* exception */);
}
bool wasError = false;
FilterExecutedContext postContext = null;
try {
postContext = ExecuteRecursive(enumerator);
}
catch (Exception ex) {
wasError = true;
postContext = new FilterExecutedContext(_context, ex);
filter.OnActionExecuted(postContext);
if (!postContext.ExceptionHandled) {
throw;
}
}
if (!wasError) {
filter.OnActionExecuted(postContext);
}
return postContext;
}
else {
_continuation();
return new FilterExecutedContext(_context, null /* exception */);
}
}
}
}
ExecuteRecursive使用了递归算法,通过迭代器 MoveNext() 方法提取一个过滤器对象,执行其 OnActionExecuting 方法。 如果该方法设置了filterContext.Cancel =true,则放弃后续执行代码。这种机制为我们提供了更好的控制,例如用它来实现伪静态页。一层一层调用所有的ActionFilterAttribute.OnActionExecuting 方法,直到 MoveNext() == false。在最后一次递归调用时,由于 enumerator.MoveNext() == false, _continuation()方法被执行。这个就是前面给传递过来的 Action 方法委托,Action 方法总算是执行了。 在 Action委托执行完成后,递归调逐级往上回溯,直到最初那个方法堆栈。这样所有ActionFilterAttribute.OnActionExecuted也被执行完成。
到此开始进入最后的视图呈现阶段,可以把数据呈现到视图上,Controller 提供了几个重载的 RenderView() 来完成这个工作。
protected void RenderView(string viewName) {
RenderView(viewName, String.Empty, ViewData);
}
protected void RenderView(string viewName, string masterName) {
RenderView(viewName, masterName, ViewData);
}
protected void RenderView(string viewName, object viewData) {
RenderView(viewName, String.Empty, viewData);
}
protected virtual void RenderView(string viewName, string masterName, object viewData) {
ViewContext viewContext = new ViewContext(ControllerContext, viewName, masterName, viewData, TempData);
ViewEngine.RenderView(viewContext);
}
将一路传递过来的相关 "数据" (上下文)ControllerContext 再次包装成ViewContext。当然,这次依然会多出些东西,里面就有我们向视图传递的数据 —— viewData 和tempData。作为默认选择,MVC 创建WebForm 视图引擎来展示结果。其他的视图引擎可以去看mvccontrib,这个项目就是整合:Castle Windsor 、StructureMap 、Spring.NET 等IoC框架以及视图引擎,包括Castle MonoRail所用的NVelocityView视图引擎、NHamlView视图引擎、XsltViewEngine视图引擎等等。
继续看这个 WebFormViewEngine:
namespace System.Web.Mvc {
using System;
using System.Globalization;
using System.Web.Compilation;
using System.Web.Resources;
using System.Web.Routing;
using System.Web.Mvc.Resources;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class WebFormViewEngine : IViewEngine {
private IBuildManager _buildManager;
private IViewLocator _viewLocator;
internal IBuildManager BuildManager {
get {
if (_buildManager == null) {
_buildManager = new BuildManagerWrapper();
}
return _buildManager;
}
set {
_buildManager = value;
}
}
public IViewLocator ViewLocator {
get {
if (_viewLocator == null) {
_viewLocator = new WebFormViewLocator();
}
return _viewLocator;
}
set {
_viewLocator = value;
}
}
protected virtual void RenderView(ViewContext viewContext) {
if (viewContext == null) {
throw new ArgumentNullException("viewContext");
}
string viewPath = ViewLocator.GetViewLocation(viewContext, viewContext.ViewName);
if (String.IsNullOrEmpty(viewPath)) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.WebFormViewEngine_ViewNotFound,
viewContext.ViewName));
}
object viewInstance = BuildManager.CreateInstanceFromVirtualPath(viewPath, typeof(object));
if (viewInstance == null) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.WebFormViewEngine_ViewCouldNotBeCreated,
viewPath));
}
ViewPage viewPage = viewInstance as ViewPage;
if (viewPage != null) {
if (!String.IsNullOrEmpty(viewContext.MasterName)) {
string masterLocation = ViewLocator.GetMasterLocation(viewContext, viewContext.MasterName);
if (String.IsNullOrEmpty(masterLocation)) {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.WebFormViewEngine_MasterNotFound,
viewContext.MasterName));
}
// We don't set the page's MasterPageFile directly since it will get
// overwritten by a statically-defined value. In ViewPage we wait until
// the PreInit phase until we set the new value.
viewPage.MasterLocation = masterLocation;
}
viewPage.SetViewData(viewContext.ViewData);
viewPage.RenderView(viewContext);
}
else {
ViewUserControl viewUserControl = viewInstance as ViewUserControl;
if (viewUserControl != null) {
if (!String.IsNullOrEmpty(viewContext.MasterName)) {
throw new InvalidOperationException(MvcResources.WebFormViewEngine_UserControlCannotHaveMaster);
}
viewUserControl.SetViewData(viewContext.ViewData);
viewUserControl.RenderView(viewContext);
}
else {
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
MvcResources.WebFormViewEngine_WrongViewBase,
viewPath));
}
}
}
#region IViewEngine Members
void IViewEngine.RenderView(ViewContext viewContext) {
RenderView(viewContext);
}
#endregion
}
}
首先会创建一个 WebFormViewLocator 对象来获取视图存放路径。
namespace System.Web.Mvc {
public class WebFormViewLocator : ViewLocator {
public WebFormViewLocator() {
ViewLocationFormats = new[] {
"~/Views/{1}/{0}.aspx",
"~/Views/{1}/{0}.ascx",
"~/Views/Shared/{0}.aspx",
"~/Views/Shared/{0}.ascx"
};
MasterLocationFormats = new[] {
"~/Views/{1}/{0}.master",
"~/Views/Shared/{0}.master"
};
}
}
}
在获取路径后,WebFormViewEngine 通过一个包装类 BuildManagerWrapper 间接调用System.Web.Compilation.BuildManager 的静态方法CreateInstanceFromVirtualPath() 将视图进行编译,并返回一个对象实例。(System.Web.Compilation.BuildManagerBuildManager 类管理应用程序的程序集和页的编译过程),后面通过 as 转换结果来判断视图是 ViewPage 还是ViewUserControl。ViewPage 继承自我们所熟悉的 System.Web.UI.Page,它的 RenderView()方法也就是完成WebForm的 Page.ProcessRequest() 的处理而已,也就完成了对WebForm模型的置换。
下面看看ViewPage和ViewUserControl的代码:
namespace System.Web.Mvc {
using System.Web.UI;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class ViewPage : Page, IViewDataContainer {
private string _masterLocation;
private object _viewData;
public string MasterLocation {
get {
return _masterLocation ?? String.Empty;
}
set {
_masterLocation = value;
}
}
public AjaxHelper Ajax {
get;
set;
}
public HtmlHelper Html {
get;
set;
}
public TempDataDictionary TempData {
get {
return ViewContext.TempData;
}
}
public UrlHelper Url {
get;
set;
}
public ViewContext ViewContext {
get;
private set;
}
public ViewData ViewData {
get {
return new ViewData(_viewData);
}
}
public HtmlTextWriter Writer {
get;
private set;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")]
protected override void OnPreInit(EventArgs e) {
base.OnPreInit(e);
if (!String.IsNullOrEmpty(MasterLocation)) {
MasterPageFile = MasterLocation;
}
}
public virtual void RenderView(ViewContext viewContext) {
ViewContext = viewContext;
Ajax = new AjaxHelper(viewContext);
Html = new HtmlHelper(viewContext);
Url = new UrlHelper(viewContext);
ProcessRequest(HttpContext.Current);
}
protected override void Render(HtmlTextWriter writer) {
Writer = writer;
try {
base.Render(writer);
}
finally {
Writer = null;
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
Justification = "There is already a ViewData property and it has a slightly different meaning.")]
protected internal virtual void SetViewData(object viewData) {
_viewData = viewData;
}
#region IViewDataContainer Members
object IViewDataContainer.ViewData {
get {
return _viewData;
}
}
#endregion
}
}
namespace System.Web.Mvc {
using System.ComponentModel;
using System.Globalization;
using System.Web.Resources;
using System.Web.UI;
using System.Web.Mvc.Resources;
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand,Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,Level = AspNetHostingPermissionLevel.Minimal)]
public class ViewUserControl : UserControl, IViewDataContainer {
private string _viewDataKey;
private object _viewData;
public AjaxHelper Ajax {
get {
return ViewPage.Ajax;
}
}
public HtmlHelper Html {
get {
return ViewPage.Html;
}
}
public TempDataDictionary TempData {
get {
return ViewPage.TempData;
}
}&n
