TU Tran

Technologies should serve for business purpose.

NAVIGATION - SEARCH

REST in WebApi

In "REST - Overview" article, I were introduced the basic rules in RESTful, Can you show me how to implement in WebApi?

Ok, in this article, we will learn how to implement those rules in WebApi using C#.

Where could I get sourcode was used in this article?

Please checkout the code at https://github.com/techcoaching/webapi

Where should I started from?

Firstly, we need to create new project in your visual studio.

Open your Visual Studio, create new project, select "ASP.Net Web Application ..." and click on OK.

Select "Web API" and click on OK (or double click on "Web API"):

The default files for "Web API" project will be created, there are so many un-used files. Please ignore this for now:

Press "F5" to run the web site, the default page on browser look likes:

Now, You are ready for writing the first action of REST.

OK, Can you start with GET list of users first?

Ok, for matching with the "REST - Overview", I will also define User class with the same properties. It has the following properties:

  • First name
  • Last Name
  • Gender
  • Age

We create new "UsersController" and define GetUsers method as below:

namespace REST.Controllers
{
    [RoutePrefix("api/users")]
    public class UsersController : ApiController
    {
        [Route("")]
        [HttpGet]
        public IList<User> GetUsers()
        {
            return UsersController.Users;
        }
    }
}

There are some points, we need to aware in this UsersController class:

  • RoutePrefix, this declare which URI your controller can be access. In this case, it is "<root uri>/api/users".
  • Route: Declare the pattern this function will be called. This is relative path from the URI of controller. It is empty at this time. So access to "<root uri>/api/users" will trigger this  function.
  • HttpGet: Declare that GetUsers will be trigger if client/ caller sends request to "<root uri>/api/users" using GET HTTP Verb.
  • GetUsers will return the IList<User> object.

Review again, This function will be called if we send GET HTTP to "<root uri>/api/users". client/ caller will receive the list of user objects as photo below:

And completed code for UsersController is as below:

namespace REST.Controllers
{
    [RoutePrefix("api/users")]
    public class UsersController : ApiController
    {
        private static List<User> Users { get; set; }
        static UsersController()
        {
            UsersController.CreateTestUsers();
        }
        private static void CreateTestUsers()
        {
            UsersController.Users = new List<User>() {
                new User("TU","Tran", 20,"Male"),
                new User("TU 1","Tran", 20,"Male")
            };
        }

        [Route("")]
        [HttpGet]
        public IList<User> GetUsers()
        {
            return UsersController.Users;
        }
    }
}

The URI accesses to UsersController is "<root uri>/api/users", can I change it to "<root uri>/api/user"?

In WebApi, we can change and the API still works well. But this will break the rules of REST.

"User" was considered as resource of your system, Thus the URI for user in REST is "<root uri>/users" (<root uri>="http://localhost:11092/api" in this case).

It was required that resource must be a noun and in plural form. so ".../user" is not the correct case. It should be "users".

Can I remove "RoutePrefix" and "Route" in my controller. Will this violate the rule of REST?

Yes, we can remove these.

REST aware on the interface between caller and callee. It means that which information caller needs to send to server and how server responses to request from caller/ client.

"RoutePrefix" and "Route" are attribute of .Net.

The application still works well when removing "RoutePrefix" attribute. How can .Net Framework understand "<...>/api/users" mapped to my UsersController?

Please open "<project folder>/app_start/WebApiConfig.cs", we have this route config:

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

By default, .Net will use this route config to resolve your UsersController (controller =Users in this case)

The application can run well without RoutePrefix, Why do I need to use this attribute?

We use this for explicit mapping between URI (../api/users) and appropriated controller (UsersController).

This help the code easier for maintenance in future. Especially, in the application that we have many controllers, these controller may have similar name and located in different sub folders.

How can I change the format of received data?

In request, Please specify "accept" header, the valid value can be: application/json, application/xml... For more information about accept header, see HTTP Request fields

When sending request to "..../api/users" with "accept=application/xml", the response looks like:

 

I got this error on my local, What should I do?

 

 We get this error as .Net Framework does not know how to serialize (convert from .Net object to string).

There is 2 options for fixing this:

Marked User class as serializable:
[Serializable()]
public class User{}

The response will be:

In this way, the response message is hard to understand by caller.

Use DataContract and DataMember to control the response format:

Change User class as below:

[DataContract(Name = "user")]
public class User
{
	[DataMember(Name = "firstName")]
	public string FirstName { get; set; }
	[DataMember(Name = "lastName")]
	public string LastName { get; set; }
	[DataMember(Name = "age")]
	public int Age { get; set; }
	[DataMember(Name = "gender")]
	public string Gender { get; set; }
}

And the response will be:

We can control the response to caller using Serializable or DataContract. Which one I should use for my class?

I prefer using DataContract as the name of field returned to caller more specifically.

Ok, How about getting the specified user?

Similar with "get list of users", we send request to server with the following information:

  • Uri: <root uri>/users/<userId>. For example: "http://localhost/api/users/3" will return detail information of #3 user.
  • Http Verb: GET
  • Accept: format of response data, caller want to received in. For more information about Accept Header, see Accept.

The result is as photo below:

In UsersController, add new GetUser method as below:

namespace REST.Controllers
{
    [RoutePrefix("api/users")]
    public class UsersController : ApiController
    {
		/*..... other declarations */
        [HttpGet]
        [Route("{userId}")]
        public User GetUser(Guid userId) {
            return UsersController.Users.Find(user => user.Id == userId);
        }
   }
}

In this method, we have Route with "{userId}". This is parameter that user will pass when getting detail information of user.

GetUser also has parameter with the same name as declare in Route (I mean userId). .Net Framework will map between declare parameters and request parameters for us at run-time.

The function returns user object appropriated with that userId 

Ok, How about creating new user?

For creating new user, we need to send to server the following  information as below:

  • Uri: <rootUri>/users. For example, "http://localhost/api/users"
  • Http verb: POST
  • Content-Type(Header Fields): specify the format of data we will send to server. This let server know how to deserialize to appropriate object on server. For more information about content-type header field, see The Content-Type Header Field.
  • Body: Serialized string of data want to send to server. For example this is a json string if  "content-type" is "application/json" or xml string if "content-type" is "application/xml".
  • Accept: server can return newly created data to caller. In that case, we use 'accept' header field for specifying the format of content caller want to receive.

 In photo below, I send request to server using the following information:

  • Uri: http://localhost:11092/api/users
  • Http Verb: POST
  • Content-Type: application/json
  • Body: Json string of user object
  • Accept: application/json

In UsersController, we need to add new CreateUser function as below:

namespace REST.Controllers
{
    [RoutePrefix("api/users")]
    public class UsersController : ApiController
    {
        /*... Other declarations goes here ...*/
        [HttpPost]
        [Route("")]
        public User CreateUser(User user) {
            user.Id = Guid.NewGuid();
            UsersController.Users.Add(user);
            return user;
        }
   }
}

We noticed that:

  • CreateUser was marked with HttpPost attribute.
  • CreateUser has parameter type of User, this object will contain deserialized data from caller.

If we try to send  GET to "http://localhost:11092/api/users", there is new object in the list:

 

How about updating a user?

Updating a user is similar with creating new user, with some difference as below:

  • Uri: <root Uri>/users/<userId>
  • Http Verb: PUT
  • Resposne: No response

Getting the list of users again, we can see the change applied:

Is it possible to send userId in json string we send to server?

Yes we can send. Actually we did not use this property on server. We should get passing value from URI.

Should I return updated user to caller?

I think we should not. As most of caller did not need those information as they send to server, so they have those on their locally.

I did not see you mention about HTTP Status Code. Do we need to aware on this?

Yes, we should return correct status code as client/ caller base on HTTP Status Code to know if the request was executed successful or not.

There are some common HTTP Status Codes were used:

No Status Code Comment
1 200 This indicates that the request was executed successful. This Status code was used in GET, UPDATE, DELETE
2 201 This indicates that new resource was created. This was used in POST(Create new resource)
3 204 This indicates that the request has not content attached. This was used in PUT (Update re source)
4 404 This indicates that requested resource (get user by id, ...) was not found.
5 500 This indicates that there are error on server.

 

Can I use POST for updating user?

In theory, we should use POST for creating new resource (such as: user, .... and PUT for updating existing resource (such as: user, ...).

In practical, we usually use POST for both creating new resource and updating current resource.

How can I do that in WebAPI?

We need to modify the UpdateUser method as below:

[HttpPost]
[Route("{userId}")]
public void UpdateUser(Guid userId, User user) {
	Models.User currentUser = UsersController.Users.Find(item => item.Id==userId);
	currentUser.FirstName = user.FirstName;
	currentUser.LastName = user.LastName;
	currentUser.Age = user.Age;
	currentUser.Gender = user.Gender;
}

We see that there is not change in the code. Except HttpPut to HttpPost.

How can I delete a user?

To delete a user, client side will make the call to server using DELETE HTTP Verb and passing id of user in URI.

We add this method into UsersController as below:

[HttpDelete]
[Route("{userId}")]
public User DeleteUser(Guid userId)
{
	Models.User deletedUser = UsersController.Users.Find(item => item.Id == userId);
	UsersController.Users.Remove(deletedUser);
	return deletedUser;

}

In this code:

  • We map this method to HttpDelete, so only DELETE Http Verb can trigger this methos.
  • Route with "{userId}" as parameter. this is the user Id of deleted user was sent by client.
  • In Delete operation, we can return deleted user back to client/ caller. this is useful in the case, we need extra handling in callback function.

Run the app, try to get the list of users first:

There are 2 users in the list from server. Let try to delete 1 user:

And try to get the list of users again:

Ok, What should I do in the case of deleting multiple users?

In this case, We also send DELETE request to server, but in different URI as below:

  • Uri in <root uri>/users format, for example: http://localhost:11092/api/users
  • Http Verb: DELETE
  • Body: List of ids of user will be deleted.
  • Content-Type header: specify format of data was sent on body. for example: application/json
  • Accept: Format of data that you want to receive back from server.

Let try to add DeleteUsers function as below:

[HttpDelete]
[Route("")]
public IList<User> DeleteUsers(IList<Guid> userIds)
{
	IList<Models.User> deletedUsers = UsersController.Users.Where(item => userIds.Contains(item.Id)).ToList();
	foreach (Models.User item in deletedUsers)
	{
		UsersController.Users.Remove(item);
	}
	return deletedUsers;
}

Run the app and try to send Delete action to server:

Try to get the list of users again, We can see that the list is empty at this time:

Now, we have gone through step by step using REST in WebApi.

 

Where can I find more article for REST?

Please have a look:

Thank for reading.

Note: Please like and share to your friends if you think this is use full article, I really appreciate

Add comment