Migrating partial classes of same controller to Optimizely 12

ZZ
ZZ
Vote:
 

Hi,

In our current EpiServer ver. 11 we are having partial classes of startpage controller. Each partial class contains different action methods and some only returns JSON. 

After migrating to Optimizely 12 the index method is not get called as default  ->

https://localhost:5001 should call index method of startpage but its calling some other action method of the same controller which return Json

If i write https://localhost:5001/index then the index method is called 

Or said in other words, how can I make following code work -> 

public class StartPageController : PageControllerBase<StartPage>
{
    [HttpGet]
    public IActionResult GetDTO(string id)
    {
        return Json(new { id });
    }
    [HttpGet]
    public IActionResult Index(StartPage currentPage)
    {
        var model = PageViewModel.Create(currentPage);

        // Check if it is the StartPage or just a page of the StartPage type.
        if (SiteDefinition.Current.StartPage.CompareToIgnoreWorkID(currentPage.ContentLink))
        {
            // Connect the view models logotype property to the start page's to make it editable
            var editHints = ViewData.GetEditHints<PageViewModel<StartPage>, StartPage>();
            editHints.AddConnection(m => m.Layout.Logotype, p => p.SiteLogotype);
            editHints.AddConnection(m => m.Layout.ProductPages, p => p.ProductPageLinks);
            editHints.AddConnection(m => m.Layout.CompanyInformationPages, p => p.CompanyInformationPageLinks);
            editHints.AddConnection(m => m.Layout.NewsPages, p => p.NewsPageLinks);
            editHints.AddConnection(m => m.Layout.CustomerZonePages, p => p.CustomerZonePageLinks);
        }
        return View(model);
    }
}

Any help would be appreciated 

#291939
Edited, Nov 19, 2022 8:34
Vote:
 

Hi,

As strange as this may sound, reordering your actions to put the index action first should resolve the issue.

#292016
Nov 21, 2022 9:22
ZZ
Vote:
 

Thanks for the input. But how can I then have partial classes of StartPageController ?

#292181
Nov 24, 2022 20:22
Vote:
 

Ah. I see the problem. I'd missed the "partial" bit when I read the original question. To summarise, you can't control the order of the methods as they exist in partial classes in different files and so you can't ensure the index method comes first.

I've recreated the issue and it looks as though both actions are matching the same route:
{controller=Home}/{action=Index}/{id?}

I think, to resolve the issue, you'll need to override the mechanism to select the appropriate action. You could do this with an action constraint which, for each action, would allow you to determine whether it's the most appropriate one to handle your request. I've done that by attaching a "priority" to each action and picking the action with the lowest value here:

[AttributeUsage(AttributeTargets.Method)]
public class ActionPriorityAttribute : Attribute, IActionConstraint
{
    public ActionPriorityAttribute(int priority = 0)
    {
        Priority = priority;
    }

    public int Priority { get; set; }

    public int Order => 0;

    //Return a bool indicating whether to use this action
    public bool Accept(ActionConstraintContext context)
    {
        //if the priority is 0, we can't beat that so use this action
        //similarly, if this is the only action, just use it
        if (Priority == 0 || context.Candidates.Count == 1)
            return true;

        //If this action has the lowest "priority" value of all actions, return true to use this action
        //otherwise return false so we can choose the action with the lower value when we get to it
        return !context.Candidates.Any(x => ((x.Action.ActionConstraints.FirstOrDefault(f => f.GetType() == typeof(ActionPriorityAttribute)) as ActionPriorityAttribute)?.Priority ?? 10000) < Priority);
    }
}

You can then apply it to your controller like this:

public class HomeController : PageController<HomePage>
{
    [ActionPriority(10)]
    [HttpGet("getdto")]
    public IActionResult GetDTO(string id)
    {
        return Json(new { id });
    }

    [ActionPriority(0)]
    [HttpGet()]
    public IActionResult Index(HomePage currentContent) => View(ContentViewModel.Create<HomePage>(currentContent));
}
#292443
Nov 30, 2022 10:59
Vote:
 

Hi Paul,

Thank you for sharing it. I just modified the code a bit to validate for nulls of `ActionConstraints`

[AttributeUsage(AttributeTargets.Method)]
    public class ActionPriorityAttribute : Attribute, IActionConstraint
    {
        public ActionPriorityAttribute(int priority = 0)
        {
            Priority = priority;
        }

        public int Priority { get; set; }

        public int Order => 0;

        //Return a bool indicating whether to use this action
        public bool Accept(ActionConstraintContext context)
        {
            //if the priority is 0, we can't beat that so use this action
            //similarly, if this is the only action, just use it
            if (Priority == 0 || context.Candidates.Count == 1)
                return true;

            //If this action has the lowest "priority" value of all actions, return true to use this action
            //otherwise return false so we can choose the action with the lower value when we get to it
            return !context.Candidates.Any(x => ((x.Action.ActionConstraints?.FirstOrDefault(f => f.GetType() == typeof(ActionPriorityAttribute)) as ActionPriorityAttribute)?.Priority ?? 10000) < Priority);
        }
    }
#297078
Feb 23, 2023 17:20
Vote:
 

Hi

After upgrading from CMS 11 to 12, we noticed that URL.Action didn't work anymore for action methods on the page controller. We then implemented the workaround described here: https://world.optimizely.com/forum/developer-forum/cms-12/thread-container/2022/8/url-action-does-not-work-in-cms-12/ .

However, after a few months (!), we ran into the same issue you described here: all of a sudden, on page load, the index method wasn't called, but some random action method on that page controller was, which resulted in nullreference exceptions. For more info: see my post in the linked thread.

Our current conclusion is that there's a reason Url.Action doesn't work out of the box anymore for methods on content controllers: you're supposed to put the action methods on a separate controller.

If there's any feedback on this, please share. It was a tricky issue to debug, hope to save someone else the time :).

Kr

Lander

#330557
Edited, Sep 26, 2024 7:49
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.