C# REST – Multiple GET Action Methods in ASP.NET Web API Controller

asp.net-mvc-web-apicrest

I have the following interface in my business layer

    public interface IUserService
    {
        void CreateUser(User user);
        List<User> FindUsersByName(string searchedString);
        User GetUserById(int userId);
        User GetUserByCredentials(string login, string password);
        void UpdateUser(User user);
        void UpdateUserPassword(int userId, string oldPassword, string newPassword);
    }

Now I want to provide web API for this interface. As you can see this interface has multiple get methods that return one item GetUserById and GetUserByCredentials, it also has multiple update methods UpdateUser and UpdateUserPassword, in future I might want to add additional get method that returns a collection, like, GetAllUsers, for instance.

The obvious solution was to encapsulate this functionality in one controller.
So what I did first, in WebApiConfig I changed routes configuration to

config.Routes.MapHttpRoute(
                name: "DefaultApi",
                //as you can see I added {action} to the path so that, it will be possible to differentiate between different get/put requests
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            ); 

Then I created a UsersController that looks like this

public class UsersController : ApiController
    {
        private readonly IUserService _userService;

        public UsersController(IUserService userService)
        {
            _userService = userService;
        }

        // POST api/users/createuser
        [HttpPost]
        public IHttpActionResult CreateUser(User user)
        {
            //some code
        }

        // GET api/users/getuserbyid?id=1
        [HttpGet]
        public IHttpActionResult GetUserById(int id)
        {
            //some code
        }

        // GET api/users/getuserbycredentials?login=log&password=pass
        [HttpGet]
        public IHttpActionResult GetUserByCredentials(string login, string password)
        {
            //some code
        }


        // GET api/users/findusersbyname?searchedString=jack
        [HttpGet]
        public IHttpActionResult FindUsersByName(string searchedString)
        {
            //some code
        }

        // PUT api/users/updateuser
        [HttpPut]
        public IHttpActionResult UpdateUser(UserBase user)
        {
            //some code
        }

        // PUT api/users/updateuserpassword?userId=1&oldPassword=123&newPassword=1234
        [HttpPut]
        public IHttpActionResult UpdateUserPassword(int userId, string oldPassword, string newPassword)
        {
            //some code
        }
    }

As you can see from the code above I have different URIs for each action method, e.g., for GetUserByIdapi/users/getuserbyid?id=1, for GetUserByCredentialsapi/users/getuserbycredentials?login=log&password=pass and so on. This solution works fine so far, but the problem is, as far as I know you cannot have multiple gets according to REST, so does this solution still comply with the constraints for a RESTful service? And if not how can I make it truly RESTful? The idea of splitting this interface into different controllers seems a little odd to me, because in the future I may want to add some new methods to my interface, like, GetUsersByGender, GetUsersByDateOfBirthday and so on (if I'm going to create a new controller each time, that doesn't sound right to me)

Best Answer

I know you cannot have multiple gets according to REST

Not really. REST and API modelling are different subjects. REST APIs are meant to be an integration strategy, based on the premises introduced by Fielding on his dissertation about distributed architectural styles. These premises have nothing to do with how APIs are modelled, how many resources, URIs and semantics we provide.

For instance:

/api/users-living-in-courscant
/api/users-not-living-in-courscant
/api/users?q=living:coruscant
/api/users?q=id:12345
/api/user/12345
/api/me

Some of the above URIs might refer to the same resource(s), the main difference (and key point) lays on their respective semantics.

so does this solution still comply with the constraints for a RESTful service?

In my opinion, your approach is closer to an RPC-like web service than an API REST. Take a look at Martin Fowler's article about Richardson Maturity Model.

If we read Martin's post carefully, we find that Martin is not introducing API modelling techniques or best practices. He sets the focus on how to make the communication client-server properly according to the HTTP semantics. How to represent and discover resources.

But, he does not mention how to identify these resources. He does not mention how to shape URIs.

And if not how can I make it truly RESTful?

If making the API totally RESTful is the concern, I would suggest reading Fielding dissertation first. Once assimilated the meaning of REST, I would look for documentation related to API modelling. As soon as the last agreed with the former you should be on the good path.

Here 2 links to start working:

The above links follow a deliberated order. I think it's a natural order that goes from basics to advanced concepts. From the ground up.

I have emphasised the word conventions intendedly. These can be dismissed or interpreted if you consider them to be inadequate to your needs.


Further readings

If you are interested in the topic, I have found the following books to be insightful.

  • API Design Rulebook: It is mostly focused on the API modelling. You will find a brief introduction to the web architecture and to the REST principles.

  • Rest in practice: I consider this one to be more advanced. Rather focused on the advantages of REST as an integration than on API modelling.