asked    Sylvia     2018-10-12       c#       20 view        1 Answer

[SOLVED] Proper way to route to controllers in Umbraco ASP.NET / IApplicationEventHander vs ApplicationEventHandler vs RouteConfig.cs, RenderMvcController etc

I have a Solution structure like this:

MyApp.Core
--Properties
--References
--bin
--Events
  |EventHandlers.cs
--Directory
  --Controllers
    |DirectoryController.cs
  --Helpers
    |ContextHelpers.cs
  --Models
    |DirectoryModel.cs
--AnotherSite
  --Controllers
  --Helpers
  --Models
  --Services
--Shared
  --Controllers
    |HomePageController.cs
  --Helpers
    |Extensions.cs
|app.config
|packages.config

MyApp.Umbraco
--Properties
--References
--bin
etc........
--Views
  --Directory
    --Partials
      |DirectoryFilters.cshtml
    |DirectoryBase.cshtml
    |DirectoryHome.cshtml
    |FDirectory.cshtml
    |SDirectory.cshtml
  --Partials
  --Shared
  |Base.cshtml
  |Web.config
etc........

My Umbraco instance uses the models and controllers from my "Core" project. There is nested directory structure, because of multiple websites in one installation, in the "Core", and also in the "Views" directory in the Umbraco instance.

I am still fairly noob to .NET MVC, and I understand route hijacking, but the documentation for Umbraco's routing is slim. I have the following:

EventHandlers.cs

namespace MyApp.Core.Events
{
    /// <summary>
    /// Registers site specific Umbraco application event handlers
    /// </summary>
    public class MyAppStartupHandler : IApplicationEventHandler
    {
        public void OnApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {

        }

        public void OnApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
            RegisterCustomRoutes();
        }

        public void OnApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {

        }

        private static void RegisterCustomRoutes()
        {
            // Custom Routes
            RouteTable.Routes.MapUmbracoRoute(
                "FDirectory",
                "fdirectory/{id}",
                new
                {
                    controller = "Directory",
                    action = "FDirectory",
                    id = UrlParameter.Optional
                },
                new PublishedPageRouteHandler(1000));

            RouteTable.Routes.MapUmbracoRoute(
                "SDirectory",
                "sdirectory/{id}",
                new
                {
                    controller = "Directory",
                    action = "SDirectory",
                    id = UrlParameter.Optional
                },
                new PublishedPageRouteHandler(1001));

            RouteTable.Routes.MapUmbracoRoute(
                "HomePage",
                "",
                new
                {
                    controller = "HomePage",
                    action = "Index",
                    id = UrlParameter.Optional
                },
                new PublishedPageRouteHandler(1002));
        }
    }

    public class PublishedPageRouteHandler : UmbracoVirtualNodeRouteHandler
    {
        private readonly int _pageId;

        public PublishedPageRouteHandler(int pageId)
        {
            _pageId = pageId;
        }

        protected override IPublishedContent FindContent(RequestContext requestContext, UmbracoContext umbracoContext)
        {
            if (umbracoContext != null)
            {
                umbracoContext = ContextHelpers.EnsureUmbracoContext();
            }

            var helper = new UmbracoHelper(UmbracoContext.Current);

            return helper.TypedContent(_pageId);
        }
    }
}

DirectoryController.cs

namespace MyApp.Core.Directory.Controllers
{
    public class DirectoryController : RenderMvcController
    {
        public DirectoryController() : this(UmbracoContext.Current) { }
        public DirectoryController(UmbracoContext umbracoContext) : base(umbracoContext) { }

        public ActionResult FDirectory(RenderModel model)
        {
            return CurrentTemplate(new DirectoryModel(model.Content));
        }

        public ActionResult SDirectory(RenderModel model)
        {
            return CurrentTemplate(new DirectoryModel(model.Content));
        }
    }
}

So Umbraco does not install with an App_Start folder. I would like to know what the best approach is for a multi-site installation of Umbraco for registering the routes to the controllers. My implementation works, but it seems like I shouldn't have to create actions for every single page I am going to have in a site, in every controller. I know Umbraco has its own routing, so using Umbraco concepts, ASP.NET MVC concepts, and whatever else is available, what is the best way to implement this type of solution structure? Should I even worry about using a RouteConfig.cs and create a App_Start directory? Or is what I am doing the best approach? Should I use IApplicationEventHandler or ApplicationEventHandler?

Also, I have to hard code the node ID's. I've read that there is a way to Dynamically? And example of this would be great.

Examples of the best way to implement a structured multi-site Umbraco MVC solution is what I am asking for I guess, in regards to routing the controllers, with some detail, or links to strong examples. I have searched and researched, and there are bits and pieces out there, but not really a good example like what I am working with. I am going to have to create a RouteMap for every single page I create at this point, and I don't know if this is the most efficient way of doing this. I even tried implementing a DefaultController, but didn't see the point of that when your solution is going to have multiple controllers.

  1 Answer  

        answered    Joyce     2018-10-12      

I'm not entirely sure what you are trying to achieve with this, but I'll try to explain how it works and maybe you can clarify afterwards.

I assume you have the basics of Umbraco figured out (creating document types + documents based on the document types). This is how Umbraco is normally used and it will automatically do routing for you for each of these "content nodes" (documents) you create in a site.

So create a document named document1 and it will be automatically routed in your site at URL: . By default this document will be served through a default MVC controller and it will all take place behind the scenes without you having to do anything.

Route hijacking allows you to override this default behavior and "shove in" a controller that lets you interfere with how the request is handled. To use hijacking you create a RenderMvcController with the alias of your document type. That could be HomePageController : RenderMvcController.

This controller should have an action with the following signature:

public override ActionResult Index(RenderModel model)

In this action you are able to modify the model being sent to the view in any way you like. That could be - getting some external data to add on to the model or triggering some logic or whatever you need to do.

This is all automatically hooked up by naming convention and you will not have to register any routes manually for this to work.

The other type of Umbraco MVC controller you can create is a SurfaceController. This one is usually used for handling rendering of child actions and form submissions (HttpPost). The SurfaceController is also automatically routed by Umbraco and will be located on a "not so pretty" URL. However since it is usually really not used for anything but rendering child actions and taking form submits, it doesn't really matter what URL it is located at.

Besides these auto-routed controllers you are of course able to register your own MVC controllers like in any standard MVC website. The one difference though is that unlike a normal ASP.NET MVC website, an Umbraco site does not have the automagical default registration of controllers allowing the routing to "just work" when creating a new controller.

So if you want to have a plain old MVC controller render in an Umbraco site without it being related to a document/node in Umbraco, you would have to register a route for it like you would do in any other MVC site. The best way of doing that is to hook in and add it to the Routes using an ApplicationEventHandler class. That will automatically be triggered during application startup - essentially allowing you to do what you would normally do in App_Start.

Just to be clear though - if you plan on using data from Umbraco, you should not be using normal MVC controllers and should not require any manual route registration to be done. You usually want to render a template/view in context of a document/node created in Umbraco (where you can modify data/properties of the document) and then the route hijacking is the way to go.


From what it looks like, it could seem that the correct way to do what you are trying to do is to simply create two document types:

FDirectory and SDirectory

You click to allow both of these to be created in root and then you create documents called FDirectory and SDirectory and they will be automatically routed on these URLs. Creating a RenderMvcController's called FDirectoryController : RenderMvcController will then make sure it is used to hijack the routing whenever that page is requested.

If you're simply trying to set up a multi-site solution I would suggest you create a Website document type and create a node for each site you want, in the root of your Umbraco content tree. Right click each of these nodes and edit the hostname to be whatever you need it to be. This can also be some "child url" like /fdirectory or /sdirectory in case you need to test this on localhost without using multiple hostnames.

Hope this gives you the pointers needed, otherwise try to explain what you are trying to do and I'll see if I can refine my answer a bit!





Your Answer





 2018-10-12         Asa

Populate Umbraco content form with Javascript

I trying to create a custom property editor for Umbraco 7 that talks to an external web service, retrieves some data then populates a number fields in the form with the data it's retrieved. I've tried doing this with the following simple code:$("#textbox_id").val("new value");This does indeed populate the correct field with the correct data. However if I save and reload the form the data has not been updated and value returns to it's original value.Any suggestions? The problem is that you are using jQuery to update the input field directly in the DOM. The backoffice of U...
 javascript                     1 answers                     0 view
 2018-10-12         Gordon

Set date for Umbraco date picker using jQuery

Am creating a button for an Umbraco back-end content editing page that will retrieve a load of data from another database and auto-populate a few of the fields. However some of the fields use the Umbraco datepicker. I can change the value of the text box but when I click to view the datepicker calendar, it has the wrong date selected. How can I set the date of the datpicker so it matches what's in the text box rather than just the change what's in the textbox?Thanks Think I've got it. Once change value of textbox call .change() on the text box and this will update the da...
 jquery                     1 answers                     1 view
 2018-10-12         Stephanie

Jquery Ajax Call to send data to the method when the a href clicked

I am trying to send the language type clicked from my master page into the surfacecontroller in the App_code folder. My HTML is like this below:<div class="navbar-topbar clearfix"> <div class="h5 pull-right"> <span class="linkCA"> <strong>CA</strong> <a class="lang=en-CA" href="@NewUrlLink">EN</a> | <a class="lang=fr-CA" href="@NewUrlLink">FR</a> </span> <span class="linkUS"> <strong>US</strong> <a class="lang=e...
 javascript                     1 answers                     2 view