Umbraco Routing

Written by on

At a recent Umbraco Glasgow meetup (GLUUG), we got chatting about the different ways we all “do Umbraco”. After giving a demo I was asked not about the main topic of the demo but the part I totally took for granted and glossed over - how I integrated my custom controller with Umbraco. It is really interesting seeing all the different ways we extend Umbraco so thought I’d explain a few of them I have used.

 

“Out the box” Umbraco

 

When you set up your solution, install the nuget package and go through the set up process, you get some basic page templates. The code given gets the Umbraco data using Razor, see the Umbraco Razor cheatsheet here.

 

For example, here is the blog page that pulls in data from sub nodes and displays some information from the blog posts.

 

<pre  style="font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(http://2.bp.blogspot.com/_z5ltvMQPaa8/SjJXr_U2YBI/AAAAAAAAAAM/46OqEP32CJ8/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;"><code style="color:#000000;word-wrap:normal;"> &lt;div class="row"&gt;  
@foreach(var post in CurrentPage.Children)
{
&lt;div class="col-sm-6"&gt;
&lt;div class="content equal"&gt;
&lt;a href="@post.Url"&gt;
&lt;div class="date"&gt;@post.CreateDate.ToLongDateString()&lt;/div&gt;
&lt;h2&gt;@post.Name&lt;/h2&gt;
&lt;p&gt;@Umbraco.Truncate(post.Introduction, 240, true)&lt;/p&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
}
&lt;/div&gt;
</code></pre>

 

Route Hijacking

 

Like other backend developers, having this kind of logic in the view files makes me a bit uncomfortable. I prefer to have any logic done in MVC controllers and passed to the front end, particularly when we are doing more than just presenting content from that node.

 

In Umbraco you can hijack the route so it will go into your controller rather than go straight to the Umbraco view. This is naming convention based, so if you want to hijack the BlogPostRepository document type route, you create a controller BlogPostRepositoryController, the Index action will be called and your custom logic used. See code sample below:

 

 

<pre  style="font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(http://2.bp.blogspot.com/_z5ltvMQPaa8/SjJXr_U2YBI/AAAAAAAAAAM/46OqEP32CJ8/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;"><code style="color:#000000;word-wrap:normal;">  public class BlogPostRepositoryController : Umbraco.Web.Mvc.RenderMvcController  
{
public override ActionResult Index(RenderModel model)
{
var viewModel = new BlogPostRepoModel();
viewModel.Title = "Hello";
//add logic with custom model before returning to view
return CurrentTemplate(viewModel);
}
}
</code></pre>

 

This article https://our.umbraco.org/documentation/reference/routing/custom-controllers can explain this way better than I can!

 

When trying to set up route hijacking to write this blog, I got:

 

 

Cannot bind source type UmbracoRoutesDemo.Models.BlogPostRepoModel to model type Umbraco.Web.Models.RenderModel

 

 

turns out there is a bug as of 7.4.2 but there is a work around provided...Thankfully this isn’t the option I use so it hasn't really affected me until now!

 

 

MVC actions:

 

Recently when working in Umbraco I use MVC actions. I let the Umbraco route go into the view as usual, then call an action on a specific controller.

 

<pre  style="font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(http://2.bp.blogspot.com/_z5ltvMQPaa8/SjJXr_U2YBI/AAAAAAAAAAM/46OqEP32CJ8/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;"><code style="color:#000000;word-wrap:normal;"> @inherits Umbraco.Web.Mvc.UmbracoTemplatePage  
@{
Layout = "Master.cshtml";
}
&lt;div role="content"&gt;
&lt;section class="light blogarchive equalizer"&gt;
&lt;div class="container"&gt;
&lt;div class="row"&gt;
@Html.Action("BlogOverviewPage", "Content", new { node = CurrentPage.Id })
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;/div&gt;
</code></pre>

 

This calls the BlogOverviewPage action on the ContentController. Within this method we can have any code we need to go to Umbraco nodes or any other sources (basically, anything you would put in any MVC controller). To get the Umbraco data I am using UmbracoContext.ContentCache.GetById(), not sure if there is another way to do this, but it seems to do the job. I have then put the data in strongly typed model and return to a separate view file. It follows the usual MVC convention for finding the view file based on controller and action name, for this example it will be in /Views/Content/BlogOverviewPage.cshtml, see code below:

 

<pre  style="font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(http://2.bp.blogspot.com/_z5ltvMQPaa8/SjJXr_U2YBI/AAAAAAAAAAM/46OqEP32CJ8/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;"><code style="color:#000000;word-wrap:normal;"> public class ContentController : SurfaceController  
{
public ActionResult BlogOverviewPage(int node)
{
var blogList = new List&lt;BlogPostModel&gt;();
var thisPage = UmbracoContext.ContentCache.GetById(node);
var childNodes = thisPage.Children.Where(x =&gt; x.DocumentTypeAlias == "BlogPost");
foreach (var blogPostNode in childNodes)
{
var blogPost = new BlogPostModel()
{
Introduction = blogPostNode.GetProperty("introduction").Value.ToString(),
Name = blogPostNode.Name,
Url = blogPostNode.Url
};
blogList.Add(blogPost);
}
BlogPostRepoModel viewModel = new BlogPostRepoModel()
{
Posts = blogList
};
return View(viewModel);
}
}
</code></pre>

 

Notice how this uses a SurfaceController rather than RenderMvcController like in the previous example. Again, the Umbraco docs can explain way better than I can.

Then we can have our view with a strongly typed model and it’s just like any MVC app, nothing Umbraco specific.

 

<pre  style="font-family:arial;font-size:12px;border:1px dashed #CCCCCC;width:99%;height:auto;overflow:auto;background:#f0f0f0;;background-image:URL(http://2.bp.blogspot.com/_z5ltvMQPaa8/SjJXr_U2YBI/AAAAAAAAAAM/46OqEP32CJ8/s320/codebg.gif);padding:0px;color:#000000;text-align:left;line-height:20px;"><code style="color:#000000;word-wrap:normal;"> @model UmbracoRoutesDemo.Models.BlogPostRepoModel  
&lt;div class="row"&gt;
@foreach (var post in Model.Posts)
{
&lt;div class="col-sm-6"&gt;
&lt;div class="content equal"&gt;
&lt;a href="@post.Url"&gt;
&lt;h2&gt;@post.Name&lt;/h2&gt;
&lt;p&gt;@post.Introduction&lt;/p&gt;
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt; }
&lt;/div&gt;
</code></pre>

 

One advantage I like of this approach is that we can cache each partial action separately if needed… just remember and flush the cache on publish of Umbraco or you will end up with issues on published content not showing.

 

Which is best? 

I don’t know the ‘right’ way to do things, and since I have been working with Umbraco I have tried a few different ways. It’s interesting to see the approaches other developers would take to the same problem.

 

If you have any feedback or you have a different way of using Umbraco, let me know!

 

Say hello on Twitter.