Copied to clipboard

Flag this post as spam?

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


  • João Roque 11 posts 95 karma points
    May 19, 2021 @ 08:18
    João Roque
    1

    Async Index method in Render Controller

    Hello,

    As far as I know it is not possible to perform route hijacking using a custom RenderController with an async Index method. I think it is very important to be able to do so because you usually need to load the view model with data from a DB source which in most frameworks uses async operations. You can make the async functions run sync but that is not a good practice. Is there something I'm missing? Will this be looked into in a further release?

    Meanwhile, while overriding the async Index is not possible, I'm thinking of putting my async DB functions on OnActionExecutionAsync, loading the models I need into a field on the controller and then just grabbing them in the Index method. But it would be much cleaner to do it all on Index().

    Thanks,

    João

  • David Armitage 510 posts 2081 karma points
    Jun 15, 2021 @ 07:42
    David Armitage
    0

    Hi Joao,

    Sorry I just saw this post. I wrote a blog about this a while back.

    This should be exactly what you are looking for. This is a guide how to do Route hijacking in Umbraco 9. https://www.umbrajobs.com/blog/posts/2021/june/umbraco-9-route-hijacking-custom-rendercontrollers-how-and-why/

    Let me know if you have any issues.

    Regards

    David

  • David Armitage 510 posts 2081 karma points
    Jun 15, 2021 @ 07:44
    David Armitage
    0

    Actually sorry. I didn't read correctly.

    This is more related to Async Root Hijacking.

    I will give this some more testing. Failing that this either needs logging as a bug or feature request. I would see more of a feature request than a bug.

    https://github.com/umbraco/Umbraco-CMS/issues?fbclid=IwAR2L5Dd-fjNtW251yi5Fzd-F1O6s2zhBizuhsqyptiAg4nIPDRvwVHfO6n8

    I will let you know if I manage to get this up and running.

    Regards

    David

  • João Roque 11 posts 95 karma points
    Jun 15, 2021 @ 08:12
    João Roque
    1

    Thanks for the answer David.

    As you show in your blog post Route Hijacking is still possible and easy to do in Umbraco 9, pretty much similar to Umbraco 8. The problem is if you want an async Index() method. The solution I mentioned in the orignal post works great and is not much of an hassle after all. But of course, it would be much better if we could just have an async Index() method.

    If you can put the feature request in it would be great as I have no experience doing so and I'm afraid I would mess up.

    Regards

  • Vitor Rodrigues 2 posts 74 karma points c-trib
    Jul 01, 2021 @ 20:44
    Vitor Rodrigues
    1

    Hi João,

    Ran into the same issue recently and created the following abstract class to work around the issue:

    public abstract class AsyncRenderController : RenderController
    {
        protected AsyncRenderController(ILogger<RenderController> logger, ICompositeViewEngine compositeViewEngine, IUmbracoContextAccessor umbracoContextAccessor) : base(logger, compositeViewEngine, umbracoContextAccessor)
        {
        }
    
        [NonAction]
        public sealed override IActionResult Index() => throw new NotImplementedException();
    
        public abstract Task<IActionResult> Index(CancellationToken cancellationToken);
    }
    

    The abstract class is of course not mandatory, but helps to keep the "hacky" construct hidden away. While not perfect it seems better than overriding OnActionExecutionAsync.

  • Matty 34 posts 148 karma points
    Oct 28, 2021 @ 10:50
    Matty
    0

    Hi Victor, are you able to explain exactly what this does / how it works. By marking the default, sync Index() as a [NonAction] does that route Index calls to the async Index() version?

  • Vitor Rodrigues 2 posts 74 karma points c-trib
    Oct 29, 2021 @ 07:31
    Vitor Rodrigues
    1

    That is exactly what happens.

    We mark the existing sync Index() as a [NonAction] so it isn't routable, and create a new async Index overload that will be used instead.

    The new overload needs to have a different method signature, so that's why we take a CancellationToken as a parameter, but it's fine to just not use it if not needed.

  • Arjan H. 226 posts 463 karma points c-trib
    Sep 07, 2022 @ 15:21
    Arjan H.
    1

    You can also implement an asynchronous controller method with the same name as the template alias. In that case the synchronous Index() method will not be used. For instance when the template alias is 'HomePage' the controller would look like this:

    public class HomePageController : RenderController
    {
        public HomePageController(
            ILogger<HomePageController> logger,
            ICompositeViewEngine compositeViewEngine,
            IUmbracoContextAccessor umbracoContextAccessor) : base(logger, compositeViewEngine, umbracoContextAccessor)
        {
        }
    
        public override IActionResult Index()
        {
            return base.Index();
        }
    
        public async Task<IActionResult> HomePage()
        {
            await SomeAsyncMethod();
            return CurrentTemplate(new ContentModel(CurrentPage));
        }
    }
    

    I've included the override for the Index() method for clarity, if you debug this controller you'll notice that the async HomePage() method is being used for the HomePage template instead of the Index() method.

    Edit: The name of the controller method should match the name of template alias (not the document type alias). I'm not sure, but this may not work when DisableAlternativeTemplates is set to true (see Web routing config).

  • Joe Begly 19 posts 110 karma points
    Mar 23, 2023 @ 16:59
    Joe Begly
    0

    I love this answer. For the past couple years I've been doing the abstract class and inheriting from this in my controllers, then overriding the async Index ActionResult, as indicated earlier on this topic. It works, but I hate boilerplate and prefer to avoid it when possible.

    This idea of making an async ActionResult with a method name that matches the template alias works like a charm for me.

Please Sign in or register to post replies

Write your reply to:

Draft