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=-,
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 = ; 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 = ,
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路由一样使用自定义路由。