Umbraco

A practical example of route hijacking in Umbraco

Sebastiaan Janssen
Written by Sebastiaan Janssen

Sebastiaan takes you through how to make pages load faster by using route hijacking. Don't know what route hijacking is? Don't worry, you'll get introduced to that too.

You will get examples and descriptions of how and what to do, so buckle up and prepare to learn some awesome Umbraco tricks.

I've updated my personal website to Umbraco 7 and in order to make the homepage load a bit faster I thought it would be a good idea to introduce some paging so that the size of the page isn't so large.

I know I can hack paging as inline Razor script in the view but I thought I'd check out what the hype was all about with regards to route hijacking

What is route hijacking? Well, it's a way to tell Umbraco that you want to add custom code to certain document types. It can be compared to using partial classes in webforms to insert your own code in masterpages (but better! ;)).

The blog posts are just children of the blog page, as noted in my previous article. Which means that I just need a list of content items and do a bit of .Skip() and .Take() on them before rendering them on the page.

The examples in this post are very simple to keep it readable, but the full examples are available in a Gist as well.

So: this is MVC, which means I'll need three things, guess what they are?

Model

My model mainly consists of content, but also some properties to track the paging (total pages, current page, previous/next page number, etc). I can mix those in with the properties of the current page easily by making my model inherit from RenderBase and asking for the "CurrentPage" in the constructor method.

using Umbraco.Web;
using Umbraco.Web.Models;

namespace Cultiv.Models
{
    public class BlogOverview : RenderModel
    {
        public BlogOverview(IPublishedContent content) : base(content)
        { }
        
        public int Page { get; set; }
        
        // More custom properties
    }
}

View

The view will have access to all of our custom properties, one if which is the current page number (more on that later, in the controller). The output of the view below will be a header with the page number in it and the name of the current page (which will be "Blog" because this view is rendered on the "Blog" page).

I'm also using the layout that I've created for all the other pages, not using route hijacking. So everything is exactly the same except I have more properties to use. In the final version (not completely included for brevity, I can also do

foreach(var post in Model.PagedBlogPosts) { //etc }

Which is much nicer than

foreach(var post in Model.Content.Children.OrderByDescending(c => c.CreateDate).Take(5).Skip(0)) { //etc }

Note that in order for the view to understand whats going on, it needs to inherit from UmbracoViewPage with the type BlogOverview (again, this is the document type's alias). Here's an example of the view:

@using Cultiv.Models
@inherits UmbracoViewPage<BlogOverview>
@{
    Layout = "~/Views/Site.cshtml";
}
<h3>This is page number @Model.Page</h3>
<p>The current page's name is @Model.Content.Name</p>

Controller

I haven't explained how this route hijacking actually works. In this example, I'm making a controller with the same name as the document type's alias (BlogOverview). My template name is BlogOverview as well so the ActionResult method in this Controller is also named BlogOverview. In this case the document type alias and the template name are the same but if the template was named BlogPosts then the method would also need to be named BlogPosts.

Note that the Controller class name has to end with the world Controller (as usual with ASP.NET MVC) and inherit from Umbraco's RenderMvcController so that we get access to the current page and current template.

In this example I'll do some very simple processing: using model binding, the querystring value for the page number is set when the request comes in. If there's no querystring (?page=x is missing from the URL) then I'll set the current page number to 1. Remember I rendered the page number in my view earlier.

using System.Web.Mvc;
using Cultiv.Models;
using Umbraco.Web.Mvc;

namespace Cultiv.Controllers
{
    public class BlogOverviewController : RenderMvcController
    {
        public ActionResult BlogOverview(RenderModel model, int page = 1)
        {
            var blogOverviewModel = new BlogOverview(model.Content) { Page = page };

            // do paging 

            return CurrentTemplate(blogOverviewModel);
        }
    }
}

For brevity's sake, I've left out the boring paging but as you can see if you go to the homepage of this site then the paging at the bottom actually works. ;-)

I hope this was helpful in showing when route hijacking can come in handy: if you want to do some processing on the server but want to keep your model simple, make sure to consider executing the code in a controller and keep your view very simple (or "dumb" as people like to say).

The full code for the model view and controller are available in a Gist for you to peruse.

Note: Yes, I know I could also have used a SurfaceController with a ChildActionOnly Index method and rendered that from my template but I like exploring and route hijacking was new to me. Works pretty well for my purposes here!