One of the goals of the SDL Tridion Reference Implementation (TRI) is to provide an example web application for which ASP.NET MVC application developers can develop functionality without deep understanding of the underlying CMS. A key difference between a ‘classic’ MVC app and a CMS managed one is how URLs are managed and the impact this has on routing requests to controllers. In this article I dig into the mechanism used in the TRI to explain how it fits together.
Routing in ‘Classic’ MVC applications
In a ‘classic’ MVC application you use routing to map your application URLs to controllers, actions and parameters for those actions. For example the URL /blog/list/2014 might be routed to a Blog controller List action, with the year parameter set to 2014, which would populate a model containing a list of blog post summaries from the year 2014, and render this with an appropriate view. To view a blog post in detail, a URL such as /blog/view/2014-10-07-cat-bites-dog cpuld be used, again routed to the Blog controller View action with the article ID parameter set to 2014-10-07-cat-bites-dog. As such, the URL is primarily under the control of the developer, and any managed content (for example the blog articles themselves) usually can only influence a portion of the URL (in this case the date on which the blog post was written and/or the title of the post are used in parameters for the controller actions.
The flexibility of using a CMS
The greatest advantage of using a CMS like SDL Tridion, is that it gives the potential for editorial users to fully manage their websites without IT intervention, creating whole new sites, sections within a site, and adding pages by selecting a group of content items to show and a particular layout (template) for each item, and for the page as a whole. Provided that developers have created the schemas and templates up front, I can create blog content and pages wherever I like in my site, combine blog and other content items on the same page, or even have multiple blogs on the same site. Clearly there is a lot more flexibility possible here than in a ‘classic’ MVC application (where a developer/IT administrator would apply configuration of a Blog route for the application). So can I realize a fully CMS managed site with an MVC architecture? The answer is of course, Yes. Indeed using a CMS like Tridion which works with content on a component, rather than page level actually guides you to develop your application in a modular manner very much suited to the MVC approach.
The page route for a CMS MVC site
The main change is how routing is configured. In the TRI, instead of having multiple routes configured, mapping to different controllers, we have a single, greedy route which maps to the Page controller Page action, with pageUrl parameter set to the full URL of the request:
// Tridion Page Route
routes.MapRoute(
"Core_Page",
"{*pageUrl}",
new { controller = "Page", action = "Page" }
).DataTokens.Add("area", "Core");
Note that as the TRI uses ASP.NET MVC areas to separate our controllers and views, we need to add the area in which the standard Page Controller lives (Core) as a data token on the route.
Breaking the request into Sub Controller Actions
This controller action will load in the page data from the Broker database into a Sdl.Web.Common.Models.WebPage
model. This model consists of a set of regions, which are simply a grouping of related content items (Component Presentations). For example we might have content in 2 regions; Main and 3-Column, which would be rendered in separate parts of the page. The model is rendered by a Page view, which is related to the Page Template selected by the Editor. This page view provides basic page layout but does not render the content items. Instead it makes a call to a Region sub controller Region action, which renders the region with an appropriate Region View (a 3-column view would ensure that the items rendered are in rows of 3 items each). Again, the region view does not render the content items, but by default calls a further Entity sub controller Entity action which renders the content item with an Entity view related to the Component Template selected by the Editor.
So, in summary we have 3 out of the box controllers, each for rendering different levels of the Page hierarchy: Page, Region and Entity. Routing is used only to map every request to the Page controller, which splits up the responsibility for page rendering into first Region and then Entity controller actions. So how would a developer create some custom application logic? Well, the answer is in the way they normally would, by creating a custom Controller which pulls in the appropriate data into a Model and renders it with a View. The main difference is, that rather than thinking on a page level, you should create your controller and view logic with a modular mindset – this functionality should be controllable by an Editor and potentially be put on any page in the site, rather than with a fixed URL.
Mapping different content items to different Controllers, Actions and Views
The missing piece of the puzzle is how content items added to the page are mapped to your custom Controllers and Views. The answer is Component Template metadata. If you look at any TRI Component Template, you will notice that it uses the MVC Metadata metadata schema. The example below is from the List [3-Column] component template:
Here you can specify the following, which is enough to give you complete control over the controller, action and view used to render a part of the page.
- The Controller Name, prefixed with the Module (Area) name
- The Controller Action
- Additional Route Values (action parameters)
- The View Name, prefixed with the Module (Area) name
- The Region View Name, prefixed with the Module (Area) name
Page Templates also have metadata, but in this case we can only specify the View name (as the Controller/Action cannot be altered from the default greedy route).
Further Routing Options
There is nothing to stop you having routes for special, non-CMS managed URLs, or indeed have sections of your application managed with ‘classic’ ASP.NET MVC routes. For example in the reference implementation we use the route /sitemap.xml to map to our Navigation controller SiteMap action, which generates a Google Sitemap for the site. You could also implement some hybrid routing where the first part of the URL represents a page in the CMS, but latter parts are mapped to action parameters (for example /news/archive/2013 could map to a news listing page news/archive with the parameter to filter by year 2013) however that would require extending the Page Controller and having some route information available in your web application to understand these ‘special’ URLs. Remember of course to put any custom routes before the greedy page route in your global.asax.cs or similar application startup code.
Whats next?
Now that you understand how routing to controllers works in the TRI, its time to put it in to practice. A post will follow shortly giving the pattern and code examples for creating your own custom controllers.
Pingback: Tridion Reference Implementation Patterns 2: Mixing CMS Content With External Data | SDL Tridion Developer