請求一個ASP.NET
mvc的網站和之前的web form是有區別的,ASP.NET MVC框架內部給咱們提供了路由機制,當IIS接受到一個請求時,會先看是否請求了一個靜態資源(.html,css,js,圖片等),這一步是web form和mvc都是同樣的,若是不是說明則說明是請求的一個動態頁面,就會走asp.net的管道,mvc的程序請求都會走路由系統,會映射到一個Controller對應的Action方法,而web form請求動態頁面是會查找本地實際存在一個aspx文件。下面經過一個ASP.NET MVC5項目來詳細介紹一下APS.NET MVC5路由系統的機制。
1、認識Global.asax.cs
當咱們建立一個APS.NET MVC5的項目的時候會在項目的根目錄中生成一個Global.asax文件。
- public class MvcApplication : System.Web.HttpApplication
- {
- protected void Application_Start()
- {
- //註冊 ASP.NET MVC 應用程序中的全部區域
- AreaRegistration.RegisterAllAreas();
- //註冊 全局的Filters
- FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
- //註冊 路由規則
- RouteConfig.RegisterRoutes(RouteTable.Routes);
- //註冊 打包綁定(js,css等)
- BundleConfig.RegisterBundles(BundleTable.Bundles);
- }
- }
這個Application_Start方法會在網站啓動的自動調用,其中咱們看到:RouteConfig.RegisterRoutes(RouteTable.Routes);這個就是向ASP.NET MVC 框架註冊咱們自定義的路由規則,讓以後的URL可以對應到具體的Action。接下來咱們再來看看RegisterRoutes方法作了些什麼?
- public class RouteConfig
- {
- public static void RegisterRoutes(RouteCollection routes)
- {
- routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
- routes.MapRoute(
- name: "Default",
- url: "{controller}/{action}/{id}",
- defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
- );
- }
- }
上面代碼是vs自動爲大家生成,只定義了一個默認規則。
一、規則名:Default
二、URL分段:{controller}/{action}/{id},分別有三段,第一段對應controller參數,第段爲action參數,第三段爲id參數
三、URL段的默認值:controller爲Home,action爲Index,id = UrlParameter.Optional表示該參數爲可選的。
之因此咱們訪問http://www.xx.com/ 這樣的URL網址能正確返回,是由於咱們設置了URL段的默認值,至關於訪問:
http://www.xx.com/Home/Index
RegisterRoutes調用的是RouteCollection的MapRoute方法,RouteCollection是一個集合,繼承於Collection<RouteBase>
3、ASP.NET MVC默認的命名約定
一、Controller命名約定
Controller類必須以Controller結尾,好比:HomeController,ProductController。咱們在頁面上用HTML heper來引用一個Controller的時只須要前面Home,Product就能夠,ASP.NET MVC框架自帶的DefaultControllerFactory自動爲咱們在結尾加上Controller,並開始根據這個名字開始找對應的類。咱們建立一個ASP.NET MVC項目新加的Controller,文件會自動放在根目錄的Controllers文件夾裏面,咱們剛開始能夠看到有一個HomeController.cs。固然你也能夠實現接口IControllerFactory,定義本身的ControllerFactory來改變查找Controller文件的行爲。我會再之後的文章中介紹。
二、View命名約定
ASP.NET MVC的視圖View默認狀況是放在根目錄的Views文件下的,規則是這樣的:/Views/ControllerName/ActionName.cshtml。好比:HomeController的Action名字爲Index的視圖對應文件爲:/Views/Home/Index.cshtml
所以是經過Controller和Action的名字來肯定視圖文件的位置的。採用這個命名約定的好處是在Action返回視圖的時候會MVC框架會按照這個約定找到默認的視圖文件。好比在ProductController的Action方法List最後是這樣的代碼:
return View();
會自動去路徑,/Views/Product/找文件List.cshtml(或者List.aspx若是使用的老的視圖引擎)。
固然也能夠指定視圖的名字:
return View("~/Views/Product/List.cshtml")
或者
return View("MyOtherView")
MVC框架在查找具體的默認視圖文件時,若是在/Views/ControllerName/下面沒有找到,會再在/Views/Shared下面找,若是都沒找到就會找錯:找不到視圖。
4、ASP.NET MVC的URL規則說明
最開始咱們在網站的Application_Start事件中註冊一些路由規則routes.MapRoute,當有請求過來的時候,mvc框架會用這些路由規則去匹配,一旦找到了符合要求就去處理這個URL。例若有下面這個URL:
http://mysite.com/Admin/Index
URL能夠分爲幾段,除去主機頭和url查詢參數,MVC框架是經過/來把URL分隔成幾段的。上面的URl分爲兩段。以下圖:
第一段的值爲Admin,第二段的值爲Index,咱們是很容易看出Admin對應就是Controller,Index就是Action。可是咱們要告訴MVC框架這樣的規則,所以爲下面的Application_Start有下面的代碼:
- routes.MapRoute(
- name: "Default",
- url: "{controller}/{action}/{id}",
- defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
- );
上面表示URL規則是:
{controller}/{action}
這個路由規則有兩個段,第一個是controller,第二個是action。聲明url段每一個部分要且{}括起來,至關於佔位符,是變量。
當一個URL請求到來的時候MVC路由系統就負責把它匹配到一個具體的路由規則,並把URL每段的值提取出來。這裏說「一個具體的路由規則」,是由於可能會註冊多個路由規則,MVC路由系統會根據註冊順序一個一個的查找匹配,直到到爲止。
默認狀況,URL路由規則只匹配與之有相同URL段數量的URL。以下表:
URL
|
URL段 |
http://mysite.com/Admin/Index
|
controller = Admin
action = Index
|
http://mysite.com/Index/Admin
|
controller = Index
action = Admin
|
http://mysite.com/Apples/Oranges
|
controller = Apples
action = Oranges
|
http://mysite.com/Admin
|
無匹配-段的數量不夠
|
http://mysite.com/Admin/Index/Soccer
|
無匹配-段的數量超了 |
5、mvc建立一個簡單的Route規則
咱們在前面註冊路由規則都是經過下面的方式:
- public static void RegisterRoutes(RouteCollection routes) {
- routes.MapRoute("MyRoute", "{controller}/{action}");
- }
用到了RouteCollection的MapRoute方法。其實咱們還能夠調用 Add方法,傳一個Route的實例給它同樣的達到相同的效果。
- public static void RegisterRoutes(RouteCollection routes) {
- Route myRoute = new Route("{controller}/{action}", new MvcRouteHandler());
- routes.Add("MyRoute", myRoute);
- }
6、mvc路由的默認值的設定
以前有說:URL路由規則只匹配與之有相同URL段數量的URL,這種是嚴格,可是咱們又想有些段不用輸入,讓用戶進入指定的頁面。像,http://www.xx.com/Home/就是進入進入Home的Index。只須要設定mvc路由的默認值就能夠了。
- public static void RegisterRoutes(RouteCollection routes) {
- routes.MapRoute("MyRoute", "{controller}/{action}", new { action = "Index" });
- }
要設置Controller和Action的默認值。
- public static void RegisterRoutes(RouteCollection routes) {
- routes.MapRoute("MyRoute", "{controller}/{action}",
- new { controller = "Home", action = "Index" });
- }
下面是一個具體的Url對應的Route映射。
Url段的數量
|
實例
|
Route映射
|
0
|
mydomain.com
|
controller = Home
action = Index
|
1
|
mydomain.com/Customer
|
controller = Customer
action = Index
|
2
|
mydomain.com/Customer/List
|
controller = Customer
action = List
|
3
|
mydomain.com/Customer/List/All
|
無匹配—Url段過多
|
7、mvc使用靜態URL段
前面定義路由規則都是佔位符的形式,{controller}/{action},咱們也可使用在使用靜態字符串。如:
- public static void RegisterRoutes(RouteCollection routes) {
- routes.MapRoute("MyRoute", "{controller}/{action}",
- new { controller = "Home", action = "Index" });
- routes.MapRoute("", "Public/{controller}/{action}",
- new { controller = "Home", action = "Index" });
- }
上面匹配:http://mydomain.com/Public/Home/Index
路由:"Public/{controller}/{action}"只匹配有三段的url,第一段必須爲Public,第二和第三能夠是任何值,分別用於controller和action。
除此這外,路由規則中能夠既包含靜態和變量的混合URL段,如:
- public static void RegisterRoutes(RouteCollection routes) {
- routes.MapRoute("", "X{controller}/{action}");
- routes.MapRoute("MyRoute", "{controller}/{action}",
- new { controller = "Home", action = "Index" });
- routes.MapRoute("", "Public/{controller}/{action}",
- new { controller = "Home", action = "Index" });
- }
8、mvc的路由中自定義參數變量
mvc框架除了能夠定義自帶的controller和action的參數以外,還能夠定義自帶的變量。以下:
- public static void RegisterRoutes(RouteCollection routes) {
- routes.MapRoute("MyRoute", "{controller}/{action}/{id}",
- new { controller = "Home", action = "Index", id = "1" });
- }
上面定義了一個id,默認值咱們設爲1。
這個路由能夠匹配0-3個url段的url,第三個url段將被用於id。若是沒有對應的url段,將應用設置的的默認值。
自定義參數變量使用:
方法1、
- public ViewResult CustomVariable() {
- ViewBag.CustomVariable = RouteData.Values["id"];
- return View();
- }
MVC框架從URL獲取到變量的值均可以經過RouteData.Values["xx"],這個集合訪問。
方法2、
public ViewResult CustomVariable(int id) {
ViewBag.CustomVariable = id;
return View();
}
9、mvc定義可選URL段、可選參數
asp.net mvc定義 參數是也能夠設置爲可選的,這樣用戶能夠不用輸入這部分的參數。
一、註冊路由時定義可選URL段
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("MyRoute", "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
二、經過Action參數來定義可選參數
public ViewResult CustomVariable(string id = "DefaultId") {
ViewBag.CustomVariable = id;
return View();
}
經過Action參數來定義可選參數是沒有加默認值的,而經過註冊路由時定義可選URL段是加了默認值的,是利用c#參數的默認參數特性。這樣若是用戶沒有輸入這部分url段,就會默認值就會被使用。
10、mvc使用*來定義變長數量的URL段
除了在路由規則中聲明固定的數量的URL段,咱們也能夠定義變長數量的URL段,以下面代碼:
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
經過變量前面加一個星號(*)開頭就能匹配任意變長數量的URL。
匹配URL以下:
Url段的數量
|
實例
|
Route映射
|
0
|
mydomain.com
|
controller = Home
action = Index
|
1
|
mydomain.com/Customer
|
controller = Customer
action = Index
|
2
|
mydomain.com/Customer/List
|
controller = Customer
action = List
|
3
|
mydomain.com/Customer/List/All
|
controller = Customer
action = List
id = All
|
4
|
mydomain.com/Customer/List/All/Delete
|
controller = Customer
action = List
id = All
catchall = Delete
|
5
|
mydomain.com/Customer/List/All/Delete/Perm
|
controller = Customer
action = List
id = All
catchall = Delete /Perm
|
11、mvc使用命名空間來爲路由的Controller類定優先級
當一個用戶輸入一個URL請求ASP.NET MVC的網站時,ASP.NET MVC會根據URL的獲取請求是找到是哪個Controller類,若是一個項目有多相同的類名的Controller,就會有問題。好比:當請求的變量controller的值爲Home時,MVC框架就會去找一個Controller名字爲HomeController的類,這個類(HomeController)默認是不受限制的,若是多個命名空間都有名字爲HomeContoller的類, ASP.NET MVC就不知道怎麼辦了。當這種狀況發生是,就會報錯:
「/」應用程序中的服務器錯誤。
找到多個與名爲「Home」的控制器匹配的類型。若是爲此請求(「{controller}/{action}/{id}」)提供服務的路由沒有指定命名空間以搜索與此請求相匹配的控制器,則會發生這種狀況。若是是這樣,請經過調用帶有 'namespaces' 參數的 "MapRoute" 方法的重載來註冊此路由。
「Home」請求找到下列匹配的控制器:
WebApplication1.Controllers.HomeController
WebApplication1.Controllers1.HomeController
[InvalidOperationException: 找到多個與名爲「Home」的控制器匹配的類型。若是爲此請求(「{controller}/{action}/{id}」)提供服務的路由沒有指定命名空間以搜索與此請求相匹配的控制器,則會發生這種狀況。若是是這樣,請經過調用帶有 'namespaces' 參數的 "MapRoute" 方法的重載來註冊此路由。
「Home」請求找到下列匹配的控制器:
解決辦法:
- public static void RegisterRoutes(RouteCollection routes) {
- routes.MapRoute("Default",
- "{controller}/{action}/{id}",
- new { controller = "Home", action = "Index", id = UrlParameter.Optional },
- new string[] { "WebApplication1.Controllers" }
- );
- }
上面MapRoute的最後一個參數,new string[] { "WebApplication1.Controllers" }就是指定先去命名空間爲WebApplication1.Controllers查找在controller,若是找到就中止往下找,沒找到仍是會去其它命名空間中去找的。所以當你指定的這個命名空間若是沒存在要找的controller類,而在其它命名空間是有的,是會正常執行的,因此這裏指定命名空間並非限定了命名空間,而只是設了一個優先級而已。
12、mvc定義路由規則的約束
在前面咱們介紹了爲mvc路由的規則設置路由默認值和可選參數,如今咱們再深刻一點,咱們要約束一下路由規則。
一、用正則表達式限制asp.net mvc路由規則
- public static void RegisterRoutes(RouteCollection routes) {
- routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
- new { controller = "Home", action = "Index", id = UrlParameter.Optional },
- new { controller = "^H.*"},
- new[] { "URLsAndRoutes.Controllers"});
- }
上面用到正則表達式來限制asp.net mvc路由規則,表示只匹配contorller名字以H開頭的URL。
二、把asp.net mvc路由規則限制到到具體的值
- public static void RegisterRoutes(RouteCollection routes) {
- routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
- new { controller = "Home", action = "Index", id = UrlParameter.Optional },
- new { controller = "^H.*", action = "^Index$|^About$"},
- new[] { "URLsAndRoutes.Controllers"});
- }
上例在controller和action上都定義了約束,約束是同時起做用是,也就是要同時知足。上面表示只匹配contorller名字以H開頭的URL,且action變量的值爲Index或者爲About的URL。
三、把asp.net mvc路由規則限制到到提交請求方式(POST、GET)
- public static void RegisterRoutes(RouteCollection routes) {
- routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
- new { controller = "Home", action = "Index", id = UrlParameter.Optional },
- new { controller = "^H.*", action = "Index|About",
- httpMethod = new HttpMethodConstraint("GET") },
- new[] { "URLsAndRoutes.Controllers" });
- }
上面表示只匹配爲GET方式的請求。javascript
四、使用接口IRouteConstraint自定義一個asp.net mvc路由約束
下面我自定義一個約束對特定瀏覽器進行處理。
UserAgentConstraint.cs:
- using System.Web;
- using System.Web.Routing;
- namespace URLsAndRoutes.Infrastructure {
- public class UserAgentConstraint : IRouteConstraint {
- private string requiredUserAgent;
- public UserAgentConstraint(string agentParam) {
- requiredUserAgent = agentParam;
- }
- public bool Match(HttpContextBase httpContext, Route route, string parameterName,
- RouteValueDictionary values, RouteDirection routeDirection) {
- return httpContext.Request.UserAgent != null &&
- httpContext.Request.UserAgent.Contains(requiredUserAgent);
- }
- }
- }
asp.net mvc自定義路由約束的使用:
- public static void RegisterRoutes(RouteCollection routes) {
- routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
- new { controller = "Home", action = "Index", id = UrlParameter.Optional },
- new {
- controller = "^H.*", action = "Index|About",
- httpMethod = new HttpMethodConstraint("GET", "POST"),
- customConstraint = new UserAgentConstraint("IE")
- },
- new[] { "URLsAndRoutes.Controllers" });
- }
上面表示這個路由規則只匹配用戶使用IE瀏覽器的請求。利用這點咱們就能夠實現不一樣瀏覽器使用不一樣的Controller,進行不一樣的處理。雖然這樣作的意義不大,可是不排除有時會有這種變態的需求。
十3、mvc將URL路由到磁盤文件
mvc的網站並非因此的url請求都是對應controller,action,咱們仍然要一種方式來提供一些靜態內容,好比:html文件,css,圖片,javascript文件些,其實默認狀況下mvc框架在在收到url請求時會先判斷這個url是不是對應一個磁盤中真實存在的文件,若是是直接返回,這時路由是沒有使用到的,若是不是真實存在的文件時纔會走路由系統,再去匹配註冊的路由規則。
這種默認的處理url機制順序咱們也能夠改變它,讓在檢查物理文件以前就應用路由,以下:
- public static void RegisterRoutes(RouteCollection routes) {
- routes.RouteExistingFiles = true;
- routes.MapRoute("DiskFile", "Content/StaticContent.html",
- new {
- controller = "Account", action = "LogOn",
- },
- new {
- customConstraint = new UserAgentConstraint("IE")
- });
- routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
- new { controller = "Home", action = "Index", id = UrlParameter.Optional },
- new {
- controller = "^H.*", action = "Index|About",
- httpMethod = new HttpMethodConstraint("GET", "POST"),
- customConstraint = new UserAgentConstraint("IE")
- },
- new[] { "URLsAndRoutes.Controllers" });
- }
咱們把RouteExistingFiles屬性設置爲true,表示存在的文件也走路由,上面咱們把Content/StaticContent.html這個文件映射到controller 爲Account,action 爲LogOn中了,而並非指磁盤中存在的文件。基於asp.net mvc的這個特性咱們就能夠實現mvc以.html結尾的僞靜態,具體實現方式請看我之前寫的文章:
教你如何在asp.net mvc中實現高性能以html結尾的僞靜態
十4、mvc跳過、繞開路由系統設定
上面咱們用使用routes.RouteExistingFiles = true,讓全部的請求都走路由系統過一下,不免有一些性能影響,由於一些圖片,一些真正的html,文件是沒有必要的。咱們能夠對些文件作一些起特殊設定讓它們跳過、繞開路由系統。下面就是讓Content目錄下的全部文件都繞開mvc的路由系統:
- public static void RegisterRoutes(RouteCollection routes) {
- routes.RouteExistingFiles = true;
- routes.MapRoute("DiskFile", "Content1/StaticContent.html",
- new {
- controller = "Account", action = "LogOn",
- },
- new {
- customConstraint = new UserAgentConstraint("IE")
- });
- routes.IgnoreRoute("Content/*{filename}");
- routes.MapRoute("", "{controller}/{action}");
- }