0
votes

When calling

http://mysite.com/Project/42

for the routing and methods below I get a "Multiple actions were found that match the request...Get(Int32)...Children(Int32)"

while calling

http://mysite.com/Project/42/Children

returns correctly.

My routing is:

config.Routes.MapHttpRoute(
    name: "ForChildren",
    routeTemplate: "api/{controller}/{id}/Children",
    defaults: new { action = "Children" }
);

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

And my methods:

public class ProjectController : ApiController {

    public IEnumerable<Project> Get(int id){
        ...
    }

    [HttpGet]
    public IEnumerable<Project> Children(int id) {
        ...
    }
}

I thought not specifying the action would result in Webapi defaulting to the method (GET). Now it looks like Webapi is running through the methods disregarding their names, only looking for parameter match. I really don't believe that and think I fault elsewhere.

3

3 Answers

1
votes

This is a known behavior/issue with how action selection currently works in Web API. In the scenario where your request is 'http://mysite.com/Project/42', the request matches the 'DefaultApi' route and during action selection the action selector does not have the value for the require variable 'action' and hence it tries to figure out from the list of available actions which can be reached through GET method. It tries to find out the best action from the se based on route parameters (like 'id' here) and query string parameters. In your scenario, the action selector sees 2 get actions each accepting id parameter and hence it sees ambiguity in selecting them.

There is an issue currently filed here which is similar to your scenario: http://aspnetwebstack.codeplex.com/workitem/184

1
votes

A simple solution to this is to have only one route:

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}/{action}",
    defaults: new { id = RouteParameter.Optional, action = "DefaultAction"  }
);

Then decorate your default action:

public class ProjectController : ApiController {

    [ActionName("DefaultAction")]
    public IEnumerable<Project> Get(){
        // api/project
        // Return all projects
    }

    [HttpGet]
    public IEnumerable<Project> Children(int id) {
        // api/project/1/children 
        // Return children of project with id == 1
    }
}
0
votes

In Web API 2 you can use attribute routing:

[RoutePrefix("Project")]
public class ProjectController : ApiController {

    [Route("{id:int}")]
    public IEnumerable<Project> Get(int id){
        ...
    }

    [HttpGet]
    [Route("{id:int}/Children")]
    public IEnumerable<Project> Children(int id) {
        ...
    }
}