I am migrating controllers from .NET Framework to .NET Core and I want to be compatibility with API calls from previous version. I have problem with handling multiple routes from Query Params.
My example controller:
[Route("/api/[controller]")]
[Route("/api/[controller]/[action]")]
public class StaticFileController : ControllerBase
{
[HttpGet("{name}")]
public HttpResponseMessage GetByName(string name)
{
}
[HttpGet]
public IActionResult Get()
{
}
}
Calling api/StaticFile?name=someFunnyName
will lead me to Get()
action instead of expected GetByName(string name)
.
What I want to achieve:
api/StaticFile
-> goes to Get()
actionapi/StaticFile?name=someFunnyName
-> goes to GetByName()
actionMy app.UseEndpoints()
from Startup.cs
have only these lines:
endpoints.MapControllers();
endpoints.MapDefaultControllerRoute();
If I use [HttpGet]
everywhere and add ([FromQuery] string name)
it gets me AmbiguousMatchException: The request matched multiple endpoints
Thank you for your time to helping me (and maybe others)
What I want to achieve:
- Calling GET api/StaticFile -> goes to Get() action
- Calling GET api/StaticFile?name=someFunnyName -> goes to GetByName() action
To achieve above requirement of matching request(s) to expected action(s) based on the query string, you can try to implement a custom ActionMethodSelectorAttribute and apply it to your actions, like below.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class QueryStringConstraintAttribute : ActionMethodSelectorAttribute
{
public string QueryStingName { get; set; }
public bool CanPass { get; set; }
public QueryStringConstraintAttribute(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 (QueryStingName == "" && CanPass)
{
return true;
}
else
{
if (CanPass)
{
return !StringValues.IsNullOrEmpty(value);
}
return StringValues.IsNullOrEmpty(value);
}
}
}
Apply to Actions
[Route("api/[controller]")]
[ApiController]
public class StaticFileController : ControllerBase
{
[HttpGet]
[QueryStringConstraint("name", true)]
[QueryStringConstraint("", false)]
public IActionResult GetByName(string name)
{
return Ok("From `GetByName` Action");
}
[HttpGet]
[QueryStringConstraint("name", false)]
[QueryStringConstraint("", true)]
public IActionResult Get()
{
return Ok("From `Get` Action");
}
}
Test Result