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 = Indexarameter.Optional }

但是在匹配这个之前,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)) 404,255); line-height:1.5!important">null,e); else null); } catch (DirectoryNotFoundException e) { if (HttpRuntime.HasPathdiscoveryPermission(pathTranslated)) else catch (PathTooLongException e) { 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的调用的。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


正则替换html代码中img标签的src值在开发富文本信息在移动端展示的项目中,难免会遇到后台返回的标签文本信息中img标签src属性按照相对或者绝对路径返回的形式,类似:<img src="qinhancity/v1.0.0/ima
正则表达式
AWK是一种处理文本文件的语言,是一个强大的文件分析工具。它是专门为文本处理设计的编程语言,也是行处理软件,通常用于扫描,过滤,统计汇总等工作,数据可以来自标准输入也可以是管道或文件。当读到第一行时,匹配条件,然后执行指定动作,在接着读取第二行数据处理,不会默认输出。如果没有定义匹配条件,则是默认匹配所有数据行,awk隐含循环,条件匹配多少次,动作就会执行多少次。逐行读取文本,默认以空格或tab键为分割符进行分割,将分割所得的各个字段,保存到内建变量中,并按模式或或条件执行编辑命令。与sed工作原理相比:s
正则表达式是特殊的字符序列,利用事先定义好的特定字符以及他们的组合组成了一个规则,然后检查一个字符串是否与这种规则匹配来实现对字符的过滤或匹配。我们刚才在学习正则表达式的时候,我们表示数字,字母下划线的时候是用w表示的,为什么我们在书写的时候用的是w?我们可以发现我们分割空格的话,并没有达到我们预期的效果,这里我们可以使用正则表达式的方式进行分割。我们可以发现,我们和上面得到的结果不一致,既然出错了,肯定是我们的使用方式不对。看到这里我们就能感受到正则表达式的作用了,正则表达式是字符串处理的有力工具。
Python界一名小学生,热心分享编程学习。
收集整理每周优质开发者内容,包括、、等方面。每周五定期发布,同步更新到和。欢迎大家投稿,,推荐或者自荐开源项目/资源/工具/文章~
本文涉及Shell函数,Shell中的echo、printf、test命令等。
常用正则表达,包括: 密码、 手机号、 身份证、 邮箱、 中文、 车牌号、 微信号、 日期 YYYY-MM-DD hh:mm:ss、 日期 YYY-MM-DD、 十六进制颜色、 邮政编号、 用户名、 QQ号
一、python【re】的用法1、re.match函数·单一匹配-推荐指数【★★】2、re.search函数·单一匹配-推荐指数【★★★★★】3、re.findall函数·多项匹配-推荐指数【★★★★★】4、re.finditer函数·多项匹配-推荐指数【★★★★】5、re.sub函数·替换函数-推荐指数【★★★★】二、正则表达式示例·总有一款适合你1、正则表达式匹配HTML指定id/class的标签2、正则表达式匹配HTML中所有a标签中的各类属性值3、获取标签的文本值
1.借助词法分析工具Flex或Lex完成(参考网络资源)2.输入:高级语言源代码(如helloworld.c)3.输出:以二元组表示的单词符号序列。通过设计、编制、调试一个具体的词法分析程序,加深对词法分析原理的理解,并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。由于各种不同的高级程序语言中单词总体结构大致相同,基本上都可用一组正则表达式描述,所以构造这样的自动生成系统:只要给出某高级语言各类单词词法结构的一组正则表达式以及识别各类单词时词法分析程序应采取的语义动作,该系统
正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。例如:我们在写登录注册功能的时候使用的表单验证(对用户名、密码进行一些字符或长度进行限制) ===> (`匹配`) - 正则表达式还常用于过滤掉页面内容的一些敏感词汇。例如:我们平常在打游戏时候的口吐芬芳被换成了***:full_moon_with_face: ===> (`替换`) - 正则表达式从字符串中获取我们想要的特定部分。例如:我们在逛淘宝的时候在搜索框中搜索内容,会弹出很多与搜索相关的提示内容 ===> (`提取`) etc..
通过上面几个简单的示例,可以了解到常见的基础正则表达式的元字符主要包括以下几个^ 匹配输入字符串的开始位置。除非在方括号表达式中使用,表示不包含该字符集合。要匹配”^”字符本身,请使用"^"$ 匹配输入字符串的结尾位置。如果设置了RegExp对象的 Multiline属性,则"$”也匹配'n'或'r’,。要匹配”$"字符本身,请使用”$". 匹配除"rn"之外的任何单个字符 反斜杠,又叫转义字符,去除其后紧跟的元字符或通配符的特殊意义* 匹配前面的子表达式零次或多次。...
给出补充后描述 C 语言子集单词符号的正则文法,设计并实现其词法分析程序。
正则表达式(Regular Expression),又称规则表达式,它不是某个编程语言所特有的,是计算机科学的一个概念,通常被用来检索和替换符合某些规则的文本。
Python Re 正则表达式 数据匹配提取 基本使用
正则表达式:是用来描述字符串内容格式,使用它通常用于匹配一个字符串的内容是否符合格式要求
python的学习还是要多以练习为主,想要练习python的同学,推荐可以去牛客网看看,他们现在的IT题库内容很丰富,属于国内做的很好的了,而且是课程+刷题+面经+求职+讨论区分享,一站式求职学习网站,最最最重要的里面的资源全部免费!