UrlRouting的理解

前端之家收集整理的这篇文章主要介绍了UrlRouting的理解前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

根据对Http Runtime和Http Pipeline的分析,我们知道一个ASP.NET应用程序可以有多个HttpModuel,但是只能有一个HttpHandler,并且通过这个HttpHandler的BeginProcessRequest(或ProcessRequest)来处理并返回请求,前面的章节将到了再MapHttpHandler这个周期将会根据请求的URL来查询对应的HttpHandler,那么它是如何查找的呢?

一起我们在做自定义HttpHandler的时候,需要执行URL以及扩展名匹配规则,然后查找HttpHandler的时候就是根据相应的规则来查找哪个HttpHandler可以使用。另一方面我们本系列教材讲的MVC就是通过注册路由(Route)来匹配到对应的Controller和Action上的,例如Global.asax里的代码

routes.MapRoute(
    "Default",{controller}/{action}/{id}new { controller = Home",action = Index 但是在匹配这个之前,MVC首先要接管请求才能处理,也就是说我们要有对应MVC的HttpHandler(后面知道它的名字叫MvcHandler)被MapRequestHandler周期的处理引擎查找到并且应用上才行,然后后面才能由 Controller/Action执行。另外一方面,由于该URL地址没有扩展名,所以无法进入ASP.NET的RunTime,MVC2的实现方式是:注册通配符(*.*)映射到aspnet_ISPAI.dll,然后通过一个自定义的UrlRoutingModuel来匹配Route规则,再继续处理,但是MVC3的时候,匹配Route规则的处理机制集成到ASP.NET4.0里了,也就是今天我们这篇文章所要讲的主角(UrlRoutingModule)的处理机制。

先来看UrlRoutingModule的源码,无容置疑地这个类是继承于IHttpModule,首先看一下Init方法代码

protected virtual void Init(HttpApplication application) {

    ////////////////////////////////////////////////////////////////// 
    // Check if this module has been already addded
    if (application.Context.Items[_contextKey] != null) { 
        return;  already added to the pipeline 
    }
    application.Context.Items[_contextKey] = _contextKey; 

     Ideally we would use the MapRequestHandler event.  However,MapRequestHandler is not available
     in II6 or IIS7 ISAPI Mode.  Instead,we use PostResolveRequestCache,which is the event immediately
     before MapRequestHandler.  This allows use to use one common codepath for all versions of IIS. 
    application.PostResolveRequestCache += OnApplicationPostResolveRequestCache;
}

代码在PostResolveRequestCache周期事件上添加了我们需要执行的方法,用于URL匹配规则的设置,但是为什么要在这个周期点上添加事件呢?看了注释,再结合我们前面对Pipeline的了解,释然了,要像动态注册自己的HttpHandler,那就需要在MapRequestHandler之前进行注册自己的规则(因为这个周期点就是做这个事情的),但由于IIS6不支持这个事件,所以为了能让IIS6也能运行MVC3,所以我们需要在这个周期之前的PostResolveRequestCache的事件点上去注册我们的规则,也许如果IIS6被微软废弃以后,就会将这个事件添加到真正的开始点MapRequestHandler上哦。

我们继续来看注册该事件的OnApplicationPostResolveRequestCache方法代码

public void PostResolveRequestCache(HttpContextBase context) { Match the incoming URL against the route table RouteData routeData = RouteCollection.GetRouteData(context); Do nothing if no route found if (routeData == null) { return; } If a route was found,get an IHttpHandler from the route's RouteHandler IRouteHandler routeHandler = routeData.RouteHandler; if (routeHandler == throw new InvalidOperationException( String.Format( CultureInfo.CurrentUICulture,SR.GetString(SR.UrlRoutingModule_NoRouteHandler))); } This is a special IRouteHandler that tells the routing module to stop processing routes and to let the fallback handler handle the request. if (routeHandler is StopRoutingHandler) { return; } RequestContext requestContext = new RequestContext(context,routeData); Dev10 766875 Adding RouteData to HttpContext context.Request.RequestContext = requestContext; IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); if (httpHandler == new InvalidOperationException( String.Format( CultureInfo.CurrentUICulture,SR.GetString(SR.UrlRoutingModule_NoHttpHandler),routeHandler.GetType())); } if (httpHandler is UrlAuthFailureHandler) { if (FormsAuthenticationModule.FormsAuthrequired) { UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current,255); line-height:1.5!important">this); return; } else { new HttpException(401,SR.GetString(SR.Assess_Denied_Description3)); } } Remap IIS7 to our handler context.RemapHandler(httpHandler); }

我已经加粗了4行重要的代码,第一行是通过传递HttpContext参数,从RouteCollection找到对应的静态属性RouteData( GetRouteData方法里会先判断真实文件是否存在,如果不存在才去找RouteData),第二行然后从RouteData的属性RouteHandler获取一个IRouteHandler的实例,第三行是从该实例里获取对应的IHttpHandler实例,第4行是调用HttpContext的RemapHandler方法重新map新的handler(这行代码的注释虽然说是remap IIS7,其实IIS6也是用了,只不过判断该方法里对IIS7集成模式多了一点特殊处理而已),然后可以通过HttpContext. RemapHandlerInstance属性来得到这个实例。

关于Route/RouteData/RouteCollection/IRouteHandler的作用主要就是定义URL匹配到指定的IHttpHandler,然后注册进去,具体实现我们稍后再讲,现在先看一下Http Pipeline里是如何找到这个IHttpHandler实例的,由于IIS6和IIS7集成模式是差不多的,前面的文章我们提到了都是最终调用到IHttpHandlerFactory的实例,然后从中获取IHttpHandler,所以我们这里只分析IIS6和IIS7经典模式的实现。

先来看BuildSteps里查找HttpHandler的方法MapHandlerExecutionStep的代码,只有几行代码,最重要的是:

context.Handler = _application.MapHttpHandler(
    context,request.RequestType,request.FilePathObject,request.PhysicalPathInternal,255); line-height:1.5!important">false /*useAppConfig*/); 

MapHttpHandler就是我们要查找Handler的方法了,来仔细看看代码

internal IHttpHandler MapHttpHandler(HttpContext context,String requestType,VirtualPath path,String pathTranslated,bool useAppConfig) { Don't use remap handler when HttpServerUtility.Execute called IHttpHandler handler = (context.ServerExecuteDepth == 0) ? context.RemapHandlerInstance : null; using (new ApplicationImpersonationContext()) { Use remap handler if possible if (handler != null){ return handler; } Map new handler HttpHandlerAction mapping = GetHandlerMapping(context,requestType,path,useAppConfig); If a page developer has removed the default mappings with <httpHandlers><clear> without replacing them then we need to give a more descriptive error than a null parameter exception. if (mapping == null) { PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_NOT_FOUND); PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_Failed); new HttpException(SR.GetString(SR.Http_handler_not_found_for_request_type,requestType)); } Get factory from the mapping IHttpHandlerFactory factory = GetFactory(mapping); Get factory from the mapping try { Check if it supports the more efficient GetHandler call that can avoid a VirtualPath object creation. IHttpHandlerFactory2 factory2 = factory as IHttpHandlerFactory2; if (factory2 != null) { handler = factory2.GetHandler(context,pathTranslated); } else { handler = factory.GetHandler(context,path.VirtualPathString,pathTranslated); } } catch (FileNotFoundException e) { if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated)) @H_186_301@404,255); line-height:1.5!important">null,e); else null); } catch (DirectoryNotFoundException e) { if (HttpRuntime.HasPathDiscoveryPermission(pathTranslated)) else catch (PathTooLongException e) { @H_186_301@414,255); line-height:1.5!important">null); } Remember for recycling if (_handlerRecycleList == null) _handlerRecycleList = new ArrayList(); _handlerRecycleList.Add(new HandlerWithFactory(handler,factory)); } return handler; }

代码可以看出,首先如果当前页面使用了HttpServerUtility.Execute进行页面跳转,就不使用我们通过路由设置的HttpHandler(也就是HttpContent.RemapHandlerInstance属性),如果没有跳转,就使用,并且优先级是第一的,只有当不设置任何基于Route的HttpHandler,才走剩余的匹配规则(也就是之前ASP.NET默认的按照扩展名类匹配的,这部分和我们关系不大就不做详细分析了)。

好了,知道了UrlRouteModuel的大概机制,我们再回头看看如何通过Route/RouteData/RouteCollection/IRouteHandler这几个类来实现动态注册Route规则的,先来看Route的代码

[TypeForwardedFrom(System.Web.Routing,Version=3.5.0.0,Culture=Neutral,PublicKeyToken=31bf3856ad364e35")]
class Route : RouteBase
{    
    public Route(string url,IRouteHandler routeHandler)
    {
        Url = url;
        RouteHandler = routeHandler;
    }
     
     url;
            Defaults = defaults; 
            Constraints = constraints; 
            RouteHandler = routeHandler;
        }

    省略部分代码
    override RouteData GetRouteData(HttpContextBase httpContext)
    {
         Parse incoming URL (we trim off the first two chars since they're always "~/")
        string requestPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;

        RouteValueDictionary values = _parsedRoute.Match(requestPath,Defaults);

        if (values == null)
        {
             If we got back a null value set,that means the URL did not match
            return null;
        }

        RouteData routeData = new RouteData(this,RouteHandler);

                 Validate the values
        if (!ProcessConstraints(httpContext,values,RouteDirection.IncomingRequest)) { 
            null; 
        }
 
         Copy the matched values
        foreach (var value in values) {
            routeData.Values.Add(value.Key,value.Value);
        } 

         Copy the DataTokens from the Route to the RouteData 
        if (DataTokens != null) { 
            var prop in DataTokens) {
                routeData.DataTokens[prop.Key] = prop.Value; 
            }
        }
        return routeData;
    }       
    }

Route代码提供了一系列的构造函数重载(我们这里只列出了两个),构造函数主要是传入URL和对应的IRouteHandler实例以及约束规则(比如正则等),然后提供了一个最重要的GetRouteData方法,用于将Route自身和IRouteHandler组装成RouteData,然后返回(中途也会验证相应的约束条件,比如是否符合某个正则表达式),RouteData类本身没有什么逻辑,只是暴露了Route和RouteHandler属性

我们再来看RouteCollection,该类保存了所有的Route规则(即URL和对应的IRouteHandler),通过静态属性RouteTable.Routes来获取RouteCollection实例,通过UrlRoutingModule里暴露的RouteCollection属性我们可以验证这一点:

public RouteCollection RouteCollection { get { if (_routeCollection == null) { _routeCollection = RouteTable.Routes; } return _routeCollection; } set { _routeCollection = value; } }

还有一个需要注意的,RouteHandler继承的IRouteHandler的代码

interface IRouteHandler { IHttpHandler GetHttpHandler(RequestContext requestContext); }

代码只提供了一个GetHttpHandler方法,所有实现这个接口的类需要实现这个方法,MVCHandler就是这么实现的(下一章节我们再细看)。

至此,我们应该有一个清晰的认识了,我们通过全局静态属性集合(RouteTable.Routes)去添加各种各样的Route(但应该在HttpModule初始化周期之前),然后通过UrlRoutingModule负责注册Route以及对应的IRouteHandler实例(IRouteHandler实例可以通过GetHttpHandler获取IHttpHandler),最终实现根据不同URL来接管不同的HttpHandler。

MVC正是利用HttpApplication创建的周期(Application_Start方法)来添加了我们所需要的Route规则,当然在添加规则的时候带上了MVCHandler这个重要的HttpHandler,

代码如下:

void Application_Start() { RegisterRoutes(RouteTable.Routes); } static void RegisterRoutes(RouteCollection routes) { routes.MapRoute( UrlParameter.Optional } ); }

MapRoute方法是一个扩展方法,通过该扩展方法注册Route是个不错的方法,下一章节,我们讲讲解MVC是如何注册自己的MVCRouteHandler实例以及如何实现MVCHandler的调用的。

猜你在找的正则表达式相关文章