• .Net MVC5路由机制与扩展


    新建一个MVC项目启动后,首先访问的地址是http://localhost:xxx/Home/Index,这时候我们也明白因为在程序中有个叫做Home的控制器,并且在这个控制器下面有个叫做Index的方法,基于这种对应的关系,才有了这种结果,那么这种对应关系是如何产生,如何工作的了?

    在我们网站在第一次启动的时候,会去访问一个全局的配置文件Global.asax下面的Application_Start()方法,代码如下

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Web;
     5 using System.Web.Mvc;
     6 using System.Web.Optimization;
     7 using System.Web.Routing;
     8 
     9 namespace Test
    10 {
    11     public class MvcApplication : System.Web.HttpApplication
    12     {
    13         protected void Application_Start()
    14         {
    15             AreaRegistration.RegisterAllAreas();
    16             FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    17             RouteConfig.RegisterRoutes(RouteTable.Routes);
    18             BundleConfig.RegisterBundles(BundleTable.Bundles);
    19         }
    20     }
    21 }

    在这个方法中就注册了我们的路由配置,相关的注册文件都是在在App_Start目录下

     路由配置文件RouteConfig.cs,内容如下

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Web;
     5 using System.Web.Mvc;
     6 using System.Web.Routing;
     7 
     8 namespace Test
     9 {
    10     public class RouteConfig
    11     {
    12         public static void RegisterRoutes(RouteCollection routes)
    13         {
    14             routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    15 
    16             routes.MapRoute(
    17                 name: "Default",
    18                 url: "{controller}/{action}/{id}",
    19                 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    20             );
    21         }
    22     }
    23 }

    首先是忽略路由,满足匹配会被忽略,如果没有特殊需求,这个地方就不需要更改,

    name表示路由的名称,同一个路由集合中,路由名称必须唯一,不能够有重名,

    url就是正则表达式,用于http://localhost:xxx/后路径地址的匹配,

    defaults表示缺省路由,也就是默认的路由.这是一个路由规则增删的方法,并且默认的路由为home/index,

    还有一个比较常用的参数是constraints,用于条件约束,简单实例如下:

     1     public class RouteConfig
     2     {
     3         public static void RegisterRoutes(RouteCollection routes)
     4         {
     5             routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
     6 
     7             //假若Home下有Test(int year,int month,int day)方法
     8             //home/Test_2017_06_05可以访问至该方法
     9             //home/Test_2017_6_05是无法访问
    10             routes.MapRoute(
    11               name: "Constraints",
    12               url: "{controller}/{action}_{year}_{month}_{day}",
    13               defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
    14               //条件参数,约束后面参数(此处简单正则:4位数字,2位数字,2位数字)
    15               constraints: new { year = @"^d{4}", month = @"^d{2}", day = @"^d{2}" });
    16 
    17             routes.MapRoute(
    18                 name: "Default",
    19                 url: "{controller}/{action}/{id}",
    20                 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
    21         }
    22     }

    路由浅析

    那么路由究竟是如何工作的,下面是2个与路由工作十分紧密的2个方法的源码,任何http请求进入后,都会先被它们处理

     1 public virtual void PostResolveRequestCache(HttpContextBase context)
     2     {
     3         RouteData routeData = RouteCollection.GetRouteData(context);
     4         if (routeData != null)
     5         {
     6             IRouteHandler routeHandler = routeData.RouteHandler;
     7             if (routeHandler == null)
     8             {
     9                 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler")));
    10             }
    11             if (!(routeHandler is StopRoutingHandler))
    12             {
    13                 RequestContext requestContext = new RequestContext(context, routeData);
    14                 context.Request.RequestContext = requestContext;
    15                 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
    16                 if (httpHandler == null)
    17                 {
    18                     throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[1]
    19                     {
    20                         routeHandler.GetType()
    21                     }));
    22                 }
    23                 if (httpHandler is UrlAuthFailureHandler)
    24                 {
    25                     if (FormsAuthenticationModule.FormsAuthRequired)
    26                     {
    27                         UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
    28                         return;
    29                     }
    30                     throw new HttpException(401, SR.GetString("Assess_Denied_Description3"));
    31                 }
    32                 context.RemapHandler(httpHandler);
    33             }
    34         }
    35     }
    PostResolveRequestCache
     1 public RouteData GetRouteData(HttpContextBase httpContext)
     2 {
     3     if (httpContext == null)
     4     {
     5         throw new ArgumentNullException("httpContext");
     6     }
     7     if (httpContext.Request == null)
     8     {
     9         throw new ArgumentException(SR.GetString("RouteTable_ContextMissingRequest"), "httpContext");
    10     }
    11     if (base.Count == 0)
    12     {
    13         return null;
    14     }
    15     bool flag = false;
    16     bool flag2 = false;
    17     if (!RouteExistingFiles)
    18     {
    19         flag = IsRouteToExistingFile(httpContext);
    20         flag2 = true;
    21         if (flag)
    22         {
    23             return null;
    24         }
    25     }
    26     using (GetReadLock())
    27     {
    28         foreach (RouteBase item in this)
    29         {
    30             RouteData routeData = item.GetRouteData(httpContext);
    31             if (routeData != null)
    32             {
    33                 if (!item.RouteExistingFiles)
    34                 {
    35                     if (!flag2)
    36                     {
    37                         flag = IsRouteToExistingFile(httpContext);
    38                         flag2 = true;
    39                     }
    40                     if (flag)
    41                     {
    42                         return null;
    43                     }
    44                 }
    45                 return routeData;
    46             }
    47         }
    48     }
    49     return null;
    50 }
    GetRouteData

    大致分析下这段源码,我们可以得出下列比较重要的结论

    1在路由匹配之前,先一步检查了物理文件的存在,所以mvc和webform可以共存,如果存在满足路径条件的aspx文件,它会优先被访问

    2路由是从上到下逐个匹配的,所以排列的顺序对于路由匹配是非常重要的

     路由扩展

    在上述基础下,我们对路由有了大概的了解,对此我们可以简单扩展一下

    路由并不是只能够在路径规则上做文章,我们也可以继承路由的基类RouteBase自定义一些路由,然后把自定义的路由放入注册路由集合中,调准好顺序,实现我们想要的结果.因为能够得到HttpContext这个用户请求的上下文,我们可以完成很多扩展,例如检查ip,浏览器类型,参数等等,下面我们就简单的实现不同浏览器访问到不同路径的功能,如下:

     1     public class RouteConfig
     2     {
     3         public static void RegisterRoutes(RouteCollection routes)
     4         {
     5             routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
     6 
     7             //注意顺序
     8             routes.Add("Agent",new Agent());
     9 
    10             routes.MapRoute(
    11                 name: "Default",
    12                 url: "{controller}/{action}/{id}",
    13                 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
    14         }
    15 
    16       
    17     }
    18     public class Agent : RouteBase
    19     {
    20         public override RouteData GetRouteData(HttpContextBase httpContext)
    21         {
    22             if (httpContext.Request.UserAgent.Contains("Chrome/63.0.3239.132"))
    23             {
    24                 var data = new RouteData(this, new MvcRouteHandler());
    25                 data.Values.Add("controller", "Home");
    26                 data.Values.Add("action", "About");
    27                 return data;
    28             }
    29             else
    30             {
    31                 return null;
    32             }
    33         }
    34 
    35         public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    36         {
    37             return null;
    38         }
    39     }

    一定得注意路由排列的顺序,如果将上面的Agent路由放在Default路由后面,那么就直接会访问到Home/Index,而不会去判断.

    我们也可以通过自定义继承了RouteBase的Route类,来完成特殊路径的扩展,如下

     1   public class RouteConfig
     2     {
     3         public static void RegisterRoutes(RouteCollection routes)
     4         {
     5             routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
     6 
     7             routes.Add(new Route("Test",new TestRouteHandler()));
     8 
     9             routes.MapRoute(
    10                 name: "Default",
    11                 url: "{controller}/{action}/{id}",
    12                 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
    13         }
    14     }
    15 
    16     public class TestRouteHandler : IRouteHandler
    17     {
    18         public IHttpHandler GetHttpHandler(RequestContext requestContext)
    19         {
    20             return new TestHandler();
    21         }
    22     }
    23     public class TestHandler : IHttpHandler
    24     {
    25         public bool IsReusable => true;
    26 
    27         public void ProcessRequest(HttpContext context)
    28         {
    29             context.Response.Write("Test");
    30         }
    31     }

    效果如下:

    出自:博客园-半路独行

     原文地址:https://www.cnblogs.com/banluduxing/p/9185159.html

     本文出自于http://www.cnblogs.com/banluduxing 转载请注明出处。

  • 相关阅读:
    新闻项目——项目准备——代码抽取
    新闻项目——立项准备——框架搭建
    测试总结
    OSI——网络层
    软件测试的方法
    flask中cookie和session介绍
    flask数据库基本操作
    csrf原理和flask的处理流程
    xadmin快速搭建后台管理系统
    orm的聚合索引
  • 原文地址:https://www.cnblogs.com/banluduxing/p/9185159.html
Copyright © 2020-2023  润新知