webForm頁面運行起來url一般是這樣的:localhost:****/index.aspx,這個過程就是當你運行頁面的時候,vs開發工具自帶的微型服務器會打開你存在硬盤上的這個文件然后顯示在瀏覽器上,所以url是后半部分是頁面的名字(index.aspx),但是在mvc中卻是這樣的:localhost:****/index,因為mvc中有一整套路由機制來控制瀏覽器的請求。
看看Global.asax文件里路由的定義:
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // 路由名稱 "{controller}/{action}/{id}", // 帶有參數的 URL new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 參數默認值 ); }
注釋掉這個默認的路由,自己重新定義一個路由:
根據vs的只能提示可以看出參數一個是路由名稱,一個是路由的模式,定義為這樣:
routes.MapRoute("MyRoute", "{controller}/{action}");
有了路由了就可以去創建控制器了,新建一個HomeController:
public class HomeController : Controller { public ActionResult Index() { return View(); } }
再去創建一個對應的View:
@{ Layout = null; } <!DOCTYPE html> <html> <head> <title>Index</title> </head> <body> <div> HomeController下的Index Action響應的同名視圖Index </div> </body> </html>
直接運行程序發現報404錯誤,找不到頁面。查看下路由就知道了問題:這個路由指定的是url模式是{controller}/{action}的方式,而瀏覽器里的url卻是localhost:****,既沒有寫Controller,也沒有寫Action,顯然不符合路由的定義,自然得報404錯了。把Url補充完整就可以找到頁面了:localhost:****/home/index,當然如果在url后面少一級或者多加一級都會報404錯:localhost:****/home、localhost:****/home/index/1,可見Url必須嚴格根據路由的定義來。
當然,也可以在路由定義的時候就給默認值:
routes.MapRoute("MyRoute", "{controller}/{action}", new { controller = "Home", action = "Index" });
這樣運行程序就不報錯了:localhost:****,根據路由給的默認值就相當於:localhost:****/home/index
再看這兩條路由的定義:
routes.MapRoute("", "Customers/{controller}/{action}"); routes.MapRoute("", "X{controller}/{action}");
第一條路由對應的Url是這樣的:localhost:****/Customers/Home/Index
第二條是:localhost:****/XHome/Index
區別:第一條是靜態的路由,第一級目錄必須是路由里指定了的Customers,而第二條路由則是動態可變的,第一級只要以X開頭都可以和本路由匹配上
路由的順序:
如果同時定義了這么兩條路由:
routes.MapRoute("MyRoute", "{controller}/{action}"); routes.MapRoute("", "X{controller}/{action}");
現在瀏覽器有這么條Url請求過來了:localhost:****/XHome/Index 看到XHome好像得使第二條路由了,但是不是。這條Url同時符合兩條路由就優先使用前面的路由。根據第一條路由,這條Url過去就是找XHome這個控制器Controller里的Index Action
混合的靜態路由,帶默認值:
routes.MapRoute("ShopSchema", "Shop/{action}", new { controller = "Home" });
這條名為ShopSchema的路由沒有定義Controller,而是給了一個默認值Home,但是如果直接請求localhost:****/home/index 雖然有HomeController,但是還是會報404,因為路由已經限制了Controller部分必須是Shop,所以url得是這樣:localhost:****/shop/index,雖然沒有ShopController,但是同樣會導向到HomeController下。
注:為了演示路由效果,防止前面定義的路由影響到Url請求的控制器,所以得先把之前的所有路由都注釋了再運行查看效果。
routes.MapRoute("ShopSchema2", "Shop/OldAction", new { controller = "Home", action = "Index" });
Url:localhost:1042/shop/OldAction 找的同樣是HomeController下的Index這個Action
定義額外的參數:
路由當然不光可以定義controller和action,同時也可以定義之后的參數,比如:
routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "10086" });
在控制器下可以這么拿到傳進來的id參數:ViewBag.CustomVariable = RouteData.Values["id"];
視圖View中這么調用:@ViewBag.CustomVariable
{controller}/{action}/{id} 對應的url是這樣的:localhost:****/home/index/10010 視圖中輸出:10010
當然,因為路由給了默認值,所有一下url都可以:
localhost:****
localhost:****/home
localhost:****/home/index 輸出的都是:10086
可選參數:
routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
Optional這個在EF配置實體之間的一對一、一對多等關系里演示過,表示可選的意思,這里的路由也是一樣,表示id部分就是可選的:
localhost:**** => controller = Home action = Index
localhost:****/Customer => controller = Home action = Index
localhost:****/Customer/List => controller = Home action = Index
localhost:****/Customer/List/All => controller = Home action = Index id = All
可變長度的路由:
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
localhost:**** => controller = Home action = Index
localhost:****/Customer => controller = Home action = Index
localhost:****/Customer/List => controller = Home action = Index
localhost:****/Customer/List/All => controller = Home action = Index id = All
localhost:****/Customer/List/All/Delete => controller = Home action = Index id = All catchall = Delete
localhost:****/Customer/List/All/Delete/Perm => controller = Home action = Index id = All catchall = Delete/Perm
指定命名空間:
routes.MapRoute("MyRoute", "{controller}/{action}", new { controller = "Home", action = "Index" });
這是一個極普通的路由,項目運行起來,默認找的是HomeController下的Index這個Action,但是現在如果項目下同個命名空間有兩個HomeController怎么辦,那自然要報錯的:
可以指定命名空間:
routes.MapRoute("MyRoute", "{controller}/{action}", new { controller = "Home", action = "Index" }, new[] { "RouteDemo4Blog.Controllers" });
正則表達式路由:
routes.MapRoute("MyRoute", "{controller}/{action}", new { controller = "Home", action = "Index" }, new { controller = "^H.*" });
可適配所有以字母H開頭的Controller 再來一個:
routes.MapRoute("MyRoute", "{controller}/{action}", new { controller = "Home", action = "Index" }, new { controller = "^H.*", action = "^Index$|^About$" });
元字符^和$分別匹配字符串的開始和結束 | 表示或,所有這個路由匹配名為Index或者About的Action
指定url請求方式:
routes.MapRoute("MyRoute", "{controller}/{action}", new { controller = "Home", action = "Index" }, new { controller = "^H.*", action = "Index|About", httpMethod = new HttpMethodConstraint("GET") });
只響應GET請求(默認)
傳遞額外的值:
@Html.ActionLink("About this application", "About", new { id = "MyID" })
生成的HTML就是:
<a href="/Home/About/MyID">About this application</a>
點擊頁面上這個a標簽自然區找的就是對應的路由了。
@Html.ActionLink("Click me", "List", "Catalog", new {page=789}, null)
生成的HTML:
<a href="/Catalog/List/Purple/789">Click me</a>
對應的路由自然就是這樣的:
routes.MapRoute("MyRoute", "{controller}/{action}/{color}/{page}");
指定css:
@Html.ActionLink("About this application", "Index", "Home", null, new {id = "myAnchorID", @class = "myCSSClass"})
<a class="myCSSClass" href="/" id="myAnchorID">About this application</a>
生成完整的url:
之前生成的都是相對路徑的Url,現在看看絕對路徑的:
@Html.ActionLink("About this application", "Index", "Home", "https", "myserver.mydomain.com", " myFragmentName", new { id = "MyId"}, new { id = "myAnchorID", @class = "myCSSClass"})
生成的HTML:
<a class="myCSSClass" href="https://myserver.mydomain.com/Home/Index/MyId#myFragmentName" id="myAnchorID">About this application</a>
生成Url:
之前的@Html.ActionLink方法生成的都是a標簽,看看如何生成url:
@Url.Action("Index", "Home", new { id = "MyId" })
/Home/Index/MyId
Areas
Areas官方的解釋是為了划分功能使用的,比如區分:管理員、訂單、客戶等功能,適合大項目使用,同時方便程序員之間協同開發。來一個試試:右鍵解決方案 - 添加 - 區域 - Admin - 確定
可以看出每個區域Areas里都是個mini的MVC項目,Controller、Models、Views一個都不缺,還多了一個AdminAreaRegistration類,看看其中的代碼:
public class AdminAreaRegistration : AreaRegistration { public override string AreaName { get { return "Admin"; } } public override void RegisterArea(AreaRegistrationContext context) { context.MapRoute( "Admin_default", "Admin/{controller}/{action}/{id}", new { action = "Index", id = UrlParameter.Optional } ); } }
可以看出其中已經有一個默認的路由了,跟普通的MVC項目是一樣的,只不過前面多了一個Admin。向這個Areas里添加一個Controller試試:右鍵Controllers目錄 - 添加 - 控制器 - HomeController 同時添加對應的視圖Index:
@{ ViewBag.Title = "Index"; } <h2> Admin Area Index</h2>
運行下程序,url定位到:localhost:1042/admin/home/index就能顯示這個視圖了。但是如果直接請求localhost:1042會報錯:
很明顯是因為默認的路由和Areas下的路由沖突了,這個之前演示過。解決方法就是加上命名空間:'
routes.MapRoute( "Default", // 路由名稱 "{controller}/{action}/{id}", // 帶有參數的 URL new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // 參數默認值 new[] { "RouteDemo4Blog.Controllers" } );
當然也可以從一個Areas跳轉到另一個Areas:
@Html.ActionLink("跳轉到別的Areas", "Index", new { area = "Support" })
生成的HTML:
<a href="/Support/Home">跳轉到別的Areas</a>
跳轉到頂級Controller:
@Html.ActionLink("Click me to go to another area", "Index", new { area = "" })