深入ASP.NET MVC之二:路由模块如何工作

先看路由模块的PostResolveRequestCache事件中被触发的方法:

双击代码全选

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

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"));  

}

 

这 个方法做的工作还是比较清晰的,首先从RouteCollection中获得RouteData,从RouteData中获得RouteHandler, 从RouteHandler获得httpHandler,最后调用RemapHandler将控制权交给httpHandler。 UrlRoutingModule是System.Web中的通用的路由模块,并不仅限于给ASP.NET MVC使用,这里处理的今本都是针对抽象接口来处理的。后文会介绍ASP.NET MVC是如何利用这个模块实现了URL到controller/action的映射的。
RouteCollection是一张路由表,里面 包括了很多路由规则(RouteBase),RouteData则是解析好的路由,里面包括了Key-Value对的路由信息,一个 routehandler。RouteCollection的GetRouteData方法,是找到符合当前请求的路由规则的RouteData,其内部 实现就是遍历所有的路由规则,调用RouteBase的GetRouteData方法,返回第一个非空的RouteData。IRouteHandler 只有一个方法,

双击代码全选

1

2

3

4

public interface IRouteHandler  

{  

    IHttpHandler GetHttpHandler(RequestContext requestContext);  

}

 

但 是他是从路由模块完成最重要的工作之一,等到其他模块执行完毕之后,将由这个IHttpHandler(如果其他模块没有更改这个handler)来完成 接下来的请求。得到合适的HttpHandler之后,调用了HttpContext的RemapHandler方法,这个方法的核心是

双击代码全选

1

this._remapHandler = handler;

 

此时尚没有真正的转交控制权,当前的handler还是global.asax中的类,在上文中有介绍到在初始化一个请求的时候,由一个StepManager来初始化需要触发的step,其中有:

双击代码全选

1

HttpApplication.IExecutionStep step = new HttpApplication.MaterializeHandlerExecutionStep(application); application.AddEventMapping("ManagedPipelineHandler", RequestNotification.MapRequestHandler, false, step);

 

因此在MapRequestHandler的时候,会触发MaterializeHandlerExecutionStep的Execute方法,其中主要的代码是:

双击代码全选

1

2

3

4

5

6

if (context.RemapHandlerInstance != null)  

        

{  

       IIS7WorkerRequest.SetScriptMapForRemapHandler();  

       context.Handler = context.RemapHandlerInstance;  

}

 

在MapRequestHandler之后调用RemapHandler会导致异常,原因是改变handler的时机是在MapRequestHandler。
下 面看ASP.MVC框架是如何使用这个routing module的。MVC在RouteCollectionExtensions这个类中定义了一系列扩展方法,扩展了原有的 RouteCollection类。RouteCollection类是支持ASP.NET Webform的路由模块,可以用MapPageRoute方法将url映射到aspx文件上。RouteCollectionExtensions的核 心方法是:

双击代码全选

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) {  

    //…   

        

    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;  

}

 

注意Route构造函数中的第二个参数,这是一个MvcRouteHandler,这个handler也就是RouteData中的RouteHandler的值,其实现为:

双击代码全选

1

2

3

4

protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {  

    requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));  

    return new MvcHandler(requestContext);  

}

 

暂时忽略这里的SessionStateBehavior,可以看到最终的httpHandler是MvcHandler。
综 上,routing module在PostResolveRequestCache被触发,获得RouteCollection中的 Route(System.Web.Routing),解析Route数据获得RouteData,MVC框架设置RouteData中的 RouteHandler为MvcRouteHandler,MvcRouteHandler的GetHttpHandler方法返回的是一个 MvcHandler,这个handler最终在MapRequestHandler事件触发的时候接过request处理流程,开始处理请求,它将利用 RouteData中解析好的值去触发controller/action,这个下文再介绍。ASP.NET MVC的路由模块主要是用的.NET中的System.Web.Routing.Route类来实现的,Route类的主要工作是解析路由规则,得到一个 Key-Value对的序列。这是一个单纯而又复杂的过程,这里不分析其实现。