Asp.Net MVC4 系列-- 進階篇之路由(1)
建立一個路由
開啟 RouteConfig.cs ,發現已經建立了一個預設路由 :
routes.MapRoute(
name:"Default",
url:"{controller}/{action}/{id}"
// defaults: new { controller ="Home", action = "Index", id = UrlParameter.Optional }
);
為了說明路由的url匹配過程,暫時comment掉default引數。
開啟Global.cs ,可以看到路由配置檔案已經註冊:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
關於路由工作方式
Asp.net MVC Framework 的路由部分,是插入在http pipeline中的,當接受到http請求,會尋找註冊的路由表(在ApplicationStart時候註冊,就是應用啟動時候),找到路由規則,獲取每個路由規則的pattern,試圖匹配當前請求合適的那個route,匹配成功,則解析出controller和action,從controllerfactory找到相應的controller,把請求傳遞給action,如果請求中傳參,路由還會解析出引數,給action。
下面是幾種url匹配的例子:
Controller =Admin,Action=Index |
|
Controller=Index,Action=Admin |
|
Controller=Apples,Action=Oranges |
|
匹配失敗,Segment太少 |
|
http://mysite/Admin/Index/Soccer |
匹配失敗,Segment太多 |
路由會呼叫route handler來完成路由過程,預設的,mvc應用會使用MVCRouteHandler.手動新增一個Route,就可以體現出來:
routes.Add("MyRoute",newRoute("{controller}/{action}", new MvcRouteHandler()));
指定預設(default)
剛才說明url匹配時候,拿掉了default引數,這時我們一起看看default引數的作用。
routes.MapRoute(
name:"Default",
url:"{controller}/{action}/{id}",
defaults: new { controller = "Home", action ="Index", id = UrlParameter.Optional }
);
可以看到最後一個引數,指定了一個預設的controller和action。
Mydomain.com |
Controller = Home ,action=Index |
Mydomain.com/Customer |
Controller=Customer ,action=Index |
Mydomain.com/Customer/List |
Controller=Customer, action=List |
Mydomain.com/Customer/List/All |
匹配失敗,segment太多 |
定值Segment
場景1,所有請求中第一個Segment為”public”的,需要統一處理,因此定義一個路由:
routes.MapRoute(name: "PublicReqRoute", url:"Public/{controller}/{action}",
defaults: new {controller = "PublicHome", action ="Index"});
示例url:http://mysite/Public
匹配結果:controller = PublicHome,action=Index
場景2,請求中以public開始的,需要統一處理,定義路由:
routes.MapRoute(name: "PublicReqRoute", url:"Public{controller}/{action}",
defaults: new {controller = "PublicHome", action ="Index"});
示例url: Http:/mysite/PublicX/
匹配結果:controller=X,action=Index
場景3:有個controller或action不工作,需要改個bug,把所有原來指向這個controller的請求暫時路由到首頁:
routes.MapRoute("myshop","Shop/OldAction",
new { controller = "Home", action ="Index" });
注意:路由是按著新增順序依次解析的,因此把最特殊的那個路由放在前面,避免先fall 到相對generall的那個。
獲取引數
對於Route:
routes.MapRoute(
name:"Default",
url:"{controller}/{action}/{id}",
defaults: new { controller = "Home", action ="Index", id = UrlParameter.Optional }
);
請求:http://mysite/Home/Index/15
Action 中使用RouteData.Values獲取傳入的id:
public ActionResult CustomVariable() {
ViewBag.Controller = "Home";
ViewBag.Action = "CustomVariable";
ViewBag.CustomVariable = RouteData.Values["id"];
return View();
}
使用mvcframework Route System自動傳參機制
除了使用RouteData.Values取出傳入的引數,可以更簡單的定義個引數在action,但是引數名要和route定義的相同(id)
Public ActionResult CustomVariable(string id) {
ViewBag.Controller = "Home";
ViewBag.Action = "CustomVariable";
ViewBag.CustomVariable = id;
return View();
}
對於url:http://mysite/Home/Index/15,id就會自動被賦值為15傳入action
定義可選引數
依然對於url:
routes.MapRoute(
name:"Default",
url:"{controller}/{action}/{id}",
defaults: new { controller = "Home", action ="Index", id = UrlParameter.Optional }
);
Id=UrlParameter.Optional,此時id就是可選引數
Mydomain.com |
Controller=home ,action=Index |
Mydomain.com/customer |
Controller=customer, action=Index |
Mydomain.com/customer/List |
Controller=customer, action=List |
Mydomain.com/customer/List/All |
Controller=customer , action=List, Id=All |
Mydomain.com/customer/List/All/Delete |
url 匹配失敗 |
如果沒有傳參,action提供了id引數,那麼id此時就為null;
public ActionResultCustomVariable(string id) {
ViewBag.Controller = "Home";
ViewBag.Action = "CustomVariable";
ViewBag.CustomVariable = id == null ? "<novalue>" : id;
return View();
}
作為另一個選擇,可以指定一個預設引數,如果沒url沒傳值,預設引數值就會被使用。
Public ActionResultCustomVariable(string id = "DefaultId") {
ViewBag.Controller = "Home";
ViewBag.Action = "CustomVariable";
ViewBag.CustomVariable = id;
return View();
}
使用{*catchall}捕捉超出數量的segment
例如,對於這條route:
routes.MapRoute("MyRoute","{controller}/{action} /{*catchall}",
new { controller = "Home", action ="Index",
id = UrlParameter.Optional });
由於使用了{*catchall},對於url:
http://mysite/Home/Index/All/More/More/More
此時,controller=Home,Action=Index, catchall=”All/More/More/More”
這樣,就把解析剩下segment的工作交給了自己處理
解決在不同namespace的同名controller
例如現在有兩個controller,在不同的名稱空間:
namespace UrlsAndRoutes.Controllers {
public class HomeController : Controller {
public ActionResult Index() {
ViewBag.Controller = "Additional Controllers - Home";
ViewBag.Action = "Index";
return View("ActionName");
}
}
}
和
namespace UrlsAndRoutes.AdditionalControllers {
public classHomeController : Controller {
public ActionResultIndex() {
ViewBag.Controller = "Additional Controllers -Home";
ViewBag.Action = "Index";
return View("ActionName");
}
}
}
對於這種情況,可能希望路由先在一個指定的名稱空間裡找,找到了返回:
routes.MapRoute("MyRoute","{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action ="Index",
id = UrlParameter.Optional
},
new[] { "URLsAndRoutes.AdditionalControllers" });
關鍵在於最後的那個名稱空間引數,mvc framework會優先找 “URLsAndRoutes.AdditionalControllers”裡面的controller,如果沒找到,會搜尋其餘的名稱空間。
注意,這個new []{}數字裡的引數是平行的,也就是說,如果mvc framework在這些名稱空間裡找到多個同名controller,不會找到第一個就返回,依然會丟擲異常,例如:
new[] { "URLsAndRoutes.AdditionalControllers","UrlsAndRoutes.Controllers"});
對於url:
mvc framework會在指定的名稱空間陣列裡找,由於找到了多個homecontroller,因此丟擲異常。
如果希望這樣:指定多個名稱空間,找到了第一個就返回,怎麼做?
可以配置多條route:
routes.MapRoute("AddContollerRoute","Home/{action}/{id}/{*catchall}",
new { controller = "Home", action ="Index",
id = UrlParameter.Optional },
new[] { "URLsAndRoutes.AdditionalControllers" });
routes.MapRoute("MyRoute","{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action ="Index",
id = UrlParameter.Optional },
new[] { "URLsAndRoutes.Controllers" });
mvc framework就會按著新增的順序依次查詢匹配的controller和action,找到了把解析好的引數(如果有)傳遞給action就返回了。
只允許mvc framework在指定的 namespace裡找,如何做?
Route myRoute=routes.MapRoute("AddContollerRoute",
"Home/{action}/{id}/{*catchall}",
new { controller = "Home", action ="Index",
id = UrlParameter.Optional },
new[] { "URLsAndRoutes.AdditionalControllers" });
myRoute.DataTokens["UseNamespaceFallback"] = false;
由於把DataTokens[“UseNamespaceFallback”] 設為false,因此mvcframework在指定的名稱空間:URLsAndRoutes.AdditionalControllers"裡面找完了,就不去其他地方找了。
給路由加限制
正則限制
可以加正則限制在controller和action:
routes.MapRoute("MyRoute","{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action ="Index", id = UrlParameter.Optional },
new { controller = "^H.*"},
new[] { "URLsAndRoutes.Controllers"});
由於給controller加上了正規表示式:”^H.*”的限制,因此對於url匹配了urlpattern,解析出controller,如果不是以H開頭的,也會被過濾掉。
類似的,也可以使用給action加正則限制:
new { controller = "^H.*", action ="^Index$|^About$"}
這樣,就限制了action必須是Index和About。
使用Http Method限制
可以限制請求是Get ,Post亦或是Put ,Delete等型別的。
new { controller = "^H.*", action ="Index|About",
httpMethod = new HttpMethodConstraint("GET") } ,
這樣,就限制了請求必須是Get方式的。
注意,給route加httpmethod限制,和給controller還有action加httpmethod區別在於,route在httppipeline很早的位置就被處理了,而到controller和action的時候,已經是httppipeline很晚的時候了,controller和action已經被解析了,引數也已經被解析了(如果有)。
自定義限制
Mvc 提供了IRouteConstranit 介面,可以自己定義限制,通過實現這個介面:
public class UserAgentConstraint : IRouteConstraint {
private string requiredUserAgent;
public UserAgentConstraint(string agentParam) {
requiredUserAgent = agentParam;
}
Public boolMatch(HttpContextBase httpContext, Routeroute, string parameterName,
RouteValueDictionary values,RouteDirection routeDirection) {
return httpContext.Request.UserAgent != null &&
httpContext.Request.UserAgent.Contains(requiredUserAgent);
}
}
以上程式碼,實現了一個限制,必須Request物件中的代理物件不為空,也就是必須使用代理才能訪問,並且代理名稱包含指定的名稱。
使用自定義限制:
routes.MapRoute("ChromeRoute","{*catchall}",
new { controller = "Home", action ="Index" },
new {
customConstraint = newUserAgentConstraint("Chrome")
},
new[] { "UrlsAndRoutes.AdditionalControllers" });
這樣,就實現了,必須使用chrome才能訪問的限制。
讓訪問物理檔案目錄的請求也參與路由
預設情況下,route system會先檢查url是否指向了一個物理檔案目錄,如果是,返回找到這個檔案,返回;否則,執行註冊到路由表裡面的每一條route。也就是說,指向物理檔案目錄的請求實際在mvc路由機制之前已經被處理掉了,預設沒有參與路由。
如果要改變這個行為,例如訪問
http://mysite/resource/pic/ ,不希望route去找resource/pic物理目錄,而希望route找resourcecontroller ,pic action,那麼:
在註冊路由時,加上
routes.RouteExistingFiles = true;
這樣,本地檔案的檢測 就會在路由機制執行之後了。
在路由前後,httppipeline的示意圖
其中,httpmodule 1和 httpmodule2 在pipeline上面routesystem前後的兩個module。
加上route.RouteExistingFiles=true
Route system中,就會先執行MVC Route,後執行CheckDisk Files .最後,為物理檔案指定一個route:
routes.MapRoute("DiskFile","Content/StaticContent.html",
new {
controller = "Resource",
action = "List",
});
By Passing Route System
有些訪問資原始檔的請求,是需要ignore的,這種需求直接用ignoreRoute就可以了:
routes.IgnoreRoute("Content/{filename}.html");
這樣,對於url:http://mysite/Content.test.html 的請求就被route忽略了,訪問頁面,就會出現資源無法找到和404錯誤:
HTTP Error 404.0 - Not Found
The resource you are looking for has beenremoved, had its name changed, or is temporarily unavailable.
相關文章
- Asp.Net MVC4 系列--進階篇之Model(1)ASP.NETMVC
- Asp.Net MVC4系列--進階篇之Helper(1)ASP.NETMVC
- Asp.Net MVC4 系列--進階篇之路由 (2)ASP.NETMVC路由
- Asp.Net MVC4系列--進階篇之AJAXASP.NETMVC
- Asp.Net MVC4 系列--進階篇之ViewASP.NETMVCView
- Asp.Net MVC4 系列--進階篇之Model(2)ASP.NETMVC
- Asp.Net MVC4 系列--進階篇之Helper(2)ASP.NETMVC
- Asp.Net MVC4 系列--進階篇之Controller(2)ASP.NETMVCController
- Asp.Net MVC系列--進階篇之controller(1)ASP.NETMVCController
- Asp.Net MVC4 系列--基礎篇(1)ASP.NETMVC
- Asp.Net MVC 系列--進階篇之FilterASP.NETMVCFilter
- Asp.Net MVC4系列---基礎篇(5)ASP.NETMVC
- Asp.Net MVC4系列---基礎篇(4)ASP.NETMVC
- 【webpack 系列】進階篇Web
- vue2進階篇:vue-router之命名路由Vue路由
- React進階篇1React
- QlikView Script – 進階篇1 Script呼叫Macro之變化ViewMac
- Sanic 路由進階路由
- 測開之函式進階· 第1篇《遞迴函式》函式遞迴
- .NET進階系列之四:深入DataTable
- 帶你深度解鎖Webpack系列(進階篇)Web
- 正規表示式系列之中級進階篇
- asp.net core 系列之Response caching(1)ASP.NET
- Java多執行緒之進階篇Java執行緒
- Membership三步曲之進階篇
- Java進階篇 設計模式之十四 ----- 總結篇Java設計模式
- to debug asp.net mvc4ASP.NETMVC
- 你所不知道的ASP.NET Core進階系列(三)ASP.NET
- create table進階學習系列(十一)之cluster
- Linux ACL 許可權之進階篇Linux
- Dagger 2 系列(五) -- 進階篇:@Scope 和 @Singleton
- Vue開發之路由進階Vue路由
- ASP.NET進階:認清控制元件 之 Button (轉)ASP.NET控制元件
- 高階前端進階系列 - webview前端WebView
- Flutter進階:路由、路由棧詳解及案例分析Flutter路由
- ASP.NET Web API實踐系列03,路由模版, 路由慣例, 路由設定ASP.NETWebAPI路由
- 持續改進之技術篇#1
- [一天一個進階系列] - MyBatis基礎篇MyBatis