ASP.NET MVC 4 (一)路径映射
正如ASP.NET MVC名字所揭示的一樣,是以模型-視圖-控制設(shè)計(jì)模式構(gòu)建在ASP.NET基礎(chǔ)之上的WEB應(yīng)用程序,我們需要?jiǎng)?chuàng)建相應(yīng)的程序類來(lái)協(xié)調(diào)處理,完成從客戶端請(qǐng)求到結(jié)果相應(yīng)的整個(gè)過(guò)程:
VS2012中一個(gè)典型的MVC工程結(jié)構(gòu)是這樣的:
Controllers文件夾下存放控制類,Models文件下是業(yè)務(wù)數(shù)據(jù)模型類,Views文件下則是類似于aspx的視圖文件。在傳統(tǒng)ASP.NET form的應(yīng)用程序中,客戶端的請(qǐng)求最后都映射到磁盤上對(duì)應(yīng)路徑的一個(gè)aspx的頁(yè)面文件,而MVC程序中所有的網(wǎng)絡(luò)請(qǐng)求映射到控制類的某一個(gè)方法,我們就從控制類說(shuō)起,而在講控制類前,必須要講的是URL路由。
注冊(cè)URL路由
我們?cè)跒g覽器中請(qǐng)求鏈接?http://mysite.com/Home/Index,MVC認(rèn)為是這樣的URL模式(默認(rèn)路徑映射):
{controller}/{action}也就是說(shuō)上面的請(qǐng)求會(huì)被映射到Home控制類的Index方法,MVC命名規(guī)則中控制類必須以Controller結(jié)尾,所以Home控制類應(yīng)該是HomeController:?
public class HomeController : Controller{public ActionResult Index(){return View();}}?MVC是根據(jù)什么將上面的請(qǐng)求映射到控制類的相應(yīng)方法的呢?答案就是路由表,Global.asax在應(yīng)用程序啟動(dòng)時(shí)會(huì)調(diào)用路由配置類來(lái)注冊(cè)路徑映射:
public class MvcApplication : System.Web.HttpApplication{protected void Application_Start(){AreaRegistration.RegisterAllAreas();WebApiConfig.Register(GlobalConfiguration.Configuration);FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);RouteConfig.RegisterRoutes(RouteTable.Routes);BundleConfig.RegisterBundles(BundleTable.Bundles);}}路徑映射的配置類則在App_Start目錄下:
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 });}}routes.MapRoute()添加了一個(gè)URL路由到路由表中,URL的映射模式是"{controller}/{action}/{id}",controller和action我們已經(jīng)清楚,id則是請(qǐng)求中額外的參數(shù),比如我們的請(qǐng)求可以是?http://mysite.com/Home/Index/3,對(duì)應(yīng)的action方法可以是:
public ActionResult Index(int id=1) {return View(); }在傳遞到Index方法時(shí)參數(shù)id會(huì)被賦值3(保存在RouteData.Values["id"]),MVC足夠智能來(lái)解析參數(shù)并轉(zhuǎn)化為需要的類型,MVC稱之為模型綁定(后續(xù)具體來(lái)看)。上面注冊(cè)路由時(shí)使用了默認(rèn)參數(shù):defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },如果我們?cè)谡?qǐng)求URL沒有指定某些參數(shù),defaults參數(shù)會(huì)被用作默認(rèn)值,比如:
mydomain.com = mydomain.com/home/index mydomain.com/home = mydomain/home/index mydomain.com/customer = mydomain/customer/indexid為可選參數(shù),可以不包括在URL請(qǐng)求中,所以上面注冊(cè)的路徑可以映射的URL有:
mydomain.com mydomain.com/home mydomain.com/home/list mydomain.com/customer/list/4除此之外不能映射的請(qǐng)求都會(huì)得到404錯(cuò)誤,比如mydomain.com/customer/list/4/5,這里參數(shù)過(guò)多不能被映射。
RouteCollection.MapRoute()等同于:
Route myRoute = new Route("{controller}/{action}", new MvcRouteHandler()); routes.Add("MyRoute", myRoute);這里直接向Routes表添加一個(gè)Route對(duì)象。
其他一些URL映射的例子:
routes.MapRoute("", "Public/{controller}/{action}",new { controller = "Home", action = "Index" }); //URL可以包含靜態(tài)的部分,這里的public routes.MapRoute("", "X{controller}/{action}"); //所有以X開頭的控制器路徑,比如mydomain.com/xhome/index映射到home控制器 routes.MapRoute("ShopSchema", "Shop/{action}",new { controller = "Home" }); //URL可以不包含控制器部分,使用這里的默認(rèn)Home控制器 routes.MapRoute("ShopSchema2", "Shop/OldAction",new { controller = "Home", action = "Index" }); //URL可以是全靜態(tài)的,這里mydomain.com/shop/oldaction傳遞到home控制器的index方法一個(gè)比較特殊的例子:
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });這可以映射任意多的URL分段,id后的所有內(nèi)容都被賦值到cathall參數(shù),比如/Customer/List/All/Delete/Perm,catchall = Delete/Perm。
需要注意的是路徑表的注冊(cè)是有先后順序的,按照注冊(cè)路徑的先后順序在搜索到匹配的映射后搜索將停止。
命名空間優(yōu)先級(jí)
MVC根據(jù){controller}在應(yīng)用程序集中搜索同名控制類,如果在不同命名空間下有同名的控制類,MVC會(huì)給出多個(gè)同名控制類的異常,我們可以在注冊(cè)路由的時(shí)候指定搜索的命令空間:
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = UrlParameter.Optional , new[] { "URLsAndRoutes.AdditionalControllers" });這里表示我們將在"URLsAndRoutes.AdditionalControllers"命名空間搜索控制類,可以添加多個(gè)命名空間,比如:
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}", new { controller = "Home", action = "Index", id = UrlParameter.Optional }, new[] { "URLsAndRoutes.AdditionalControllers", "UrlsAndRoutes.Controllers"});"URLsAndRoutes.AdditionalControllers", "UrlsAndRoutes.Controllers"兩個(gè)命名空間是等同處理沒有優(yōu)先級(jí)的區(qū)分,如果這兩個(gè)空間里有重名的控制類一樣導(dǎo)致錯(cuò)誤,這種情況可以分開注冊(cè)多條映射:
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" });路由限制
除了在注冊(cè)路由映射時(shí)可以指定控制器搜索命名空間,還可以使用正則表達(dá)式限制路由的應(yīng)用范圍,比如:
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" });這里限制MyRoute路由僅用于映射所有H開頭的控制類、且action為Index或者About、且HTTP請(qǐng)求方法為GET的客戶端請(qǐng)求。
如果標(biāo)準(zhǔn)的路由限制不能滿足要求,可以從IRouteConstraint接口擴(kuò)展自己的路由限制類:
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);}}在注冊(cè)路由時(shí)這樣使用:
routes.MapRoute("ChromeRoute", "{*catchall}",new { controller = "Home", action = "Index" },new { customConstraint = new UserAgentConstraint("Chrome")},new[] { "UrlsAndRoutes.AdditionalControllers" });這表示我們限制路由僅為瀏覽器Agent為Chrome的請(qǐng)求時(shí)使用。
路由到磁盤文件
除了控制器方法,我們也需要返回一些靜態(tài)內(nèi)容比如HTML、圖片、腳本到客戶端,默認(rèn)情況下路由系統(tǒng)優(yōu)先檢查是否有和請(qǐng)求路徑一致的磁盤文件存在,如果有則不再?gòu)穆酚杀碇衅ヅ渎窂健N覀兛梢酝ㄟ^(guò)配置顛倒這個(gè)順序:
public static void RegisterRoutes(RouteCollection routes) { routes.RouteExistingFiles = true; ... ....還需要修改web配置文件:
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition=""/>這里設(shè)置preCondition為空。如果我們?cè)僬?qǐng)求一些靜態(tài)內(nèi)容比如~/Content/StaticContent.html時(shí)會(huì)優(yōu)先從路徑表中匹配。
而如果我們又需要忽略某些路徑的路由匹配,可以:
... public static void RegisterRoutes(RouteCollection routes) { routes.RouteExistingFiles = true; routes.IgnoreRoute("Content/{filename}.html");...
它會(huì)在RouteCollection中添加一個(gè)route handler為StopRoutingHandler的路由對(duì)象,在匹配到content路徑下的后綴為html的文件時(shí)停止繼續(xù)搜索路徑表,轉(zhuǎn)而匹配磁盤文件。
生成對(duì)外路徑
路徑表注冊(cè)不僅影響到來(lái)自于客戶端的URL映射,也影響到我們?cè)谝晥D中使用HTML幫助函數(shù)生成對(duì)外路徑,比如我們注冊(cè)了這樣的映射
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional }); } ...在視圖中調(diào)用Html.ActionLink生成一個(gè)對(duì)外路徑:
<div> @Html.ActionLink("This is an outgoing URL", "CustomVariable") </div>根據(jù)我們當(dāng)前的請(qǐng)求鏈接,http://localhost:5081/home,生成的outgoing鏈接為:?
<a href="/Home/CustomVariable">This is an outgoing URL</a>而如果我們調(diào)整路徑表為:
... public static void RegisterRoutes(RouteCollection routes) { routes.MapRoute("NewRoute", "App/Do{action}", new { controller = "Home" }); routes.MapRoute("MyRoute", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional }); } ...“@Html.ActionLink("This is an outgoing URL", "CustomVariable")?”得到的結(jié)果是:
<a href="/App/DoCustomVariable">This is an outgoing URL</a>它將使用在路徑表中找到的第一條匹配的記錄來(lái)生成相應(yīng)的鏈接路徑。
Html.ActionLink()有多個(gè)重載,可以多中方式生成URL鏈接:
@Html.ActionLink("This targets another controller", "Index", "Admin") //生成到Admin控制器Index方法的鏈接 @Html.ActionLink("This is an outgoing URL", "CustomVariable", new { id = "Hello" }) //生成額外參數(shù)的鏈接,比如上面的路徑配置下結(jié)果為“href="/App/DoCustomVariable?id=Hello"”;如果路徑映射為 "{controller}/{action}/{id}",結(jié)果為href="/Home/CustomVariable/Hello" @Html.ActionLink("This is an outgoing URL", "Index", "Home", null, new {id = "myAnchorID", @class = "myCSSClass"}) //設(shè)定生成A標(biāo)簽的屬性,結(jié)果類似“<a class="myCSSClass"href="/" id="myAnchorID">This is an outgoing URL</a> ”參數(shù)最多的調(diào)用方式是:
@Html.ActionLink("This is an outgoing URL", "Index", "Home", "https", "myserver.mydomain.com", " myFragmentName", new { id = "MyId"}, new { id = "myAnchorID", @class = "myCSSClass"})得到的結(jié)果是:
<a class="myCSSClass" href="https://myserver.mydomain.com/Home/Index/MyId#myFragmentName" id="myAnchorID">This is an outgoing URL</a>?Html.ActionLink方法生成的結(jié)果中帶有HTML的<a>標(biāo)簽,而如果只是需要URL,可以使用Html.Action(),比如:
@Url.Action("Index", "Home", new { id = "MyId" }) //結(jié)果為單純的/home/index/myid如果需要在生成URL指定所用的路徑記錄,可以:
@Html.RouteLink("Click me", "MyOtherRoute","Index", "Customer") //指定使用路徑注冊(cè)表中的MyOtherRoute記錄上面講的都是在Razor引擎視圖中生成對(duì)外URL,如果是在控制器類中我們可以:
string myActionUrl = Url.Action("Index", new { id = "MyID" }); string myRouteUrl = Url.RouteUrl(new { controller = "Home", action = "Index" });更多的情況是在控制類方法中需要轉(zhuǎn)到其他的Action,我們可以:
... public RedirectToRouteResultMyActionMethod() { return RedirectToAction("Index"); } ... public RedirectToRouteResult MyActionMethod() { return RedirectToRoute(new { controller = "Home", action = "Index", id = "MyID" }); } ...創(chuàng)建自定義ROUTE類
除了使用MVC自帶的Route類,我們可以從RouteBase擴(kuò)展自己的Route類來(lái)實(shí)現(xiàn)自定義的路徑映射:
public class LegacyRoute : RouteBase{private string[] urls;public LegacyRoute(params string[] targetUrls){urls = targetUrls;}public override RouteData GetRouteData(HttpContextBase httpContext){RouteData result = null;string requestedURL = httpContext.Request.AppRelativeCurrentExecutionFilePath;if (urls.Contains(requestedURL, StringComparer.OrdinalIgnoreCase)){result = new RouteData(this, new MvcRouteHandler());result.Values.Add("controller", "Legacy");result.Values.Add("action", "GetLegacyURL");result.Values.Add("legacyURL", requestedURL);}return result;}public override VirtualPathData GetVirtualPath(RequestContext requestContext,RouteValueDictionary values){VirtualPathData result = null;if (values.ContainsKey("legacyURL") &&urls.Contains((string)values["legacyURL"], StringComparer.OrdinalIgnoreCase)){result = new VirtualPathData(this,new UrlHelper(requestContext).Content((string)values["legacyURL"]).Substring(1));}return result;}}GetRouteData()函數(shù)用于處理URL請(qǐng)求映射,我們可以這樣注冊(cè)路徑映射:
routes.Add(new LegacyRoute( "~/articles/Windows_3.1_Overview.html", "~/old/.NET_1.0_Class_Library"));上面的例子中如果我們請(qǐng)求"~/articles/Windows_3.1_Overview.html"將被映射到Legacy控制器的GetLegacyURL方法。
GetVirtualPath()方法則是用于生成對(duì)外鏈接,在視圖中使用:
@Html.ActionLink("Click me", "GetLegacyURL", new { legacyURL = "~/articles/Windows_3.1_Overview.html" })生成對(duì)外鏈接時(shí)得到的結(jié)果是:
<a href="/articles/Windows_3.1_Overview.html">Click me</a>創(chuàng)建自定義ROUTE Handler
除了可以創(chuàng)建自定義的Route類,還可以創(chuàng)建自定義的Route handler類:
public class CustomRouteHandler : IRouteHandler {public IHttpHandler GetHttpHandler(RequestContext requestContext) {return new CustomHttpHandler();}}public class CustomHttpHandler : IHttpHandler {public bool IsReusable {get { return false; }}public void ProcessRequest(HttpContext context) {context.Response.Write("Hello");}}注冊(cè)路徑時(shí)使用自定義的Route handler:
routes.Add(new Route("SayHello", new CustomRouteHandler()));其效果就是針對(duì)鏈接 /SayHello的訪問(wèn)得到的結(jié)果就是“Hello”。
使用Area
大型的Web應(yīng)用可能分為不同的子系統(tǒng)(比如銷售、采購(gòu)、管理等)以方便管理,可以在MVC中創(chuàng)建不同的Area來(lái)劃分這些子系統(tǒng),在VS中右鍵點(diǎn)擊Solution exploer->Add->Area可以添加我們想要的區(qū)域,在Solution exploer會(huì)生成Areas/<區(qū)域名稱>的文件夾,其下包含Models、Views、Controllers三個(gè)目錄,同時(shí)生成一個(gè)AreaRegistration的子類,比如我們創(chuàng)建一個(gè)名為Admin的區(qū)域,會(huì)自動(dòng)生成名為AdminAreaRegistration的類:
namespace UrlsAndRoutes.Areas.Admin {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 });}} }它的主要作用是注冊(cè)一個(gè)到Admin/{controller}/{action}/{id}路徑映射,在global.asax中會(huì)通過(guò)AreaRegistration.RegisterAllAreas()來(lái)調(diào)用到這里的RegisterArea()來(lái)注冊(cè)區(qū)域自己的路徑映射。
在Area下創(chuàng)建Controller、視圖同整個(gè)工程下創(chuàng)建是相同的,需要注意的是可能遇到控制器重名的問(wèn)題,具體解決參見命名空間優(yōu)先級(jí)一節(jié)。
如果在視圖中我們需要生成到特定Area的鏈接,可以在參數(shù)中指定Area:
@Html.ActionLink("Click me to go to another area", "Index", new { area = "Support" })如果需要得到頂級(jí)控制器的鏈接area=""留空即可。
?
以上為對(duì)《Apress Pro ASP.NET MVC 4》第四版相關(guān)內(nèi)容的總結(jié),不詳之處參見原版 http://www.apress.com/9781430242369。??
總結(jié)
以上是生活随笔為你收集整理的ASP.NET MVC 4 (一)路径映射的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java的网络工具netty简介
- 下一篇: 基于DDD的.NET开发框架 - ABP