Is it okay to have multiple get action methods in ASP .Net Web API controller according to RESTful API constraints?

softwareengineering.stackexchange https://softwareengineering.stackexchange.com/questions/349091

  •  12-01-2021
  •  | 
  •  

سؤال

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 GetUserById - api/users/getuserbyid?id=1, for GetUserByCredentials - api/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)

هل كانت مفيدة؟

المحلول

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.

نصائح أخرى

REST does not limit the number of methods. You can use as many of them as you need. The API and your implementation are 2 different things. If you want your API to follow the REST architecture then it it is about exchanging resources. How you handle in your code is up to you.

Your code deals with users. But the URI's look like actions, not resources. Consider a schema like this:

GET  /users      - returns the collection of users
GET  /users/{id} - return the user with a specific ID
POST /users      - add a user to the collection

searching based on criteria could look like this:
GET  /users?country={country-code}&city={city} 

This exposes the users a resources and uses the methods of the HTTP protocol to identify the action to perform on these resources.

People have different ideas about what makes an API a REST API. Most of the API's I've read about or seen in examples are limited. They are not exposed at web scale with a great number of client implementations.

Also the use of explicit MIME types to identify the transferred resources and a structured way to define links between resources is often not included.

So, think what you want your API to be, a 'real' REST API, or something simpler that uses logical URI's, HTTP methods and HTTP response codes to communicate.

as far as I know you cannot have multiple gets according to REST

No, not really. What you can't have is state. For instance, if you have an API such as:

POST /set-current-user/[id]
GET /user-info
GET /user-avatar
POST /change-password

which means that in order to get the profile picture of the user, you should first call set-current-user, you're not RESTful.

Aside that, you're free to have as many GET or non-GET actions in your controller as needed, since among six architectural constraints, there is no constraint which tells you not to have more than one action per controller.


Also, I can't avoid highlighting one of your examples:

GET api/users/getuserbycredentials?login=log&password=pass

If this is the actual route you've used, don't. Even with HTTPS (and you have to use HTTPS, since you're manipulating sensitive user data here), this has a huge security flaw of sending passwords plain text right into the server logs. Even if you're absolutely sure that the logs are encrypted and the traffic from the application server to the logs is done with a secure protocol, the sole fact of storing users' passwords plain text is terrible in terms of security. Don't do that. Never.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى softwareengineering.stackexchange
scroll top