文章目錄
- 自訂RouteBase
- 自訂IRouteHandler
系列目錄
DataTokens和Areas機制
到目前為止Route對象只剩下DataTokens屬性沒有涉及,事實上這個Areas機制的核心。
DataTokens實際上也是一個RouteValueDictionary,在用MapRoute方法構造在Route構造的時候,可以傳一個namespaces字串數組,這個參數會構造成Route對象的DataTokens["Namespaces"],它的值將被MVC架構優先用來在對應的名字空間中尋找相應的Controller。如果在指定的名字空間中能找到Controller,那麼,就算在其他名字空間中有相同名字的Controller(大小寫敏感)也沒關係;如果在指定的名字空間中沒有找到Controller,那麼將在所有引用的程式集中尋找,此時如果出現重複名字的Controller,那麼將出現多個匹配的錯誤。這種行為是DefaultControllerFactory實現的,關於DefaultControllerFactory將在以後分析。
Areas機制是這樣的一種機制:在不同的Area中可以有相同名字的Controller,也就是說Controller的名字可以重複了!這樣整個web應用程式可以按功能劃分成幾個模組,每個模組是一個Area,每個Area互相獨立,可以獨立地由某個開發人員隨意定義URL或Controller而不影響其他Area。
比如:有管理員和使用者兩個模組,也許需要如下的URL:
Admin/Home/Index
User/Home/Index
於是可以用VS整合的Area產生模板建立兩個Area:Admin和User,分別地,由兩個開發人員分別負責開發,他們都需要用HomeController和Index方法,有了Areas機制,HomeController被分別放到兩個不同的名字空間中,這就不會有衝突。
講到這裡你也許隱約明白DataTokens和Areas機制的某種關係了。在vs建立Areas的時候到底做了哪些事情呢?
一、首先在每個Area中Controller都將被放置到一個名字空間中,例如:MyAppName.Areas.Admin.Controllers;
二、為每個Area建立一個AreaRegistration的繼承類,如果是Admin的Area將是AdminAreaRegistration。在這個類中重寫AreaName屬性和RegisterArea方法:
public override void RegisterArea(AreaRegistrationContext context){ context.MapRoute( "Admin_default", "Admin/{controller}/{action}/{id}", new { action = "Index", id = UrlParameter.Optional } );}
可以看到在RegisterArea方法中也調用了MapRoute方法註冊路由。需要注意的是這個的MapRoute雖然也是操作全域路由表,但是它的實現略有不同:
1.首先設定的URL Pattern是以Area名字開頭的,這樣做是必要的,畢竟這個URL Pattern最終是放在全域中的;
2.它會將DataTokens["Namespaces"]設定成當前Area的名字空間,比如MyAppName.Areas.Admin.*。結果是,當一個請求到來是,DefaultControllerFactory會優先到這個名字空間下尋找Controller。而且會增加一個DataTokens["UseNamespaceFallback"],並設定為false,這樣若且唯若顯示設定的名字空間中有需要的Controller時,才能成功,其他名字空間的的同名Controller將無效;
3.最後,還會添加一個叫DataTokens["area"]的索引值,並設定為當前Area名字,這是為了在反向映射(outbounding)URL的時候使用。因此在MVC中"area"鍵是有特殊用途的,所以不能用於url pattern的參數。
在Areas機制中有一個衝突需要注意。由於路由表只有一張,如果當前的url映射到了"root area"(即在Global域),那麼將從當前所有的名字空間中尋找Controller,此時很可能找到多個匹配的。解決方案是,在Globla.asax.cs中設定路由的時候,為DataTokens設定優先名字空間。
進一步擴充
當從深層次瞭解了路由工作機制後,就進行一些自訂了。
自訂RouteBase
有前面的分析,可以知道,在inbound時Route(繼承自RouteBase)需要提供一個RouteData,因此RouteBase定義了GetRouteData方法,這是我們可以自己實現的;同時,GetVirtualPath方法用於outbound。所以,只要實現了這兩個方法就可以完成一個RouteBase的實現。比如:當想要把一個老的網站改造成新的基於MVC架構的,又不想使原來的url失效,簡單的處理方案可以像下面這樣:
public class LegacyUrlsRoute : RouteBase{// In practice, you might fetch these from a database// and cache them in memoryprivate static string[] legacyUrls = new string[] {"~/articles/may/zebra-danio-health-tips.html","~/articles/VelociraptorCalendar.pdf","~/guides/tim.smith/BuildYourOwnPC_final.asp"};public override RouteData GetRouteData(HttpContextBase httpContext){string url = httpContext.Request.AppRelativeCurrentExecutionFilePath;if(legacyUrls.Contains(url, StringComparer.OrdinalIgnoreCase)) {RouteData rd = new RouteData(this, new MvcRouteHandler());rd.Values.Add("controller", "LegacyContent");rd.Values.Add("action", "HandleLegacyUrl");rd.Values.Add("url", url);return rd;}elsereturn null; // Not a legacy URL}public override VirtualPathData GetVirtualPath(RequestContext requestContext,RouteValueDictionary values){// This route entry never generates outbound URLsreturn null;}}
自訂IRouteHandler
通常在MVC架構中IRouteHandler由MvcRouteHandler實現,這個MVC架構的入口。儘管如此,我們還是可以自己定義一個IRouteHandler。當我們需要對某些請求做最佳化處理的時候可以考慮這樣做。因為,自訂實現IRouteHandler意味著將忽略MVC架構。比如下面這個實現:
public class HelloWorldHandler : IRouteHandler{public IHttpHandler GetHttpHandler(RequestContext requestContext){return new HelloWorldHttpHandler();}private class HelloWorldHttpHandler : IHttpHandler{public bool IsReusable { get { return false; } }public void ProcessRequest(HttpContext context){context.Response.Write("Hello, world!");}}}
勞動果實,轉載請註明出處:http://www.cnblogs.com/P_Chou/archive/2010/11/15/details-asp-net-mvc-04.html