Orchard对mvc路由重新做了包装,重写了asp.net的路由模块
一、路由模块类图
1、路由 Descriptor
RouteDescriptor是对常规mvc路由的包装类,它的Route属性就是在mvc注册路由中使用的RouteBase类型。在Orchard中注册路由,应该用这种方式
-
newRouteDescriptor{ Route=newRoute( "Admin/Blogs/Create", newRouteValueDictionary{ {"area","Orchard.Blogs"}, {"controller","BlogAdmin"}, {"action","Create"} }, newRouteValueDictionary(), newRouteValueDictionary{ {"area","Orchard.Blogs"} }, newMvcRouteHandler()) }
2、路由实现
Orchard的路由实现有2个类组成:HubRoute和ShellRoute类。它们都继承了System.Web.Routing.RouteBase类,要重新实现GetRouteData和GetVirtualPath方法
2.1、HubRoute类中的GetRouteData方法
-
publicoverrideRouteDataGetRouteData(HttpContextBase httpContext) { var settings = _runningShellTable.Match(httpContext); if(settings ==null) returnnull; //这里获取1个ShellRoute的集合 IList<RouteBase> routes; if(!_routesByShell.TryGetValue(settings.Name,out routes)) { returnnull; } foreach(var route in routes) { RouteData routeData = route.GetRouteData(httpContext);//这里的route是ShellRoute类型 if(routeData !=null) { return routeData; } } returnnull; }
HubRoute类只是1个route的分配器,它的GetRouteData方法只是从1个ShellRoute集合中获取匹配的 ShellRoute,而真正的获取RouteData 实例的是ShellRoute 类中的 GetRouteData方法
2.2、ShellRoute中的GetRouteData方法
-
publicoverrideRouteDataGetRouteData(HttpContextBase httpContext) { // locate appropriate shell settings for request var settings = _runningShellTable.Match(httpContext); // only proceed if there was a match, and it was for this client if(settings ==null|| settings.Name!= _shellSettings.Name) returnnull; var effectiveHttpContext = httpContext; if(_urlPrefix !=null) effectiveHttpContext =newUrlPrefixAdjustedHttpContext(httpContext, _urlPrefix); var routeData = _route.GetRouteData(effectiveHttpContext);//这里获取的是真正的mvc RouteData if(routeData ==null) returnnull; // otherwise wrap handler and return it routeData.RouteHandler=newRouteHandler(_workContextAccessor, routeData.RouteHandler,SessionState); routeData.DataTokens["IWorkContextAccessor"]= _workContextAccessor; if(IsHttpRoute) { routeData.Values["IWorkContextAccessor"]= _workContextAccessor;// for WebApi } return routeData; }
3、路由注册
Orchard的路由注册功能由2个接口定义:IRouteProvider、IRoutePublisher。Orchard提供默认的实现:DefaultRouteProvider和RoutePublisher类
3.1、DefaultRouteProvider类:
-
publicclassDefaultRouteProvider:IRouteProvider { //GetRoutes方法中定义了你要使用的所有的路由信息. publicIEnumerable<RouteDescriptor>GetRoutes() { returnnew[]{ newRouteDescriptor{ Priority=-20, Route=newRoute( "{controller}/{action}/{id}", newRouteValueDictionary{ {"controller","home"}, {"action","index"}, {"id",""}, }, newRouteValueDictionary{ {"controller",newHomeOrAccount()} }, newMvcRouteHandler()) } }; } publicvoidGetRoutes(ICollection<RouteDescriptor> routes) { foreach(var routeDescriptor inGetRoutes()) routes.Add(routeDescriptor); } }
3.2、RoutePublisher类:
-
public void Publish(IEnumerable<RouteDescriptor> routes) { var routesArray = routes .OrderByDescending(r => r.Priority) .ToArray(); // this is not called often, but is intended to surface problems before // the actual collection is modified var preloading = new RouteCollection(); foreach (var routeDescriptor in routesArray) { // extract the WebApi route implementation var httpRouteDescriptor = routeDescriptor as HttpRouteDescriptor; if (httpRouteDescriptor != null) { var httpRouteCollection = new RouteCollection(); httpRouteCollection.MapHttpRoute(httpRouteDescriptor.Name, httpRouteDescriptor.RouteTemplate, httpRouteDescriptor.Defaults, httpRouteDescriptor.Constraints); routeDescriptor.Route = httpRouteCollection.First(); } preloading.Add(routeDescriptor.Name, routeDescriptor.Route); } using (_routeCollection.GetWriteLock()) { // existing routes are removed while the collection is briefly inaccessable _routeCollection .OfType<HubRoute>() .ForEach(x => x.ReleaseShell(_shellSettings)); // new routes are added foreach (var routeDescriptor in routesArray) { // Loading session state information. var defaultSessionState = SessionStateBehavior.Default; ExtensionDescriptor extensionDescriptor = null; if (routeDescriptor.Route is Route) { object extensionId; var route = routeDescriptor.Route as Route; if (route.DataTokens != null && route.DataTokens.TryGetValue("area", out extensionId) || route.Defaults != null && route.Defaults.TryGetValue("area", out extensionId)) { extensionDescriptor = _extensionManager.GetExtension(extensionId.ToString()); } } else if (routeDescriptor.Route is IRouteWithArea) { var route = routeDescriptor.Route as IRouteWithArea; extensionDescriptor = _extensionManager.GetExtension(route.Area); } if (extensionDescriptor != null) { // if session state is not define explicitly, use the one define for the extension if (routeDescriptor.SessionState == SessionStateBehavior.Default) { Enum.TryParse(extensionDescriptor.SessionState, true /*ignoreCase*/, out defaultSessionState); } } // Route-level setting overrides module-level setting (from manifest). var sessionStateBehavior = routeDescriptor.SessionState == SessionStateBehavior.Default ? defaultSessionState : routeDescriptor.SessionState; var shellRoute = new ShellRoute(routeDescriptor.Route, _shellSettings, _workContextAccessor, _runningShellTable) { IsHttpRoute = routeDescriptor is HttpRouteDescriptor, SessionState = sessionStateBehavior }; var area = extensionDescriptor == null ? "" : extensionDescriptor.Id; var matchedHubRoute = _routeCollection.FirstOrDefault(x => { var hubRoute = x as HubRoute; if (hubRoute == null) { return false; } return routeDescriptor.Priority == hubRoute.Priority && hubRoute.Area.Equals(area, StringComparison.OrdinalIgnoreCase) && hubRoute.Name == routeDescriptor.Name; }) as HubRoute; if (matchedHubRoute == null) { matchedHubRoute = new HubRoute(routeDescriptor.Name, area, routeDescriptor.Priority, _runningShellTable); int index; for (index = 0; index < _routeCollection.Count; index++) { var hubRoute = _routeCollection[index] as HubRoute; if (hubRoute == null) { continue; } if (hubRoute.Priority < matchedHubRoute.Priority) { break; } } _routeCollection.Insert(index, matchedHubRoute); } matchedHubRoute.Add(shellRoute, _shellSettings); } } }
Publish方法将路由注册到asp.net mvc 的路由系统中。
二、如何在Module开发中使用路由系统
Orchard的功能都是通过Module实现的。那么在Module开发中如何使用 路由呢?我们以Orcahrd自己的 Orchard.Blogs Module为例,在Orchard中实现自己的路由其实很简单,只需要在自己的Module中创建1个实现IRouteProvider接口的类型即可。
-
public class Routes : IRouteProvider { private readonly IArchiveConstraint _archiveConstraint; private readonly IRsdConstraint _rsdConstraint; public Routes( IArchiveConstraint archiveConstraint, IRsdConstraint rsdConstraint) { _archiveConstraint = archiveConstraint; _rsdConstraint = rsdConstraint; } public void GetRoutes(ICollection<RouteDescriptor> routes) { foreach (var routeDescriptor in GetRoutes()) routes.Add(routeDescriptor); } public IEnumerable<RouteDescriptor> GetRoutes() { return new[] { new RouteDescriptor { Route = new Route( "Admin/Blogs/Create", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "BlogAdmin"}, {"action", "Create"} }, new RouteValueDictionary(), new RouteValueDictionary { {"area", "Orchard.Blogs"} }, new MvcRouteHandler()) }, new RouteDescriptor { Route = new Route( "Admin/Blogs/{blogId}/Edit", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "BlogAdmin"}, {"action", "Edit"} }, new RouteValueDictionary (), new RouteValueDictionary { {"area", "Orchard.Blogs"} }, new MvcRouteHandler()) }, new RouteDescriptor { Route = new Route( "Admin/Blogs/{blogId}/Remove", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "BlogAdmin"}, {"action", "Remove"} }, new RouteValueDictionary (), new RouteValueDictionary { {"area", "Orchard.Blogs"} }, new MvcRouteHandler()) }, new RouteDescriptor { Route = new Route( "Admin/Blogs/{blogId}", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "BlogAdmin"}, {"action", "Item"} }, new RouteValueDictionary (), new RouteValueDictionary { {"area", "Orchard.Blogs"} }, new MvcRouteHandler()) }, new RouteDescriptor { Route = new Route( "Admin/Blogs/{blogId}/Posts/Create", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "BlogPostAdmin"}, {"action", "Create"} }, new RouteValueDictionary (), new RouteValueDictionary { {"area", "Orchard.Blogs"} }, new MvcRouteHandler()) }, new RouteDescriptor { Route = new Route( "Admin/Blogs/{blogId}/Posts/{postId}/Edit", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "BlogPostAdmin"}, {"action", "Edit"} }, new RouteValueDictionary (), new RouteValueDictionary { {"area", "Orchard.Blogs"} }, new MvcRouteHandler()) }, new RouteDescriptor { Route = new Route( "Admin/Blogs/{blogId}/Posts/{postId}/Delete", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "BlogPostAdmin"}, {"action", "Delete"} }, new RouteValueDictionary (), new RouteValueDictionary { {"area", "Orchard.Blogs"} }, new MvcRouteHandler()) }, new RouteDescriptor { Route = new Route( "Admin/Blogs/{blogId}/Posts/{postId}/Publish", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "BlogPostAdmin"}, {"action", "Publish"} }, new RouteValueDictionary (), new RouteValueDictionary { {"area", "Orchard.Blogs"} }, new MvcRouteHandler()) }, new RouteDescriptor { Route = new Route( "Admin/Blogs/{blogId}/Posts/{postId}/Unpublish", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "BlogPostAdmin"}, {"action", "Unpublish"} }, new RouteValueDictionary (), new RouteValueDictionary { {"area", "Orchard.Blogs"} }, new MvcRouteHandler()) }, new RouteDescriptor { Route = new Route( "Admin/Blogs", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "BlogAdmin"}, {"action", "List"} }, new RouteValueDictionary(), new RouteValueDictionary { {"area", "Orchard.Blogs"} }, new MvcRouteHandler()) }, new RouteDescriptor { Route = new Route( "Blogs", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "Blog"}, {"action", "List"} }, new RouteValueDictionary(), new RouteValueDictionary { {"area", "Orchard.Blogs"} }, new MvcRouteHandler()) }, new RouteDescriptor { Route = new Route( "{*path}", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "BlogPost"}, {"action", "ListByArchive"} }, new RouteValueDictionary { {"path", _archiveConstraint}, }, new RouteValueDictionary { {"area", "Orchard.Blogs"} }, new MvcRouteHandler()) }, new RouteDescriptor { Priority = 11, Route = new Route( "{*path}", new RouteValueDictionary { {"area", "Orchard.Blogs"}, {"controller", "RemoteBlogPublishing"}, {"action", "Rsd"} }, new RouteValueDictionary { {"path", _rsdConstraint} }, new RouteValueDictionary { {"area", "Orchard.Blogs"} }, new MvcRouteHandler()) } }; } }
三、Orchard路由系统如何工作的。
1、将自定义路由注册到asp.net mvc路由系统中
这个工作是在创建和激活shell的时候,调用 DefaultOrchardShell类的Activate方法时进行的
-
publicvoidActivate() { var allRoutes =newList<RouteDescriptor>(); allRoutes.AddRange(_routeProviders.SelectMany(provider => provider.GetRoutes())); allRoutes.AddRange(_httpRouteProviders.SelectMany(provider => provider.GetRoutes())); _routePublisher.Publish(allRoutes);//这里就是注册路由 _modelBinderPublisher.Publish(_modelBinderProviders.SelectMany(provider => provider.GetModelBinders())); using (var events = _eventsFactory()) { events.Value.Activated(); } _sweepGenerator.Activate(); }
2、路由的使用:像mvc路由一样使用自定义路由。