前一段時間接觸了MVC的Area可以將模型、控制器和視圖分成各個獨立的節點。分區之后,區域路由注冊的需求就出來了。
默認的
在MVC項目上右鍵添加區域之后,在文件夾下會自動添加一個FolderNameAreaRegistration.cs的文件。
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 } ); } }
在其中,定義了一個繼承AreaRegistration的類,類下面 重寫了AreaName和RegisterArea。當然,這一串代碼已經可以很好的解決區域的路由注冊問題了。
但是對於這重復的代碼有點排斥,另一個也想看看有沒有其他的替換方式。
想看看能不能在路由注冊那里統一管理
插曲一
之前在開始接觸分區的時候,碰到過區域下頁面的layout鏈接錯誤的問題,后來的解決方式是在ActionLink的routeValue參數里面定義area=""。
情況和這里的有點像,不同的是,上面的需要清除area,這里需要添加area。
插曲二
最近在看《asp.net mvc4高級編程》這本書,接觸到一個很有用的工具RouteDebugger。
過程一
路由注冊那里,調用的是routes.MapRoute函數,來向RouteTable.Routes中添加route。這個函數有好幾個擴展,結合上面插曲一的思路,在擴展函數的defaults里面,嘗試添加屬性area="Test":
routes.MapRoute( name: "Test_Default", url: "Test/{controller}/{action}/{id}", defaults: new { area = "Test", controller = "AAA", action = "Index", id = UrlParameter.Optional }, namespaces: new string[] { "MVCTest.Areas.Test.Controllers" });
調試通過RouteDebugger看:
輸入/Test/AAA/Index,頁面報錯,此路不通。
過程二
同事研究這塊時,發現除了默認AreaRegistration類以外的方法:
[RouteArea("Admin")] [RoutePrefix("Test")] [Route("{action=index}")] public partial class TestController : Controller { }
在區域下的Controller上面添加Route相關特性。主要就三個:Route、RouteArea和RoutePrefix。第一個定義Area,第二個定義Controller,第三個定義默認action值為index。實際調試后,發現前台准確的匹配到了路由,思路OK
到此,結合RouteDebugger,再調試到前台,我們可以看到:
從這個圖可以看出,MapRoute的數據定義了Url、Defaults和Constraints,后面的DataTokens不能通過MapRoute函數里面傳入。
查看源代碼:
public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) { if (routes == null) { throw new ArgumentNullException("routes"); } if (url == null) { throw new ArgumentNullException("url"); } Route route = new Route(url, new MvcRouteHandler()) { Defaults = CreateRouteValueDictionaryUncached(defaults), Constraints = CreateRouteValueDictionaryUncached(constraints), DataTokens = new RouteValueDictionary() }; ConstraintValidation.Validate(route); if ((namespaces != null) && (namespaces.Length > 0)) { route.DataTokens[RouteDataTokenKeys.Namespaces] = namespaces; } routes.Add(name, route); return route; }
注意,上面的DataTokens是new了一個RouteValueDictionary對象。
而想要注入的area數據正是在DataTokens里面。所以上面的第一次嘗試失敗,是因為數據注入到了Defaults里面。
到了這里,怎么在RegisterRoutes里面統一管理區域的路由注冊,思路已經呼之欲出了。
解決方案:
routes.Add(new Route("PaperMaster/{controller}/{action}/{id}" , new RouteValueDictionary(new { controller = "Papers", action = "Index", id = UrlParameter.Optional }) , new RouteValueDictionary() , new RouteValueDictionary(new { area = "PaperMaster", namespaces = "Packmage.Web.Areas.PaperMaster.Controllers" }) , new MvcRouteHandler()));
直接實例化Route對象,輸入它的各個需要的屬性;不用MapRoute,改用Add,直接向RouteTable.Routes中添加route對象。
到此問題解決。留文備用。
PS:以前做winform沒有怎么接觸.net的源碼,現在研究asp.net,開源的模塊比較多,越來越多的接觸.net的源碼,感覺讀源碼非常重要,也非常有用。能夠更加清晰的知道自己的代碼在干什么,遇到問題,可以通過查看本質而去尋求直接有效的解決方法,效率也高很多。