Mudbath Blog

16 December 2015

Do you have an Umbraco website with user generated content?

Read on to find out how to manage your URL structure just like MVC!

Umbraco does a fantastic job of managing the URL structure for most websites. However, developers who are more familiar with traditional MVC (Model View Controller) can find Umbraco quite frustrating to deal with, particularly when trying to work with other data sources and complex logic.

In this article we’re going to discuss how to do a basic Umbraco controller hijack, and then add custom routes and values to our hijack. For a complex website (particularly user generated content), this is going to make your life a lot easier.

Standard Route Hijacking in Umbraco

In order to hijack a normal Umbraco route, the first step is to make a new controller and name it the same as the document type you wish to intercept. In MVC speak, the Umbraco document type is the route and the template is the action. If the template you are intercepting is not specified in the controller, it will route through the default Index action.

namespace CustomRoutesApp.Site.Controllers

{

    public class CustomRouteController: RenderMvcController

    {

        public ActionResult CustomTemplate(RenderModel model)

        {

            var newModel = new CustomRouteRenderModel();

            newModel.NodeId = 1111;

            return base.Index(newModel);

        }

    }

} 

Note that the controller inherits RenderMvcController, and passes a RenderModel to the ActionResult.

To add your own custom values, make your own model that inherits the RenderModel. Now you can define any values you want in the model and pass them through to the view script.

namespace CustomRoutesApp.Site.Models

{

    public class CustomRouteRenderModel: RenderModel

    {

        public CustomRouteRenderModel() : this(new 

UmbracoHelper(UmbracoContext.Current).TypedContent(UmbracoContext.Current.PageId)) { }

        public CustomRouteRenderModel(IPublishedContent content) : base(content) { }

        public int NodeId { get; set; }

        public IPublishedContent Node { get; set; }

    }

} 

In the view script all you need to do is inherit UmbracoViewPage and declare your render model instead of inheriting UmbracoTemplatePage.

@inherits 

Umbraco.Web.Mvc.UmbracoViewPage

<h1>@Model.NodeId</h1>

Adding Custom Route Data

So now we have our controller hijack, let’s say we want to pass through the node of the item we clicked on as an Id in the uURL – as you would in a regular MVC application. The first thing you want to do is override the UmbracoApplication Application_Start in the Global.asax.cs. The only tricky bit with this is that we need to ensure that the Global.asax inherits the codebehind.

<%@ Application Inherits="CustomRoutesApp.Site.Global" CodeBehind="Global.asax.cs" Language="C#" %> 

Now we add the custom route to the routeData. This needs a unique name, the route, the controller and action and the namespace, the controller exists in. In this example we are passing an id to the controller.

namespace CustomRoutesApp.Site

{

    public class Global: UmbracoApplication

    {

        protected override void OnApplicationStarted(object sender, EventArgs e)

        {

            RouteTable.Routes.MapUmbracoRoute("CustomRoute", "my/custom/route/{id}", new

            {

                controller = "CustomRoute",

                action = "CustomTemplate",

                id = UrlParameter.Optional

            }, new UmbracoVirtualNodeByIdRouteHandler(1120));

            base.OnApplicationStarted(sender, e);

        }

    }

} 

Note: Because we need to pass through node Id’s, I recommend not hardcoding these values. For the sake of this example I won’t bother.

Finally we just need to go back to the controller and get the id that has been passed through to it. This can be retrieved in two ways so for this example we get it in both ways.

public ActionResult CustomTemplate(RenderModel model, string id)

        {

            var newModel = new CustomRouteRenderModel();

            var routeId = RouteData.Values["id"].ToString();

            if (routeId != null && routeId == id)

            {

                newModel.NodeId = Convert.ToInt32(routeId);

            }

            

            return base.Index(newModel);

        }

Now you can pass through any value through the URL without having to use a query string, making your routes and your code look cleaner and easier to maintain.

Happy coding!