MVC处理程序&Asp.Net路由机制
通过、、
前面三篇我们了解了Http请求在服务器端的处理流程,Http请求最终会由实现了IHttpHandler接口的类进行处理,针对不同的请求,Asp.net要有不同的处理。
MVC也是其中之一,打开机器上C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\web.config
...
...
可以看到系统默认url路由解析模块(System.Web.Routing.UrlRoutingModule)
// System.Web.Routing.UrlRoutingModule
protected virtual void Init(HttpApplication application)
{
if (application.Context.Items[UrlRoutingModule._contextKey] != null)
{
return;
}
application.Context.Items[UrlRoutingModule._contextKey] = UrlRoutingModule._contextKey;
application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
}
- 模块初始化时,在HttpApplication中PostResolveRequestCache注册了动作OnApplicationPostResolveRequestCache
// System.Web.Routing.UrlRoutingModule
private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
{
HttpApplication httpApplication = (HttpApplication)sender;
HttpContextBase context = new HttpContextWrapper(httpApplication.Context);
this.PostResolveRequestCache(context);
}
public virtual void PostResolveRequestCache(HttpContextBase context)
{
RouteData routeData = this.RouteCollection.GetRouteData(context);
if (routeData == null)
{
return;
}
IRouteHandler routeHandler = routeData.RouteHandler;
if (routeHandler == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
}
if (routeHandler is StopRoutingHandler)
{
return;
}
RequestContext requestContext = new RequestContext(context, routeData);
context.Request.RequestContext = requestContext;
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
if (httpHandler == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[]
{
routeHandler.GetType()
}));
}
if (!(httpHandler is UrlAuthFailureHandler))
{
context.RemapHandler(httpHandler);
return;
}
if (FormsAuthenticationModule.FormsAuthRequired)
{
UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
return;
}
throw new HttpException(401, SR.GetString("Assess_Denied_Description3"));
}
- 当程序触发PostResolveRequestCache时,根据url匹配路由,多个路由则按顺序匹配,取第一个匹配的路由
// System.Web.Routing.RouteCollection
public RouteData GetRouteData(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
if (httpContext.Request == null)
{
throw new ArgumentException(SR.GetString("RouteTable_ContextMissingRequest"), "httpContext");
}
if (base.Count == 0)
{
return null;
}
bool flag = false;
bool flag2 = false;
if (!this.RouteExistingFiles)
{
flag = this.IsRouteToExistingFile(httpContext);
flag2 = true;
if (flag)
{
return null;
}
}
using (this.GetReadLock())
{
foreach (RouteBase current in this)
{
RouteData routeData = current.GetRouteData(httpContext);
if (routeData != null)
{
RouteData result;
if (!current.RouteExistingFiles)
{
if (!flag2)
{
flag = this.IsRouteToExistingFile(httpContext);
flag2 = true;
}
if (flag)
{
result = null;
return result;
}
}
result = routeData;
return result;
}
}
}
return null;
}
打开RouteBase源码发现GetRouteData这是一个抽象方法,这就给程序提供了扩展空间,因此这套流程同样适用于MVC、WebApi、WebService、WebForm...只需要重写GetRouteData方法
using System;
using System.Runtime.CompilerServices;
namespace System.Web.Routing
{
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
public abstract class RouteBase
{
private bool _routeExistingFiles = true;
public bool RouteExistingFiles
{
get
{
return this._routeExistingFiles;
}
set
{
this._routeExistingFiles = value;
}
}
public abstract RouteData GetRouteData(HttpContextBase httpContext);
public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
}
}
// System.Web.Routing.Route
public override RouteData GetRouteData(HttpContextBase httpContext)
{
string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
RouteValueDictionary routeValueDictionary = this._parsedRoute.Match(virtualPath, this.Defaults);
if (routeValueDictionary == null)
{
return null;
}
RouteData routeData = new RouteData(this, this.RouteHandler);
if (!this.ProcessConstraints(httpContext, routeValueDictionary, RouteDirection.IncomingRequest))
{
return null;
}
foreach (KeyValuePair current in routeValueDictionary)
{
routeData.Values.Add(current.Key, current.Value);
}
if (this.DataTokens != null)
{
foreach (KeyValuePair current2 in this.DataTokens)
{
routeData.DataTokens[current2.Key] = current2.Value;
}
}
return routeData;
}
在 Asp.net MVC中,路由处理程序必须实现接口IRouteHandler,这个处理程序的类是MvcRouteHandler
using System;
using System.Runtime.CompilerServices;
namespace System.Web.Routing
{
[TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
public interface IRouteHandler
{
IHttpHandler GetHttpHandler(RequestContext requestContext);
}
}
定义了一个获取处理程序的方法GetHttpHandler
// System.Web.Mvc.MvcRouteHandler
IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
{
return this.GetHttpHandler(requestContext);
}
/// Returns the HTTP handler by using the specified HTTP context.
/// The HTTP handler.
/// The request context.
protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.HttpContext.SetSessionStateBehavior(this.GetSessionStateBehavior(requestContext));
return new MvcHandler(requestContext);
}
这里指定了MVC流程后续页面级处理为MvcHandler
// System.Web.Mvc.MvcHandler
/// Processes the request by using the specified HTTP request context.
/// The HTTP context.
protected virtual void ProcessRequest(HttpContext httpContext)
{
HttpContextBase httpContext2 = new HttpContextWrapper(httpContext);
this.ProcessRequest(httpContext2);
}
/// Processes the request by using the specified base HTTP request context.
/// The HTTP context.
protected internal virtual void ProcessRequest(HttpContextBase httpContext)
{
IController controller;
IControllerFactory controllerFactory;
this.ProcessRequestInit(httpContext, out controller, out controllerFactory);
try
{
controller.Execute(this.RequestContext);
}
finally
{
controllerFactory.ReleaseController(controller);
}
}
在MvcHandler中 ProcessRequestInit方法 由ControllerBuilder创建ControllerFactory,再由ControllerFactory创建Controller
// System.Web.Mvc.MvcHandler
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory)
{
HttpContext current = HttpContext.Current;
if (current != null)
{
bool? flag = ValidationUtility.IsValidationEnabled(current);
bool? flag2 = flag;
if (flag2.GetValueOrDefault() && flag2.HasValue)
{
ValidationUtility.EnableDynamicValidation(current);
}
}
this.AddVersionHeader(httpContext);
this.RemoveOptionalRoutingParameters();
string requiredString = this.RequestContext.RouteData.GetRequiredString("controller");
factory = this.ControllerBuilder.GetControllerFactory();
controller = factory.CreateController(this.RequestContext, requiredString);
if (controller == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_FactoryReturnedNull, new object[]
{
factory.GetType(),
requiredString
}));
}
}
// System.Web.Mvc.ControllerBuilder
private Func _factoryThunk = () => null;
private HashSet _namespaces = new HashSet(StringComparer.OrdinalIgnoreCase);
internal ControllerBuilder(IResolver serviceResolver)
{
IResolver arg_6A_1 = serviceResolver;
if (serviceResolver == null)
{
arg_6A_1 = new SingleServiceResolver(() => this._factoryThunk(), new DefaultControllerFactory
{
ControllerBuilder = this
}, "ControllerBuilder.GetControllerFactory");
}
this._serviceResolver = arg_6A_1;
}
MVC中DefaultControllerFactory是默认的控制器工厂
// System.Web.Mvc.DefaultControllerFactory
/// Creates the specified controller by using the specified request context.
/// The controller.
/// The context of the HTTP request, which includes the HTTP context and route data.
/// The name of the controller.
/// The parameter is null.
/// The parameter is null or empty.
public virtual IController CreateController(RequestContext requestContext, string controllerName)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (string.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch())
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
Type controllerType = this.GetControllerType(requestContext, controllerName);
return this.GetControllerInstance(requestContext, controllerType);
}
/// Retrieves the controller instance for the specified request context and controller type.
/// The controller instance.
/// The context of the HTTP request, which includes the HTTP context and route data.
/// The type of the controller.
///
/// is null.
///
/// cannot be assigned.
/// An instance of cannot be created.
protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_NoControllerFound, new object[]
{
requestContext.HttpContext.Request.Path
}));
}
if (!typeof(IController).IsAssignableFrom(controllerType))
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase, new object[]
{
controllerType
}), "controllerType");
}
return this.ControllerActivator.Create(requestContext, controllerType);
}
// System.Web.Mvc.DefaultControllerFactory.DefaultControllerActivator
public IController Create(RequestContext requestContext, Type controllerType)
{
IController result;
try
{
result = (IController)(this._resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));
}
catch (Exception innerException)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, MvcResources.DefaultControllerFactory_ErrorCreatingController, new object[]
{
controllerType
}), innerException);
}
return result;
}
本文参考文档: