.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 }
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 }
大致分析下这段源码,我们可以得出下列比较重要的结论
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 转载请注明出处。

浙公网安备 33010602011771号