I notice that there are a bunch of similar questions out there about this topic.
I'm getting this error when calling any of the methods below.
Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints.
I can't however sort out what is best practice in resolving the issue. So far I haven't set up any specific routing middleware.
// api/menus/{menuId}/menuitems [HttpGet("{menuId}/menuitems")] public IActionResult GetAllMenuItemsByMenuId(int menuId) { .... } // api/menus/{menuId}/menuitems?userId={userId} [HttpGet("{menuId}/menuitems")] public IActionResult GetMenuItemsByMenuAndUser(int menuId, int userId) { ... }
ASP.NET Core apps should be designed to process many requests simultaneously. Asynchronous APIs allow a small pool of threads to handle thousands of concurrent requests by not waiting on blocking calls. Rather than waiting on a long-running synchronous task to complete, the thread can work on another request.
Endpoints are the app's units of executable request-handling code. Endpoints are defined in the app and configured when the app starts. The endpoint matching process can extract values from the request's URL and provide those values for request processing.
Specific Type as the Return type in ASP.NET Core Web API We can return any type of primitive data like string, integer, Boolean, etc., or complex data like Employee, Product, etc, directly from the controller action method.
This article explains OData Endpoints in the Web API. OData is a web protocol for accessing the data. It uses web technologies such as Atom Publishing, HTTP and JSON. Atom Publishing is also called AtomPub. We can easily create an OData Endpoint by the Web API OData.
What you're trying to do is impossible because the actions are dynamically activated. The request data (such as a query string) cannot be bound until the framework knows the action signature. It can't know the action signature until it follows the route. Therefore, you can't make routing dependent on things the framework doesn't even know yet.
Long and short, you need to differentiate the routes in some way: either some other static path or making the userId
a route param. However, you don't actually need separate actions here. All action params are optional by default. Therefore, you can just have:
[HttpGet("{menuId}/menuitems")] public IActionResult GetMenuItemsByMenu(int menuId, int userId)
And then you can branch on whether userId == 0
(the default). That should be fine here, because there will never be a user with an id of 0
, but you may also consider making the param nullable and then branching on userId.HasValue
instead, which is a bit more explicit.
You can also continue to keep the logic separate, if you prefer, by utilizing private methods. For example:
[HttpGet("{menuId}/menuitems")] public IActionResult GetMenuItems(int menuId, int userId) => userId == 0 ? GetMenuItemsByMenuId(menuId) : GetMenuItemsByUserId(menuId, userId); private IActionResult GetMenuItemsByMenuId(int menuId) { ... } private IActionResult GetMenuItemsByUserId(int menuId, int userId) { ... }
Action routes need to be unique to avoid route conflicts.
If willing to change the URL consider including the userId in the route
// api/menus/{menuId}/menuitems [HttpGet("{menuId:int}/menuitems")] public IActionResult GetAllMenuItemsByMenuId(int menuId) //.... } // api/menus/{menuId}/menuitems/{userId} [HttpGet("{menuId:int}/menuitems/{userId:int}")] public IActionResult GetMenuItemsByMenuAndUser(int menuId, int userId) { //... }
##Reference Routing to controller actions in ASP.NET Core
##Reference Routing in ASP.NET Core
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With