EPiServer7 – Routing
Introduction
In previous versions of EPiServer CMS the Friendly Url (FURL) handling was performed by an IHttpModule that executed early on incoming requests and rewrote the path to the aspx file that was specified by the page type. The module also hooked up a Filter on the outgoing response and parsed the outgoing html and rewrote urls to FURLs.
In CMS 7 the FURL handling has been rewritten to be based on the built-in routing in .NET 4.0. To use routing when using WebForms EPiServer.Web.HierarchicalUrlRewriteProvider must be set as URL rewrite provider on the site and the http module for url rewriting should be EPiServer.Web.RoutingUrlRewriteModule. Any other UrlRewriter (not inheriting HierarchicalUrlRewriteProvider) implementation will not use routing to handle incoming request but instead the previously used UrlRewrite functionality. MVC implementations will not use the UrlRewrite provider. In that case the outgoing urls are constructed using System.Web.Routing.RouteTable directly from views/controllers using for example HtmlHelper extension methods or UrlResolver class.
Default routes
There are several routes registered by default. The Shell modules will have routes registered to support routing to for example edit components and gadgets. The CMS will also register a number of routes by default. The CMS routes are registered in GlobalBase during initialization, it is possible to override method RegisterRoutes in Global.asax.cs to customize the registered routes or add additional routes. The namespace EPiServer.Web.Routing contains some extension methods for the class RouteCollection. Those extension methods can be used to register customized content routes. Among the default routes there will be one set up for routing simple address, one for each site (can be several in enterprise scenarios), and one that routes pages/content from the root (that is pages/content not under any start page). The "ordinary" MVC route "{controller}/{action}" is also registered to support partial request through for example Html.RenderAction, however direct browsing to those routes are prevented. The routes are registered in the order above. This is important because the first route will look if the URL matches its route, and if it does, the page will be routed through that route. If the URL does not match the first route, the next route will get its chance.
MVC and Web Forms
An URL can be routed through the routing framework for both MVC and web forms. The routing will first from the URL locate the content routed to. After the content has been located, the framework will query the EPiServer.Web.TemplateResolver instance which template that should be used to render the request. The template can be either a MVC controller or a WebForm. Then depending on if the template is a WebForm or a MVC controller a suitable httpHandler is set to handle the request. If no content is found that matches the URL or if no template is found that matches the routed content a 404 is returned.
Extending routing
Routing can be extended in several levels. There are events exposed both during incoming routing and creation of outgoing URLs that can be used to customize routing. It is also possible to modify the default URL pattern for content routing to handle part of the URL. And you can also add your own routes.
Events
The class EPiServer.Web.Routing.ContentRoute (that is responsible for routing to content) exposes static events RoutingContent, RoutedContent that are raised during incoming routing. RoutingContent are raised before the default routing implementation is executed and in an event handler can for example the content that should match the request be set. RoutedContent are raised after the default routing has executed and in an event handler can for example the routed content be replaced. During outgoing URL generation are the events CreatingVirtualPath and CreatedVirtualPath raised where event handlers can modify the URLs generated.
Modifying Content routing
The built in content routing is based on that an URL pattern is registered where each pattern is handled by an implementation of an interface EPiServer.Web.Routing.Segments.ISegment. It is possible to implement custom ISegment types and register them as part of the content routing.
By default content routing is registered with a pattern as "{language}/{node}/{partial}/{action}". The {language} part says that in the url there might first be an optional part that states the language. The {node} part is used to specify the CMS page/content in the URL, it will follow the site structure and contain all page names after the start page on the site, down to the requested page. For example, in the following structure "start > news > firstNews", the URL part handled by {node} part would be "/news/firstNews". If there is something remaining in the URL after the page/content routing any registered EPiServer.Web.Routing.IPartialRouter that matches the type of located page will get a chance to route the remaining of the URL. Then at last any remaining part is checked if it is a valid action for a MVC controller. If the requested action would be "myAction" (MVC), the whole URL would be "http://mySite/news/firstNews/myAction".
The 'MapContentRoute' extension method takes the name of the route, the URL pattern, and default values as arguments. Each part in the URL pattern for content routing (for example {node} or {partial}) is handled by an implementation of EPiServer.Web.Routing.Segments.ISegment.
Below is an example on how to add a route:
using EPiServer.Web.Routing;
namespace EPiServer.Samples.MvcTemplates
{
public class MvcApplication : EPiServer.Global
{
protected override void RegisterRoutes(RouteCollection routes)
{
//Call base.RegisterRoutes so default CMS routes are registered
base.RegisterRoutes(routes);
// Register a route, which will make all url:s with 'shop/' before page names route to the page last in the list of page names
// For example, http://mySite/shop/News/ListOfNews/FirstNews/ will route to the 'FirstNews' page.
RouteTable.Routes.MapContentRoute(name: "customRoute",
url: "shop/{node}/{partial}/{action}",
defaults: new { action = "index" });
}
Custom route implementations
It is also possible to add custom System.Web.Routing.Route implementations. The order of the routes are important since the first route that handles a request will prevent that following routes get a chance to route the request. So when registering custom routes it is important to decide whether it should be registered before or after the default routes.
Thank you for the great post!
I have a question regardign modifying the default URL of EPiServer. Is there anyway to debug the original {node} segment? I tried to copy it and rename then add it in the global.asax but it does not work.
Regards,
Hai