Building an ASP.NET Web Api RESTful service for Beginners


 

RESTful service

REST acronym stands for REpresentational State Transfer. In the latest years REST is emerging as a predominant Web service design model. AS another alternative we have Web Services built around SOAP.

RESTful services are fully built around the HTTP protocol, and use the basic HTTP commands like: GET, POST, PUT, DELETE in order to define the “action”. In simple terms, a REST Web Service is all about aResource that can be accessed or changed by using the above mentioned HTTP methods.

What do those HTTP commands represent:

GET Retrieves a Resource
POST
  • Updates a Resource: if you are requesting the server to update one or more subordinates of the specified resource.
  • Creates Resource = POST if you are sending a command to the server to create a subordinate of the specified resource, using some server-side algorithm.
PUT
  • Creates a Resource. Use PUT if you are sending the full content of the specified resource (URL).
  • Update a Resource = PUT iff you are updating the full content of the specified resource.
DELETE Deletes a Resource.

Idempotence

Idempotency guarantees that repeated invocations of a service capability are safe and will have no negative effect. The side-effects of N > 0 identical requests is the same as for a single request. The methods GET, PUT and DELETE share this property.

Message Formatting

Usually the main output formats of a restful service are JSON and XML, but obviously this is not the only option as we theoretically could return whatever type of message format we want. For instance, returning a PDF document when doing GET would be absolutely valid.

SOAP based Web Services

Another way of creating web service is by using SOAP message format as the communication between the Client and Server. Both, SOAP based and RESTful services, even though they try to solve the same issues (retrieving and getting data), the approach is very different.
Choosing REST or SOAP based services will depend upon your needs for Security, Speed, Protocols, Extensibility, Legacy Systems, etc. Is not that easy to give a recipe, but in general REST is a great tool when it comes to the AJAX dynamic pages and interoperability as it uses widely known HTTP protocol.

What is: ASP.NET Web Api

ASP.NET Web API is the latest Microsoft framework that makes it easy to build HTTP services that reach a broad range of clients, including browsers and mobile devices. ASP.NET Web API is an ideal platform for building RESTful applications on the .NET Framework.

ASP.NET Web API is build around (or better say, into) the newest ASP.NET MVC 4 framework and for the time being is the most powerful Microsoft tool when it comes to creating RESTful services. Yes, there are other possibilities as described in the article What technology for building web services in Microsoft.Net.

After this short introduction of very basic concepts, lets do some code.

ASP.NET Web API Installation

There are few ways of installing ASP.NET Web API.

  1. If you are using Visual Studio 2012, ASP.NET Web API is already pre-installed, and there is anything else needed to do.
  2. If you are using Visual Studio 2010, then there are few possibilities:

First steps

As mentioned, ASP.NET Web API is part of the ASP.NET MVC 4 Framework, and as a such in order to start, we need to create a new project by choosing ASP.NET MVC 4 Application project, and then choose the Web API as shown in the picture below:

Visual Studio will create a basic service structure, and I would like to highlight two folders:

ASP.NET Web API Visual Studio Solution Explorer
We may easily delete the HomeController as we are not interested into creating any MVC application in this example.

Routing

WebApiConfig.cs file in the App_Start folder is particularly interesting. It contains the definition of the routing needed for our service to work. As we normally do for the ASP.NET MVC application, we need to define the “route”, or better say the PATH to our service actions.

The code generated by default.

1
2
3
4
5
6
7
8
9
10
11
public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

Please note the following:

  • routeTemplate starts with “api/”. This will generate urls like: http://www.agile-code.com/api/product/1. Use “api” keyword to differentiate it from the normal ASP.NET MVC route.
  • {controller} represents the actual controller. Controller in our case would correspond to the part in bold: http://www.agile-code.com/api/product/1
  • There is no “{action}” as we would expect it in ASP.NET MVC. As there are no actions but verbs in form of Post()Get()… methods, there is no need for defining the Action in the route. The “{action}” is automatically known at runtime based on the HTTP verb used in the request.

RESTful service basic code

Visual Studio will take care of creating the basic skeleton of the service itself, as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ValuesController : ApiController
{
    // GET api/values
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }
 
    // GET api/values/5
    public string Get(int id)
    {
        return "value";
    }
 
    // POST api/values
    public void Post([FromBody]string value)
    {
    }
 
    // PUT api/values/5
    public void Put(int id, [FromBody]string value)
    {
    }
 
    // DELETE api/values/5
    public void Delete(int id)
    {
    }
}

In the code above, there are few things to note:
1. ApiController is a new controller that comes as part of the ASP.NET Web API. Every RESTful service should inherit from it.
2. Methods in the service itself have the names as mentioned above as the HTTP methods, and those are bound to the proper verb at runtime by the Web API framework. It is still allowed to have methods that contain something else than the basic HTTP methods mentioned before.

Implementing something more realistic

Lets implement a ProductController, which will represent the Product resource, that for the sake of simplicity will look like:

1
2
3
4
5
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Service Implementation

I will show one by one the methods implementing various HTTP verbs.
Important: you will see that I am using repository keyword. This is simply a class that will keep in memory the list of Products, just for the sake of this example.

GET

GET a list of products

Get is pretty much straightforward. The Method called Get() is defined, and in our case returns a list of Products

1
2
3
4
5
// GET api/product
public IEnumerable<Product> Get()
{
    return repository.GetProducts();
}

for Getting back the data we may simply use the browser as well. So, if we navigate the page (in my case is http://localhost:34133/api/product), I will get back the list of products.

GET a single product

This is the code that will return one single product:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// GET api/product/5
public Product Get(int id)
{
    Product product = repository.GetProduct(id);
 
    if (product == null)
    {
        throw new HttpResponseException(
            Request.CreateResponse(HttpStatusCode.NotFound));
    }
    else
    {
        return product;
    }
}

The important bit in this case is that if the PRODUCT id is not found, we are going to throw an error of type HttResponseException. HTTP REsponse in this case will be “Not Found” message.

This is what happens when we are searching for an existing product:

Get single Product

and in case the product doesn’t exists (for this we will use a debugging tool called Advanced Rest Client which runs in the Chrome browser in order to test the output.).
As you see the status returned is 404 Not found, with the empty content.

POST

In order to create new content, we are going to use the POST mechanism to send the data to the server.
This is the code in the service that would create a new Product

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// POST api/product 
public HttpResponseMessage Post(Product product)
{
    if (ModelState.IsValid)
    {
        // this will set the ID for instance...
        product = repository.AddProduct(product);     
 
        var response = Request.CreateResponse(
                             HttpStatusCode.Created, product);
 
        string uri = Url.Link("DefaultApi", new {id = product.Id});
        response.Headers.Location = new Uri(uri);
        return response;
    }
    else
    {
        return Request.CreateResponse(HttpStatusCode.BadRequest);
    }
}      

Creating a new resource involves few steps:
1. The client should send a POST request to api/product to create the resource.
2. REST Service should then return a 201 Created HTTP response, with the URI of the newly created resource in the Location header.
3. In case of errors “Bad Request” will be returned to the client.

as shown in the image below, the status HTTP 201 Created together with the actual data is returned to the client application.

Posting a new product

Let’s check if the product has been actually created by using the browser:
Get the list with the newly created product

as you may see, the Product with the id 6 is created.

PUT

In order to update an existing product we are going to use the PUT method. Code in the service is defined as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// PUT api/product/5
public HttpResponseMessage Put(int id, Product product)
{
    if (ModelState.IsValid && id == product.Id)
    {
        var result = repository.Update(id, product);
        if (result == false)
        {
            return Request.CreateResponse(HttpStatusCode.NotFound);
        }
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    else
    {
        return Request.CreateResponse(HttpStatusCode.BadRequest);
    }
}

Note the following:
1. There are two parameters. One is the ID of the product we are going to update, and the second parameter is the actual object itself.
2. If we try to update a non existing object, the HTTP code Not Found will be returned to the client
3. If there is any error happening to the code we return Bad Request
4. If all goes well the HTTP status returned is 200 OK
5. The return parameter is of type HttpResponseMessage. We are only returning the status not the content.
6. Is not needed to return any Location as in the previous example as know it already. The same comes with the content.

Let’s see this visually:

Posting an update to an existing product will look like this. (Note that the Name of the product changed to be “Monitor”).

in case we try to update a non-existing product. Notice in the address bar, we are trying to update a product with the id = 11 that doesn’t exists. In that case we are returning HTTP status of 404 Not found.

DELETE

To delete an already existing object we need to pass only the actual ID, as defined in the code below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// DELETE api/product/5
public HttpResponseMessage Delete(int id)
{
    Product product = repository.GetProduct(id);
 
    if (product == null)
    {
        return Request.CreateResponse(HttpStatusCode.NotFound);
    }           
 
    try
    {
        repository.Delete(id);
    }
    catch (Exception exc)
    {
        return Request.CreateResponse(HttpStatusCode.BadRequest);
    }
 
    return Request.CreateResponse(HttpStatusCode.OK, product);
}

Note the following:
1. If product we want to delete is not there we return 404 Not found
2. If there is an exception we raise Bad Request
3. In case everything goes well, we return the 200 OK together with the object data, as the client would potentially need to display some information about the product itself.

Product deleted and the status returned as 200 OK.

When trying to delete a non existing resource the status 404 Not Found is returned.
Delete a non existing product.

Complete code

In order to make it easier for you, here is the full source code of the service.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
public class ProductController : ApiController
{
    private readonly ProductContext repository = new ProductContext();
 
    // GET api/values
    public IEnumerable<Product> Get()
    {
        return repository.GetProducts();
    }
 
    // GET api/product/5
    public Product Get(int id)
    {
        Product product = repository.GetProduct(id);
 
        if (product == null)
        {
            throw new HttpResponseException(
                Request.CreateResponse(HttpStatusCode.NotFound));
        }
        else
        {
            return product;
        }
    }
 
    // POST api/product 
    public HttpResponseMessage Post(Product product)
    {
        if (ModelState.IsValid)
        {
            // this will set the ID for instance...
            product = repository.AddProduct(product);
 
            var response = Request.CreateResponse(
                              HttpStatusCode.Created, product);
 
            string uri = Url.Link("DefaultApi", new {id = product.Id});
            response.Headers.Location = new Uri(uri);
            return response;
        }
        else
        {
            return Request.CreateResponse(HttpStatusCode.BadRequest);
        }
    }
 
    // PUT api/product/5
    public HttpResponseMessage Put(int id, Product product)
    {
        if (ModelState.IsValid && id == product.Id)
        {
            var result = repository.Update(id, product);
            if (result == false)
            {
                return Request.CreateResponse(HttpStatusCode.NotFound);
            }
            return Request.CreateResponse(HttpStatusCode.OK);
        }
        else
        {
            return Request.CreateResponse(HttpStatusCode.BadRequest);
        }
    }
 
    // DELETE api/product/5
    public HttpResponseMessage Delete(int id)
    {
        Product product = repository.GetProduct(id);
 
        if (product == null)
        {
            return Request.CreateResponse(HttpStatusCode.NotFound);
        }
 
        try
        {
            repository.Delete(id);
        }
        catch (Exception exc)
        {
            return Request.CreateResponse(HttpStatusCode.BadRequest);
        }
 
        return Request.CreateResponse(HttpStatusCode.OK, product);
    }
}

Conclusion

ASP.NET Web API is a very versatile framework that allow us to create RESTful services in a very easy way without going through much pain when compared to the WCF configuration part for example.
The simplicity of HTTP statuses makes the development even more simple.

Note: The above information is provided “As Is” basis. The origal content is from
http://www.agile-code.com

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s