How do I resolve the issue the request matched multiple endpoints in .Net Core Web Api

Magnus Wallström picture Magnus Wallström · Dec 11, 2019 · Viewed 29k times · Source

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)
{
    ...
}

Answer

Chris Pratt picture Chris Pratt · Dec 11, 2019

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 and 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)
{
    ...
}