Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Nick Bennett 14 posts 84 karma points
    Mar 11, 2019 @ 12:36
    Nick Bennett
    0

    Registering Custom Routes in Umbraco 8

    In the documentation I see that the way to register a custom route is to create a class inheriting from Umbraco.Web.Mvc.UmbracoVirtualNodeRouteHandler and override its ApplicationStarted method.

    There are three issues:

    1. Where do I create this class? Anywhere?
    2. I need to override the FindContent method of the base class - but I'm not dealing with content, just trying to register a route: content doesn't come into it.
    3. The base class doesn't have an ApplicationStarted virtual method I can override.

    It looks as if this is no longer the correct base class to use for registering routes.

    How do I go about registering a custom route?

  • seanrock 99 posts 173 karma points
    Mar 17, 2019 @ 13:41
    seanrock
    0

    Just FYI, i started down this path also and i'm not sure it's the correct way.

    Seems you have to implement IComponent, then using composition (implement IUserComposer) add the component to the list of components. I created a folder Compositions and put the classes there.

    The IComponent has an Initialize() method you can use to setup the UmbracoApplication_ApplicationInit. Within that event handler you can specify your custom route and specify your custom UmbracoVirtualNodeRouteHandler (it doesn't have an ApplicationStarted method to override btw).

    In the route handler's FindContent i did use a node id for the umbracoContent.ContentCache.GetById() method. I just used a random node (home) but it did hit my custom controller and it did pass in my query parameters that i was expecting.

    hth

  • seanrock 99 posts 173 karma points
    Mar 17, 2019 @ 14:57
    seanrock
    0

    Correction, i used the node Id of a node that used a document type with the same name as the controller. Anything else and it doesn't hit the controller.

  • Marc Goodson 995 posts 6568 karma points MVP 4x c-trib
    Mar 17, 2019 @ 20:31
    Marc Goodson
    0

    Hi Nick

    You need to create a component, and register the route in the Initialize() method using the new MapUmbracoRoute RouteCollection extension method, then use a composer to add the component to the Umbraco composition.

    An Example:

    Add a c# class file in your project as per below, this example would map the custom route /products/details/123 to a custom controller called SuperProductController (that would need to inherit from RenderMvcController). The IPublishedContent model associated with the route comes from the UmbracoVirtualNodeRouteHandler (you can create your own logic as to what this should be based upon your request, but here I'm just hardcoding it to be the Id, of the products node in the starter kit!

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using Umbraco.Core.Composing;
    using Umbraco.Web;
    using Umbraco.Web.Mvc;
    
        namespace Umbraco8.Components
        {
    
                public class RegisterCustomRouteComposer : ComponentComposer<RegisterCustomRouteComponent>
                {
    
                }
    
                public class RegisterCustomRouteComponent : IComponent
                {
                    public void Initialize()
                    {
                        RouteTable.Routes.MapUmbracoRoute("Product Details", "product/details/{id}", new
                        {
                            controller = "SuperProduct",
                            action = "Details",
                            id = UrlParameter.Optional
                        }, new UmbracoVirtualNodeByIdRouteHandler(1105));
                    }
    
                    public void Terminate()
                    {
                        throw new NotImplementedException();
                    }
                }
            }
    

    Anyway hope that make sense!

    regards

    Marc

  • Nick Bennett 14 posts 84 karma points
    Mar 17, 2019 @ 20:50
    Nick Bennett
    0

    Yes, this seems to work. I am able to use a custom controller for front end functionality, but what I about backoffice functions? What I am trying to do is to create/edit some complicated data structures (railway timetables, which I am storing in the database as JSON) in the back office. I haven't yet managed to get a controller derived from UmbracoAuthorizedController to be instantiated and executed.

  • Marc Goodson 995 posts 6568 karma points MVP 4x c-trib
    Mar 17, 2019 @ 22:38
    Marc Goodson
    0

    Hi Nick

    Sorry I thought your question was regarding the changes in V8 for mapping a custom route through Umbraco using a VirtualNodeRoute handler...

    Yes, creating an api controller for use in the backoffice is different to this, but I don't think it has changed from V7, you would create an API controller that inherits from UmbracoAuthorizedApiController, with a PluginController attribute, and then this would be automatically routed via /umbraco/backoffice/{pluginname}/{controllername}/{action} etc

    For example:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Web.Http;
    using Umbraco.Web.Mvc;
    using Umbraco.Web.WebApi;
    
    namespace Umbraco8.Controllers
    {
        ///umbraco/backoffice/SuperTimeTables/TimeTableApi/GetTimeTableDetails
        [PluginController("SuperTimeTables")]
        public class TimeTableApiController : UmbracoAuthorizedApiController
        {
            [HttpGet]
            public TimeTable GetTimeTableDetails()
            {
                return new TimeTable() { Name = "test", Times = "9:00am" };
            }
            [HttpPost]
            public IHttpActionResult SaveTimetableInfo(TimeTable timetable)
            {
                if (timetable == null)
                {
                    return BadRequest("Timetable is null etc");
                }
                // do something to persist timetable
                return Ok();
            }
    
        }
        public class TimeTable
        {
            public string Name { get; set; }
            public string Times { get; set; }
    
        }
    }
    

    in this example the autorouting would be set up to the following url:

    /umbraco/backoffice/SuperTimeTables/TimeTableApi/GetTimeTableDetails

    regards

    Marc

  • Nick Bennett 14 posts 84 karma points
    Mar 21, 2019 @ 14:49
    Nick Bennett
    0

    It wasn't an API controller I had in mind. I was thinking in terms of a user interface allowing users to create and edit timetables.

    A timetable is a complicated thing with a grid of cells, with a variable number of columns (the UI should allow the user to insert columns). Not only are there times in the cells, but there are references to notes, and there is a cell at the top of each column just for references to notes. Actually, two grid - one for each direction. And then there's the notes themselves!

    Really struggling to see how to incorporate this sort of data into Umbraco! At the moment I've got it in tables in a separate database, but I've had to create the data programatically in a console app.

    What's the usual pattern for this sort of thing?

  • Marc Goodson 995 posts 6568 karma points MVP 4x c-trib
    Mar 21, 2019 @ 16:08
    Marc Goodson
    0

    Hi Nick

    Yes I sort of worked out what you meant after posting the API example ... :-(

    It's an unusual way to extend the backoffice functionality, general recommended way is AngularJS + APIControllers, but it is possible in V7! - I have a site that does just this, as long as controller inherits from UmbracoAuthorizedController, and you supply the route to work with it, then it's all ok.

    However I tried to replicate in V8 to provide you a further example and hit an error! (probably the same as you are seeing) so I've raised an issue on the Umbraco Issue Tracker:

    https://github.com/umbraco/Umbraco-CMS/issues/5005

    So hopefully we'll get an answer there, as it's one on my list to update the docs for - and of course you are trying to actually make it work!

    If you've got any further info to add, be ace if you could stick it on the issue.

    regards

    Marc

  • Marc Goodson 995 posts 6568 karma points MVP 4x c-trib
    Mar 26, 2019 @ 20:32
    Marc Goodson
    0

    Hi Nick

    Update on this.

    If you create your MVC Controller like so, inheriting from UmbracoAuthorizedController

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using Umbraco.Web.Mvc;
    
    namespace Umbraco8.Controllers
    {
        public class TimeTablesController : UmbracoAuthorizedController
        {
            // GET: TimeTables
            public ActionResult Index()
            {
                return View();
            }
        }
    }
    

    Then create a component to map the route and a composer to register the component with the Umbraco composition, and additionally register your controller with the DI implementation. eg:

    using System;
    using System.Web.Mvc;
    using System.Web.Routing;
    using Umbraco.Core;
    using Umbraco.Core.Composing;
    using Umbraco8.Controllers;
    
    namespace Umbraco8.Components
    {
        public class RegisterTimetableBackofficeMvcRouteComposer : IUserComposer 
        {
            public void Compose(Composition composition)
            {
                composition.Register<TimeTablesController>(Lifetime.Request);
                composition.Components().Append<RegisterTimetableBackofficeMvcRouteComponent>();
    
            }
        }
    
        public class RegisterTimetableBackofficeMvcRouteComponent : IComponent
        {
            public void Initialize()
            {
                RouteTable.Routes.MapRoute("TimetableRoute", "umbraco/backoffice/timetables/{action}/{id}", new
                {
                    controller = "TimeTables",
                    action = "index",
                    id = UrlParameter.Optional
                });
            }
    
            public void Terminate()
            {
                throw new NotImplementedException();
            }
        }
    }
    

    you can then request your backoffice MVC controller via /umbraco/backoffice/timetables/

    Apologies it took a while to work out!

    regards

    Marc

Please Sign in or register to post replies

Write your reply to:

Draft