1. 程式人生 > >.Net MVC5路由機制與擴展

.Net MVC5路由機制與擴展

home webform LV 定義 write ner 直接 closed alt

新建一個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_Data目錄下

路由配置文件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 轉載請註明出處。

.Net MVC5路由機制與擴展