最近有一段时间都在看ASP.NET MVC框架的源代码。原来的想法是,如果有相应ASP.NET MVC的文档,就直接看文档,学学怎么使用这个框架。可惜都是视频教程,而又感觉看那本电子书有点无趣,索性就直接看源代码了。这样一来反而收获了很多的东西。
在ASP.NET MVC框架里面,用到了URL Routing组件。URL Routing组件干什么用呢?根据你配置的URL 模式来提取数据或者生成相应的URL 。但我有一个疑问:URL Routing组件是如何同ASP.NET MVC框架关联起来的?
由于这个组件没有开放源代码,所以我们查看代码的方式也就只有反编译了。我们可以看到Route类型。这个类表示一条路由,包括URL模式,路由数据,HTTP处理程序等。我们可以看到它的构造器如下:
public Route(string url, IRouteHandler routeHandler);
public Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler);
public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler);
public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler);
public interface IRouteHandler{
IHttpHandler GetHttpHandler(RequestContext requestContext);
}
沿着这个思路,我们继续往下找,看能不能找到一个实现这个接口的类型。一开始看到一个UrlRoutingHandler,以为找到要找的类型了,不过仔细一开类型的定义,是抽象类型,表示Routing的终点,因为这个抽象类实现了IHttpHandler接口。查看它的ProcessRequest方法的代码,如下:
protected virtual void ProcessRequest(HttpContextBase httpContext)
{
RouteData routeData = this.RouteCollection.GetRouteData(httpContext);
if (routeData == null)
{
throw new HttpException(0x194, SR.GetString("UrlRoutingHandler_NoRouteMatches"));
}
IRouteHandler routeHandler = routeData.RouteHandler;
if (routeHandler == null)
{
throw new InvalidOperationException(SR.GetString("UrlRoutingModule_NoRouteHandler"));
}
RequestContext requestContext = new RequestContext(httpContext, routeData);
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
if (httpHandler == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[] { routeHandler.GetType() }));
}
this.VerifyAndProcessRequest(httpHandler, httpContext);
}
protected abstract void VerifyAndProcessRequest(
IHttpHandler httpHandler,
HttpContextBase httpContext);
protected virtual void Init(HttpApplication application)
{
if (application.Context.Items[_contextKey] == null)
{
application.Context.Items[_contextKey] = _contextKey;
application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
}
}
public virtual void PostResolveRequestCache(HttpContextBase context)
{
RouteData routeData = this.RouteCollection.GetRouteData(context);
if (routeData != null)
{
IRouteHandler routeHandler = routeData.RouteHandler;
if (routeHandler == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
}
if (!(routeHandler is StopRoutingHandler))
{
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)
{
if (!FormsAuthenticationModule.FormsAuthRequired)
{
throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3"));
}
UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
}
else
{
context.RemapHandler(httpHandler);
}
}
}
}
这个方法从Route实例取得RouteData,并且从RouteData获得相应的IHttpHandler处理程序来处理请求,这样,URL Routing最终把HTTP请求映射到一个处理程序处理。但是这里有个问题,就是这个处理程序的实例是什么时候初始化,又是如何初始化的?并且,URL Routing是怎么和ASP.NET MVC框架关联起来的?
<httpHandlers>
<remove verb="*" path="*.asmx"/>
<add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/>
<add verb="*" path="*.mvc" validate="false" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpHandlers>
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) {
if (routes == null) {
throw new ArgumentNullException("routes");
}
if (url == null) {
throw new ArgumentNullException("url");
}
Route route = new Route(url, new MvcRouteHandler()) {
Defaults = new RouteValueDictionary(defaults),
Constraints = new RouteValueDictionary(constraints),
DataTokens = new RouteValueDictionary()
};
if ((namespaces != null) && (namespaces.Length > 0)) {
route.DataTokens["Namespaces"] = namespaces;
}
routes.Add(name, route);
return route;
}