.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(); 16FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 17 RouteConfig.RegisterRoutes(RouteTable.Routes); 18 BundleConfig.RegisterBundles(BundleTable.Bundles); 19 } 20 } 21 }
在這個方法中就註冊了我們的路由配置,相關的註冊文件都是在在App_Data目錄下
路由配置文件RouteConfig.cs,內容如下
1 usingSystem; 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路由機制與擴展