Amanda Mitchell
Amanda Mitchell

Reputation: 2685

Why do certain parameter types cause ambiguous routing in ASP.NET Web API?

Suppose I have a very simple routing table, like this:

routes.MapHttpRoute("root", "",
    new { controller = "Home", action = "Index" });

And within my HomeController, I have two methods called Index:

[HttpGet]
public IHttpActionResult Index()
{
    return Content(HttpStatusCode.OK, new { service = "hard-coded string" });
}

[HttpGet]
public IHttpActionResult Index(string a)
{
    return Content(HttpStatusCode.OK, new { a });
}

If I run this application, routing occurs as I would expect: if I omit the a parameter from the query string, my request gets routed to the first method, and if I include it, the request is routed to the second method.

However, if I change the type of a to a more complex type, such as string[], then when I make a request on the default route, I get the following error (regardless of whether I specified the query parameter):

{
  Message: "An error has occurred."
  ExceptionMessage: "Multiple actions were found that match the request: Index on type SurveysApi.v1.Web.Controllers.HomeController Index on type SurveysApi.v1.Web.Controllers.HomeController"
  ExceptionType: "System.InvalidOperationException"
  StackTrace: " at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext) at System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken) at System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken) at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__0.MoveNext()"
}

The error remains if even if I specify FromUri or ModelBinder attributes on the parameter.

Why does this error occur for complex types, and is there any way to avoid it, short of specifying a simple type in the argument list and performing the necessary conversions in the controller method?

Upvotes: 2

Views: 1795

Answers (1)

Yushell
Yushell

Reputation: 745

Method overloading doesn't seem to play well with Web API. What I suggest you use instead is attribute routing.

Enable it in WebApiConfig.cs

config.MapHttpAttributeRoutes();

An example Controller would be:

public class HomeController : ApiController
{
    [Route("home/id/{Id}")]
    [HttpGet]
    public string Get(int Id)
    {
        return "id";
    }

    [Route("home/string/{str}")]
    [HttpGet]
    public string Get(string str)
    {
        return "string";
   }
}

Method names are the same, but routes are the key factor. Another way you could do this is using the ActionName attribute:

An example Controller would be:

public class HomeController : ApiController
{
    [ActionName("GetById")]
    public string Get(int id)
    {
        return "id";
    }

    [ActionName("GetByGUID")]
    public string Get(string id)
    {
        return "guid";
    }
}

Routes would be something like:

//Matches /api/Home/7
config.Routes.MapHttpRoute(
    name: "DefaultDigitApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { action = "GetById" },
    constraints: new { id = @"^\d+$" } // id must be digits
);

//Matches /api/Home/CD73FAD2-E226-4715-B6FA-14EDF0764162
config.Routes.MapHttpRoute(
    name: "DefaultGuidApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { action = "GetByGUID" },
    constraints: new { id = @"\b[A-F0-9]{8}(?:-[A-F0-9]{4}){3}-[A-F0-9]{12}\b" } // id must be guid
);

Hope this helps at least a bit.

Upvotes: 1

Related Questions