zoukankan      html  css  js  c++  java
  • Asp.net Mvc 请求是如何到达 MvcHandler的——UrlRoutingModule、MvcRouteHandler分析,并造个轮子

    这个是转载自:http://www.cnblogs.com/keyindex/archive/2012/08/11/2634005.html(那个比较容易忘记,希望博主不要生气的)

    前言

      本文假定读者对 HttpModule 、HttpHandler和IIS的处理流程有一定的了解,如果为了解可以参考以下链接。文中大部分代码通过Reflector反编译  System.Web.dll 得到,.net 版本为4.0 

    IIS 5.0 和 6.0 的 ASP.NET 应用程序生命周期概述

    IIS 7.0 的 ASP.NET 应用程序生命周期概述

    HTTP 处理程序和 HTTP 模块概述

       Asp.net MVC 程序虽然开发的模式不同,但是其本质上还是 Asp.net。其利用了HttpModule 和 HttpHandler 做了扩展,可以参考博客园里的大牛——Artech 相关系列文章。

    本文主要关注UrlRoutingModule 、MvcRouteHandler 两个类的源代码,进而分析客户的请求是如何到达MvcHandler 的。

    Asp.net MVc 程序启动流程 需要关注的行为

    • 1、Application启动时先通过RouteTable把URL映射到Handler
    • 2、通过UrlRouting Module 这个HttpModule 拦截用户请求。

    我们知道,HttpModule 是注册在 Web.config 中的,可是当你打开Asp.net MVc 程序的Web .Config 时 却没有发现该配置节,原因是:"它已经默认的写在全局的中"。应此 你可以在 “$WindowsMicrosoft.NETFramework版本号ConfigWeb.config“ 中找到 " <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />”

    UrlRoutingModule 源码


    UrlRoutingModule 位于 System.web.dll 文件中,利用Reflector 可以查看到其源码:

    UrlRoutingModuel

    UrlHttpModule 实现了 IHttpModule 接口

      HTTP Module在应用程序发出请求时被调用的并进行特定的事件处理。 HTTP Module作为请求管道的一部分调用,它们能够访问请求过程中请求周期中的各种管线事件。 
    Http Module必须经过注册才能从请求管道接收通知。 注册 HTTP 模块的最常用方法是在应用程序的 Web.config 文件中进行注册。 在 IIS 7.0 中,统一的请求管道使您还可以通过其他方式注册模块,其中包括通过 IIS 管理器和 Appcmd.exe 命令行工具。
      当 ASP.NET 创建表示您的应用程序的 HttpApplication 类的实例时,将创建已注册的任何模块的实例。 在创建模块时,将调用它的 Init 方法,并且模块会自行初始化。在模块的 Init 方法中,可以注册各种应用程序事件的处理程序(如 BeginRequest 或 EndRequest)。

    可以看到 UrlHttpModule 在 init 方法中注册了PostResolveRequestCache 事件的处理程序。
    关于Asp.net的生命周期事件可以参考:
    http://msdn.microsoft.com/zh-cn/library/ms178472
    http://msdn.microsoft.com/zh-cn/library/bb470252
    http://msdn.microsoft.com/zh-cn/library/ms178473


    PostResolveRequestCache

        该事件在完成缓存解析并投递时触发。

      在UrlRoutingModule中它主要是进行上下文的初始化,同时根据传递过来的路由信息获取指定IHttpHandler (其实就是我们的MvcHandler类)
    最后通过 context.RemapHandler() 代码将HttpHandler 处理程序映射到 管线处理中。 
    在 PostResolveRequestCache 之前分别触发的事件有:
    引发 BeginRequest 事件。
    引发 AuthenticateRequest 事件。
    引发 PostAuthenticateRequest 事件。
    引发 AuthorizeRequest 事件。
    引发 PostAuthorizeRequest 事件。
    引发 ResolveRequestCache 事件。

    http://i.msdn.microsoft.com/dynimg/IC5405.png


    核心逻辑代码:

    复制代码
     1 //获取路由信息
     2 RouteData routeData = this.RouteCollection.GetRouteData(context);
     3 IRouteHandler routeHandler = routeData.RouteHandler;
     4 //构建请求上下文
     5 RequestContext requestContext = new RequestContext(context, routeData);
     6 context.Request.RequestContext = requestContext;
     7 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
     8 //将MvcHandler 实例 映射到管线中(通常我们是利用web.config 进行配置的,但是MvcHandler 没有默认无参构造函数,所以直接通过向其传递一个实例
     9 //进行映射)
    10 context.RemapHandler(httpHandler);
    复制代码

    如何获取到MvcRouteHandler?

      MvcRouteHandler 实现了 IRouteHandler接口。 上文的 IRouteHandler routeHandler=routeData.RouteHandler; 在Asp.net MVc 程序中实际上获取的是MvcRouteHandler实例。
    RouteData 类中包含了 IRouteHandler实例的引用,它通过 RouteData 的构造函数:

    public RouteData(RouteBase route, IRouteHandler routeHandler);

    或者 属性

    public IRouteHandler RouteHandler { get; set; }

    进行注入。

    我们再往回搜索,RouteData实例是通过 RouteCollection.GetRouteData(Context) 方法获取的。查看该方法的主要逻辑实现:

    复制代码
     using (this.GetReadLock())
            {
                foreach (RouteBase base2 in this)
                {
                    RouteData routeData = base2.GetRouteData(httpContext);
                    if (routeData != null)
                    {
                        return routeData;
                    }
                }
            }
    复制代码

    可以看到通过 RouteBase 类的 GetRouteData(HttpContext)获取了 RouteData实例,并且将第一个部位Null的值返回。 我们需要深入查看RouteBase GetRouteData方法。 RouteBase 是抽象类,其方法是在 Route上具体实现的。(这里又引出一个问题,程序是何时将 Route实例绑定到了 RouteBase上)。

    Route类 

    深入到Route 类中 发现其和 RouteData 一样, IRouteHandler 也是通过 构造参数 或 属性对 IRouteHandler 进行了注入。

    复制代码
    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
        RouteValueDictionary values = this._parsedRoute.Match(virtualPath, this.Defaults);
        if (values == null)
        {
            return null;
        }
        RouteData data = new RouteData(this, this.RouteHandler);
        if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest))
        {
            return null;
        }
        foreach (KeyValuePair<string, object> pair in values)
        {
            data.Values.Add(pair.Key, pair.Value);
        }
        if (this.DataTokens != null)
        {
            foreach (KeyValuePair<string, object> pair2 in this.DataTokens)
            {
                data.DataTokens[pair2.Key] = pair2.Value;
            }
        }
        return data;
    }
    复制代码

    关于Asp.net Mvc 中的 MapRoute() 方法

      在Asp.net MVc 程序Global 文件的RegisterRoutes 方法里,RouteCollection 类使用的是MapRoute 方法添加的路由,该方法是一个扩展方法。它位于System.Web.Mvc 的RouteCollectionExtensions类中。

    复制代码
     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;
            }
    复制代码

    查看上面的关键行:
    Route route = new Route(url, new MvcRouteHandler()) {
    Defaults = new RouteValueDictionary(defaults),
    Constraints = new RouteValueDictionary(constraints),
    DataTokens = new RouteValueDictionary()
    };

    可以清楚看到,当我们在Asp.net MVc 程序里使用MapRoute()添加路由时,会生成一个 Route 类的实例,并且该实例在生成时会被注入 MvcRouteHandler 实例。 最后被添加到 RouteCollection的集合里(Route 和 MvcRouteHandler 都是以多态形式存在于 RouteCollection中的),这样就建立了一个关系映射表,只有请求的上下文通过该上下文的验证,就可以返回对应MvcRouteHandler实例。

    • MvcRouteHandler 实现了 IRouteHandler接口
    • Route 继承了 RouteBase 抽象类。
    • RouteCollection 中维护着一个 RouteBase 集合。

    如何根据路由信息获取MvcHandler

      在Asp.net Mvc 程序 启动时,会触发 Appliction_start 方法,该方法调用了 RouteTable,生成一个全局RouteCollection(单件模式),并将其作为参数传递到RegisterRoutes 方法中。

    registerRoutes 方法里通过MapRoute 向 RouteCollection添加路由。 添加的路由是包含MvcRouteHandler实例的Route。


    当用户的请求到达IIS后,由于Asp.net MVC 注册了一个HttpModule(UrlRoutingModule) ,应此会触发对应的管线事件处理方法。


    1、根据上下文获取RouteData方法, 其内部 实现是:从RouteCollection中遍历RouteBase(实际为Route)实例,并调用RouteBase的GetRouteData() 方法,如果上下文与路由匹配
    该方法就会构造一个 RouteData实例,并将 this.IRouteHandler的实例 注入(在Application映射路由时,每个Route实例都包含一个MvcRouteHandler实例的引用)。


    2、从 RouteData中 获取IRouteHandler 实例,即 MvcRouteHandler


    3、构建上下文,创建RequestContext 该类仅包含 HttpContextBase 和 RouteData 。调用MvcHandler的构造函数需要传递该参数(可以知道请求的Controller、action名等信息),MvcHandler 实现了 IHttpHandler,注意它与MvcHttpHandler 是不相同的:

    MvcHandler . 此处理程序负责启动用于 MVC 应用程序的 ASP.NET 管道。 它从 MVC 控制器工厂接收 Controller 实例;此控制器处理请求的进一步处理。 注意,即使 MvcHandler 实现 IHttpHandler,也不能将其映射为处理程序(例如,.mvc 文件扩展名),因为该类不支持无参数构造函数。 (它唯一的构造函数需要一个 RequestContext 对象。)

    因此 在 UrlRoutenModule 中 是 通过 HttpContext.RemapHttp(HttpHandler) 。 直接将 一个实例 映射到处理程序上 。(不需要通过系统对其实例化)。

    MvcHttpHandler . 此处理程序用于在不通过路由模块的情况下帮助直接处理程序映射。 如果您希望一个文件的扩展名(如 .mvc)直接映射到一个 MVC 处理程序,这很有用。 在内部,MvcHttpHandler 执行 ASP.NET 路由通常执行的相同任务(通过 MvcRouteHandler 和 MvcHandler)。 但是,它将这些任务作为处理程序而不是模块来执行。 UrlRoutingModule 为所有请求启用时,通常不使用此处理程序。



    4、调用IRouteHandler的 GetHttpHandler 方法 获取 IHttpHandler实例。(即调用了MvcRouteHandler实例的GetHttpHandler 方法,生成了一个 MvcHandler 实例)


    5、向当前上下文注册 IHttpHandler 实例,进入 Controller 处理。

     经过以上步骤,我们就大致了解到了Asp.net Mvc程序启动后,用户的请求是如何到达HttpHandler的。


    RouteTable 类

    这个类很简单只包含一个静态的RouteCollection 属性,是一个单件类。

    RouteTable

    在Asp.net MVc 中application_start 方法里 调用了RouteTable来获取唯一的RouteCollection实例,

    所以在UrlRouteModuel中可以通过RouteTable.Routes获取所配置的路由集合。

    RouteCollection类

      是一个集合类,内部维护着一个RouteBase 以路由名作为Key的字典集合, 所以我们可以给路由命名。主要的属性和方法有:

    RouteData GetRouteData(HttpContextBase httpContext)

      该方法遍历集合内部的RouteBase实体,并返回第一个非Null的RouteData ,具体的RouteData实例,是由所遍历的RouteBase 通过调用 RouteBase.GetRouteData(HttpContext)方法获取的 。

    返回的RouteData 中包含一个 IRouteHandler 对象,该接口的只有一个方法,GetHttpHandler,用于获取IHttpHandler 对象。

    VirtualPathData GetVirtualPath(...)

      该方法具有多个重载,当您使用 ASP.NET 路由框架生成 URL 时,GetVirtualPath 方法将返回 VirtualPathData 类的一个实例。 VirtualPathData 类包含与所提供的上下文匹配路由的相关信息。

    Route MapPageRoute(...)

      该方法用于向路由集合中添加路由,提供此方法是为了方便编码, 它等效于调用 Add 方法。其内部实现的主要代码为:

    Route item = new Route(routeUrl, defaults, constraints, dataTokens, new PageRouteHandler(physicalFile, checkPhysicalUrlAccess));

    注册使用 PageRouteHandler 类创建的 Route 对象。

      在Asp.net Mvc 方法中,并没有利用该方法向集合中添加路由而是通过了了 MapRoute()方法 这是一个扩展方法,定义在了 RouteCollectionExtensions 类中。

    static Route MapRoute(this RouteCollection routes, ......)

      这是一个扩展方法,并且具有多个重载,目的是方便编码,用于向RouteCollection中 添加路由。其内部主要是 生成了一个 以 MvcRouteHandler 为 路由处理的Route 并将其加入到集合中。

    Route route = new Route(url, new MvcRouteHandler()) {
                    Defaults = new RouteValueDictionary(defaults),
                    Constraints = new RouteValueDictionary(constraints),
                    DataTokens = new RouteValueDictionary()
                };

    IRouteHandler 接口

      接口的定义很简单,只有一个 GetHttpHandler 方法用于返回 一个 IHttpHandler 对象。在 Route 对象,及 RouteData对象中 都包含该对象的引用。该接口定义了一种协议, 指定了 Route 即路由应该有哪一个处理对象进行处理。 它是 Route 到 Handler的 重要桥梁。

    RouteBase 类

       这是一个抽象类,RouteBase 类用于定义应用程序中的路由。 在定义路由时,通常使用 Route 类,Route 类是从 RouteBase 类派生的。 但是,如果要提供与 Route 类所提供的功能不同的功能,则可以创建一个从 RouteBase 派生的类,并实现所需的属性和方法。

    主要方法有:GetRouteData 在派生类中重写时,会返回有关请求的路由信息。
    GetVirtualPath 在派生类中重写时,会检查路由是否与指定值匹配,如果匹配,则生成一个 URL,然后检索有关该路由的信息。

    Route 类

      可以通过 Route 类指定 ASP.NET 应用程序中路由的处理方式。 可以为要映射的每个 URL 模式创建一个 Route 对象,该类可处理与该模式相对应的请求。 当应用程序收到请求时,ASP.NET 路由会循环访问 Routes 集合中的路由,以查找与该 URL 模式匹配的第一个路由。

      可将 Url 属性设置为 URL 模式。 该 URL 模式包含某些分段,这些分段位于 HTTP 请求中应用程序名称之后。 例如,在 URL http://www.contoso.com/products/show/beverages 中,该模式应用于 products/show/beverages。 包含三个分段的模式(如 {controller}/{action}/{id})与 URL http://www.contoso.com/products/show/beverages 匹配。 每个分段由 / 字符分隔。 如果分段位于大括号({ 和 })内,则表明该分段是一个 URL 参数。 ASP.NET 路由会检索请求中的值并将其分配给 URL 参数。 在上面的示例中,URL 参数 action 被赋予值 show。 如果该分段不在大括号内,则该值被视为文本值。将 Defaults 属性设置为 RouteValueDictionary 对象,该对象包含当 URL 中缺少某个参数时所使用的值,或者包含用于设置 URL 中未参数化的其他值的值。 将 Constraints 属性设置为 RouteValueDictionary 对象,该对象包含的值为正则表达式或 IRouteConstraint 对象。 这些值用于确定参数值是否有效。

      

    复制代码
    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
        RouteValueDictionary values = this._parsedRoute.Match(virtualPath, this.Defaults);
        if (values == null)
        {
            return null;
        }
        RouteData data = new RouteData(this, this.RouteHandler);
        if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest))
        {
            return null;
        }
        foreach (KeyValuePair<string, object> pair in values)
        {
            data.Values.Add(pair.Key, pair.Value);
        }
        if (this.DataTokens != null)
        {
            foreach (KeyValuePair<string, object> pair2 in this.DataTokens)
            {
                data.DataTokens[pair2.Key] = pair2.Value;
            }
        }
        return data;
    }
    复制代码

    该方法重写了父类的方法,返回一个RouteData 作。方法一开始 首先通过 ParsedRoute 类的 Match 方法进行路由匹配,匹配成功后则生成一个RouteData对象实例。

    ParsedRoute 类
    该类是一个内部类,用于匹配、绑定URL,大家可以参考:

    复制代码
     public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        BoundUrl url = this._parsedRoute.Bind(requestContext.RouteData.Values, values, this.Defaults, this.Constraints);
        if (url == null)
        {
            return null;
        }
        if (!this.ProcessConstraints(requestContext.HttpContext, url.Values, RouteDirection.UrlGeneration))
        {
            return null;
        }
        VirtualPathData data = new VirtualPathData(this, url.Url);
        if (this.DataTokens != null)
        {
            foreach (KeyValuePair<string, object> pair in this.DataTokens)
            {
                data.DataTokens[pair.Key] = pair.Value;
            }
        }
        return data;
    }
    复制代码

      该方法返回与路由相关联的URL信息。其内部是通过ParsedRoute 类的Bind 把RouteData 绑定到一个BoundUrl 对象中。 如果有约束在则进行验证,最后返回一个 VirtualPathData 对象。

    复制代码
    protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        object obj2;
        IRouteConstraint constraint2 = constraint as IRouteConstraint;
        if (constraint2 != null)
        {
            return constraint2.Match(httpContext, this, parameterName, values, routeDirection);
        }
        string str = constraint as string;
        if (str == null)
        {
            throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_ValidationMustBeStringOrCustomConstraint"), new object[] { parameterName, this.Url }));
        }
        values.TryGetValue(parameterName, out obj2);
        string input = Convert.ToString(obj2, CultureInfo.InvariantCulture);
        string pattern = "^(" + str + ")$";
        return Regex.IsMatch(input, pattern, RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase);
    }
    复制代码

      这是一个路由约束的处理方法,可以看到如果传递的 object constraint 是一个 IRouteConstraint 类型 则直接调用 其Match 方法。

    如果正则表达式字符串,利用正则表达式进行验证,否则抛出异常。 


    造一个简陋的轮子

      不要再造轮子是软件设计的一个准则,但是在学习研究时,造一个简单的轮子能更好的帮我们了解其原理。

    创建一个HttpHandler 类,让Asp.net程序通过路由,运行我们指定的HttpHandler,同时在Handler中能够获取路由信息。

    1、创建一个类库项目,在项目里创建一个WheelHandler类并实现IHttpHandler接口,这个类只有一个构造函数构造函数需要传递一个 RequestContext

    复制代码
     public class WheelHandler : IHttpHandler
        {
            public WheelHandler(RequestContext requestContext)
            {
    
                this.RequestContext = requestContext;
            }
    
            #region IHttpHandler Members
    
            public bool IsReusable
            {
                get { return true; }
            }
    
            public void ProcessRequest(HttpContext context)
            {
                context.Response.Write(String.Format("this is a Wheel for {0}Controller and {1}action "
                    , this.RequestContext.RouteData.Values["Controller"]
                    , this.RequestContext.RouteData.Values["Action"]));
                context.Response.End();
            }
    
            #endregion
    
            public RequestContext RequestContext { get; private set; }
        }
    复制代码

     WheelHandler没有默认的无参构造函数,所以不能直接在Web.config 中注册。ProcessRequest对象很简单,就是输出传入的路由信息。

    我们需要定义一个IRouteHandler对象,当路由被捕获时,返回一个WheelHandler,然后将其映射到Http处理中。

    2.定义WheelRouteHandler

    复制代码
     public class WheelRouteHandler : IRouteHandler
        {
    
    
            public IHttpHandler GetHttpHandler(RequestContext requestContext)
            {
                return new WheelHandler(requestContext);
            }
    
            IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
            {
                return this.GetHttpHandler(requestContext);
            }
        }
    复制代码

    3、映射路由和WheelRouteHandler。

      我们可以自定义一个派生自RouteBase 的类,进行特定的路由处理,但是本例是一个简单的"轮子",因此继续使用Route类,只需要在生成Route时 向其注入 WheelRouteHandler即可。

    因此我们需要修改添加路由的方式,这里模仿MVc 利用扩展方法。当然你也可以不用扩展方法,在添加路由是直接使用Add方法,记得注入WheelRouteHandler便行。

    复制代码
    public static Route MapWheelRoute(this RouteCollection routes, string name, string url, object defaults)
            {
                return MapWheelRoute(routes, name, url, defaults, null, null);
            }
    
            public static Route MapWheelRoute(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 与 WheelRouteHandler的映射关系
                Route route = new Route(url, new WheelRouteHandler())
                {
                    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;
            }
    复制代码

    MapWheelRoute 方法里 调用了Route构造函数,并传入一个WheelRouteHandler对象。

    4、创建HttpModule 对象

      自定义的HttpModule的责任是,构建上下文,创建HttpHandler对象并将它映射到Http处理程序里去。

    复制代码
     public void Init(HttpApplication context)
            {
                context.PostResolveRequestCache += new EventHandler(context_PostResolveRequestCache);
            }
    
            void context_PostResolveRequestCache(object sender, EventArgs e)
            {
                HttpContextBase context = new HttpContextWrapper(((HttpApplication)sender).Context);
                this.PostResolveRequestCache(context);
    
            }
    
            private void PostResolveRequestCache(HttpContextBase context)
            {
                RouteData routeData = RouteTable.Routes.GetRouteData(context);
    
                if (routeData == null)
                {
                    throw new InvalidOperationException();
                }
    
                IRouteHandler routeHandler = routeData.RouteHandler;
                if (routeHandler == null)
                {
                    throw new InvalidOperationException();
                }
    
                RequestContext requestContext = new RequestContext(context, routeData);
                IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
                if (httpHandler == null)
                {
                    throw new InvalidOperationException("无法创建对应的HttpHandler对象");
                }
                context.RemapHandler(httpHandler);
    
            }
    复制代码

      这里我们也是模仿Mvc 捕获的是PostResolveRequestCache事件进行处理,处理时首先将 包装HttpContext对象为 HttpContextBase对象。然后通过GetRouteData 获取RouteData对象,它包含有IRouteHandler对象。获取到IRouteHandler对象后,需要构造 一个RequestContext 对象(该对象很简单就是包含上下文和路由信息)因为 创建WheelHandler 需要该对象。

    创建好IHttpHandler对象后,利用RemapHandler方法将其映射为处理程序。

    5、注册路由 和Module

      新建一个空的Asp.net 项目,移除里面所有的Aspx文件,事实上只要保留Global和 Web.Config文件即可。在Global 里注册路由:

    路由注册

      完成最后我们还需要组成自定义的HttpModule

    在Web.Config 中添加如下配置:

     <system.webServer>
        <modules runAllManagedModulesForAllRequests="true">
          <add name="WheelRouting" type="WheelRouting.WheelRoutingModule,WheelRouting"/>
        </modules>
    
      </system.webServer>

    关于 HttpModule的介绍 可以参考:演练:创建和注册自定义 HTTP 模块

    下面是运行结果:

    Controller默认值为 Home  Action 默认值为 index

    源码:点击下载

  • 相关阅读:
    使用NPOI导入导出标准Excel
    winform ListView应用之分组、重绘图标、网格线
    在网页中显示CHM
    动态表单(javascript实现)
    批量上传文件时,js验证文件名不能相同
    IE开发人员工具无法使用
    卸载方法 gnu grub version 0.97
    VS.Net 2003/VC6.0常用快捷键集合
    SQL SERVER 与ACCESS、EXCEL的数据转换
    通用获取父节点/子节点/子节点下所有节点ID的存储过程
  • 原文地址:https://www.cnblogs.com/wucaifang/p/3627178.html
Copyright © 2011-2022 走看看