Reputation: 26354
I have an application that is running on the old ASP.NET MVC that I'm trying to port to ASP.NET Core 3.1. The old version works fine but in the .NET Core version I have problems matching the same URLs to the equivalent actions. I am using the MVC (AddMvc/UseMvc) with EndpointRouting disabled. Here is all the configuration (no explicit routing):
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(o =>
{
o.EnableEndpointRouting = false;
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMvc();
}
}
The controller actions would look like this:
[ApiController]
[Route("[controller]")]
public class SomeController : ControllerBase
{
[HttpPost]
public ActionResult DoSomething(bool real)
{
return Content("Something Done");
}
[HttpPost]
public ActionResult DoSomethingElse(bool unreal)
{
return Content("Something else done");
}
}
The URL (that, again, works in .NET MVC) is like this: /Some?real=true
This resolves to DoSomething
action in the .NET MVC and there's no issue. On ASP.NET Core, it throws an exception:
Microsoft.AspNetCore.Mvc.Infrastructure.AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied:
netcore.Controllers.SomeController.DoSomething (netcore)
netcore.Controllers.SomeController.DoSomethingElse (netcore)
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionSelector.SelectBestCandidate(RouteContext context, IReadOnlyList`1 candidates)
at Microsoft.AspNetCore.Mvc.Routing.MvcAttributeRouteHandler.RouteAsync(RouteContext context)
at Microsoft.AspNetCore.Routing.Tree.TreeRouter.RouteAsync(RouteContext context)
at Microsoft.AspNetCore.Routing.RouteCollection.RouteAsync(RouteContext context)
at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
But why? How would this be ambiguous? The boolean variables are clearly named differently and there shouldn't be any confusion. I know I can fix it by changing the route or method names, but I need to port the application without changing any of the underlying functionality/route/etc.
UPDATE: Simplified the action methods to have one boolean variables (with different variable name) and the ambiguity exception still occurs.
Upvotes: 1
Views: 373
Reputation: 27793
But why? How would this be ambiguous? The boolean variables are clearly named differently and there shouldn't be any confusion.
In this doc, you would find:
In Asp.Net Core, Controller and ApiController classes were unified into a single Controller class. Microsoft decided to no longer provide a mechanism to attempt to find the right method based on the querystring.
To achieve the requirement in ASP.NET Core, you can try to implement a custom ActionMethodSelectorAttribute
and apply it to your actions, like below.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class CheckQueryStingAttribute : ActionMethodSelectorAttribute
{
public string QueryStingName { get; set; }
public bool CanPass { get; set; }
public CheckQueryStingAttribute(string qname, bool canpass)
{
QueryStingName = qname;
CanPass = canpass;
}
public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
{
StringValues value;
routeContext.HttpContext.Request.Query.TryGetValue(QueryStingName, out value);
if (CanPass)
{
return !StringValues.IsNullOrEmpty(value);
}
return StringValues.IsNullOrEmpty(value);
}
}
Apply to actions
[ApiController]
[Route("[controller]")]
public class SomeController : ControllerBase
{
[HttpPost]
[CheckQuerySting("real", true)]
[CheckQuerySting("unreal", false)]
public ActionResult DoSomething(bool real)
{
return Content("Something Done");
}
[HttpPost]
[CheckQuerySting("unreal", true)]
[CheckQuerySting("real", false)]
public ActionResult DoSomethingElse(bool unreal)
{
return Content("Something else done");
}
}
Test Result
Upvotes: 4