19

I have Users controller and basic REST pattern is working just fine. However I need one additional pattern users/{id}/usergroups that will return all user groups for that user.

What would be the best way to implement this since I imagine I will need similar routes on many more controllers. Just default ones are not enough...

Error

Multiple actions were found that match the request: Api.Models.Users.User GetUser(Int32) on type Api.Controllers.UsersController System.Collections.Generic.IEnumerable`1[Api.Models.Users.UserGroup] GetUserGroups(Int32) on type Api.Controllers.UsersController

Code

// GET api/Users
public IEnumerable<User> GetUsers()

// GET api/Users/5
public User GetUser(int id) // THIS IS CONFLICT 1

// PUT api/Users/5
public HttpResponseMessage PutUser(int id, User user)

// POST api/Users
public HttpResponseMessage PostUser(User user)

// DELETE api/Users/5
public HttpResponseMessage DeleteUser(int id)

// GET api/Users/5/UserGroups
public IEnumerable<UserGroup> GetUserGroups(int id)  // THIS IS CONFLICT 2

Edit 1

I did what amhed suggested and it doesn't solve the issue.

// GET api/Users/5
[HttpGet, ActionName("getuser")]
public User GetUser(int id) // THIS STILL DOES NOT WORK

// GET api/Users/5/UserGroups
[HttpGet, ActionName("usergroups")]
public IEnumerable<UserGroup> GetUserGroups(int id) // THIS WORKS

// ROUTES
config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "{controller}/{id}/{action}",
    defaults: new { id = RouteParameter.Optional, action = RouteParameter.Optional }
);
1
  • Can I see your HTTP Request? what is the error? route not found? Commented Jul 7, 2013 at 21:37

2 Answers 2

19

You can either: just define one Get method, and have an optional Id Parameter like this:

public IEnumerable<User> GetUsers(int? id){
    if (id.HasValue)
    {
         //return collection of one item here
    }
    //return collection of all items here
}

Or you can have multiple Gets decorated with the ActionName Attribute

// GET api/Users
[ActionName("GetAll")]
public IEnumerable<User> GetUsers()

// GET api/Users/5
[ActionName("Get")]
public User GetUser(int id) // THIS IS NO LONGER IN CONFLICT

And then define the routes on your RouteConfig like so:

routes.MapHttpRoute(
            name: "DefaultApiWithAction",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
            );
Sign up to request clarification or add additional context in comments.

2 Comments

Just to clarifyapi/Users and api/Users/5 are not conflicting. api/Users/5 and api/Users/5/UserGroups are.
Then use the ActionName approach :)
1

I'm stuck using Web API v1 and came up w/ the following solution to this. For all of your general routes, declare the ActionName as "Default". Then in your route config, set the default action="Default". This changes your call to /Users/5 to users/5/default and ensures it maps to the right place. The only problem I've found w/ this solution is that the documentation will show the /Default part of the route. It looks like you can edit your view to exclude these as this guy did (https://stackoverflow.com/a/29353198/4374666).

If you're using v2, it seems like Attribute Routing would be the cleanest way.

// GET api/Users/5
[HttpGet, ActionName("Default")]
public User GetUser(int id) // THIS STILL DOES NOT WORK

// GET api/Users/5/UserGroups
[HttpGet, ActionName("usergroups")]
public IEnumerable<UserGroup> GetUserGroups(int id) // THIS WORKS

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.