haseebahmed7 haseebahmed7 - 2 months ago 25
ASP.NET (C#) Question

Unusual [RoutePrefix] behavior in ASP.NET WebAPI

I have two Controllers as follows:

[RoutePrefix("v1/user/something")]
public class SomethingsController : ApiController
{
[Route("{id}")]
[HttpGet]
[ResponseType(typeof(SomethingsViewModel))]
public async Task<IHttpActionResult> GetAsync([FromUri]int id)
{
}
}

[RoutePrefix("v1/user")]
public class UserController : ApiController
{
[Route("{id}")]
[HttpGet]
[Authorize(Roles = "Super Admin")]
public async Task<IHttpActionResult> GetByIdAsync([FromUri]int id)
{
}
}


Now by looking at the code above, I'd think that the following two routes are being created:


  • v1/user/something/{id}

  • v1/user/{id}



But unfortunately, for some reason, that is not the case. I keep getting the following exception message when trying to access one of the above routes:


Multiple controller types were found that match the URL. This can happen if attribute routes on multiple controllers match the requested URL.
The request has found the following matching controller types: MyProject.Api.Controllers.UserController, MyProject.Api.Controllers.SomethingsController


Please help me out in figuring what I might be doing wrong or which small detail am I missing out here.

Answer

Though their route prefix are different their resolved routes match. for example v1/user/{id} will match v1/user/something/{id} where id parameter arg in the first route will take something/{id}.

Route prefix and Route attributes combine to create a full route that is added to the route table.

In a case like this you will need to use constraints in order to better differentiate the routes.

[RoutePrefix("v1/user/something")]
public class SomethingsController : ApiController {
    [Route("{id:int}")]
    [HttpGet]
    [ResponseType(typeof(SomethingsViewModel))]
    public async Task<IHttpActionResult> GetAsync([FromUri]int id) { ... }
}

[RoutePrefix("v1/user")]
public class UserController : ApiController {
    [Route("{id:int}")]
    [HttpGet]
    [Authorize(Roles = "Super Admin")]
    public async Task<IHttpActionResult> GetByIdAsync([FromUri]int id) { ... }
}

So now with the int constraint something wont be mistaken for valid parameter for the UserController.GetByIdAsync action

Reference Attribute Routing in ASP.NET Web API 2: Route Constraints

Route Constraints

Route constraints let you restrict how the parameters in the route template are matched. The general syntax is "{parameter:constraint}". For example:

[Route("users/{id:int}"]
public User GetUserById(int id) { ... }

[Route("users/{name}"]
public User GetUserByName(string name) { ... }

Here, the first route will only be selected if the "id" segment of the URI is an integer. Otherwise, the second route will be chosen.

Comments