I think you need to add in startup Configure Method
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(name: "Default", pattern: "{controller}/{action}/{id?}");
endpoints.MapControllers();
endpoints.MapRazorPages();
endpoints.MapContent();
});
Also I think you need to add FromBody or FromForm for the model parameter in method GuestCheckout
Hi Mark,
We had that code already in place but still having issues with the currentPage coming as null. We added this code, it fixes the issue, currentPage comes with a value, when calling actions:
Not sure if that's the best way to do it, taking into account that we will need to add every controller where we have the issue.
if you have a better solution please let us know.
Thanks in advance,
Hi guys,
I face very simillar case, in the second action, the currentPage is always null - I hope there's a generic solution for this issue.
I've tired the solution proposed by Mark (I've been missing only the MapControllerRoute), however it did not change the outcome.
public class EventPageController : PageController<EventPage>
{
[HttpGet]
public ActionResult Index(EventPage currentPage)
{
var model = PageViewModel.Create(currentPage);
// more code
return View(model);
}
[HttpGet]
[Route("[action]")]
public ActionResult ExportToICS(EventPage currentPage)
{
var result = ICSHelper.ExportToICal(currentPage);
// more code
return Content(result);
}
}
You need to find a mechanism to resolve the appropriate action result among multiple candidates. I solved this by creating a custom Matcher Policy.
public class CustomMatcherPolicy : MatcherPolicy, IEndpointSelectorPolicy
{
public override int Order => 100;
public bool AppliesToEndpoints(IReadOnlyList<Endpoint> endpoints) => true;
public Task ApplyAsync(HttpContext httpContext, CandidateSet candidates)
{
if (candidates.Count <= 1) return Task.CompletedTask;
for (int index = 0; index < candidates.Count; ++index)
{
var metadata = candidates[index].Endpoint.Metadata.GetMetadata<ActionDescriptor>();
if (metadata is ControllerActionDescriptor controllerActionDescriptor)
{
var lastSegment = httpContext.Request.Path.Value?.Split('/').Last();
var actionName = controllerActionDescriptor.ActionName;
if (lastSegment != null && lastSegment.Equals(actionName, StringComparison.InvariantCultureIgnoreCase))
{
candidates.SetValidity(index, true);
}
else
{
candidates.SetValidity(index,
actionName.Equals("Index", StringComparison.InvariantCultureIgnoreCase));
}
}
}
return Task.CompletedTask;
}
}
Don't forget to register it in the startup.cs file.
services.AddSingleton<MatcherPolicy, CustomMatcherPolicy>();
I don't think it's been asked, but is the view declaring a form with @Html.BeginContentForm or posting by other means?
Back to a few years ago, I used same Index action to serve both GET and POST, and currentPage was always populated, I know it feels like web form post back, but it works for me.
Thanks Francisco,
Your solution works, it fixes the issue I had including another scenarios.
Matthew, we're not using the @Html.BeginContentForn in some cases we're using @Html.BeginForm but sometimes it doesn't fully work so I use <form> in the view
Does anyone else experience that CMS UI gets completely broken after implementing CustomMatcherPolicy as Francisco suggested? Totally blank screen with no way of togglig between edit or admin modes. No navigation tree etc.?
In our controller we have httpGet and httpPost versions of Index and they work fine with this implementation. Search action, decorated with httpGet, however does not. Form that sends data back has been impemented as:
<form action="@Url.Action("Search")" method="get">
Does the placement of registration in the Startup.cs has to do with this UI behaviour? Is there any way of determining if request has been sent from edit mode apart from ContextModeResolver? It seems like that doesn't do any good in custom matcher policy implementation.
Hi Goran,
You might want to ignore URLs that start with "episerver" in your CustomMatcherPolicy implementation. For instance:
var candidateStates = new List<CandidateState>();
var actions = new List<ControllerActionDescriptor>();
for (int index = 0; index < candidates.Count; ++index)
{
candidateStates.Add(candidates[index]);
var action = candidates[index].Endpoint.Metadata.GetMetadata<ControllerActionDescriptor>();
if (action != null)
{
actions.Add(action);
}
}
if (!actions.Any(x =>
x.DisplayName != null &&
!x.DisplayName.StartsWith("episerver", StringComparison.InvariantCultureIgnoreCase)))
{
return Task.CompletedTask;
}
Thanks Francisco. That did the trick for CMS UI but I still cannot figure out why Search action receives currentPage param as null. This is basically the same issue as Karol Berezicki experiences.
Figured it out just now.
@Url.Action(...) that was the value of action attribute in our implementation of <form> HTML tag doesn't work anymore in .net core due to a different way that framework routes. Instead one should use @Url.ContentUrl(..) and then currentPage gets proper value in the Search action.
Hi All,
We´re migrating to Opti CMS 12, we ran into an issue when calling Controller's actions, currentPage parameter comes as null.
The first time we load a page, the currentPage parameter comes as expected
If we call an action in the same controller either it is a Get or a Post endpoint now the currentPage parameter comes always as null
Doeas anyone have an idea what the issue could be?
Any help is appreciate it.
Thanks in advance,