Click here to Skip to main content
Click here to Skip to main content

Creating a REST service using ASP.NET Web API

By , 15 Apr 2014
Rate this:
Please Sign up or sign in to vote.

"THE ARTICLE IS NO LONGER MAINTAINED HERE. PLEASE POST ANY FURTHER DISCUSSIONS/QUESTIONS IN MY BLOG http://www.prideparrot.com/blog/archive/2012/3/creating_a_rest_service_using_asp_net_web_api" - Author

Introduction

A service that is created based upon the architecture of REST is called as REST service. Although REST looks more inclined to web and HTTP its principles can be applied to other distributed communication systems also. One of the real implementation of REST architecture is the World Wide Web (WWW). REST based services are easy to create and can be consumed from a wide variety of devices.

There are many APIs available in different languages to create and consume REST services. The ASP.NET MVC beta 4 comes with a new API called ASP.NET Web API to create and consume REST services. While creating REST services it is important to follow the rules and standards of the protocol (HTTP). Without knowing the principles of REST it is easy to create a service that looks RESTful but they are ultimately an RPC style service or a SOAP-REST hybrid service. In this article we are going to see how to create a simple REST service using the ASP.NET Web API.

REST

In REST architecture there is always a client and a server where the communication is always initiated by the client. The client and server are decoupled by a uniform interface there by making both the client and server to develop independently. Every resource in the server is accessed by a unique address (URI). When the client access a resource the server returns a representation of the resource based upon the request header. The representations are usually called as media-types or MIME types.

REST architecture

REST architecture

Uniform Interface

An important concept of REST is the uniform interface. The uniform interface contains a set of methods that can be understood by both the client and the server. In the HTTP uniform interface the important methods are GET, POST, PUT, DELETE, HEAD and OPTIONS. It is important to choose the right method for the right operation. For ex. if the client is going to get the resource from the server then they should use GET method. Likewise the DELETE method should be used to delete the resource and other methods has to be used appropriately based upon the action performed on the server. I wrote an article about using HTTP methods in REST applications and you can read it here.

Status Codes

Like the uniform interface the status codes returned from the server to the client also important in RESTful applications. We will see more about this while doing the sample.

ASP.NET Web API

ASP.NET Web API was previously called as WCF Web API and recently merged into ASP.NET MVC 4 beta. You can download ASP.NET MVC beta 4 from here. The ASP.NET Web API comes with its own controller called ApiController. So now we got two types of controllers while developing MVC applications: one is the default MVC Controller and the other one is the ApiController. Choosing the right controller for the right job is important. For creating REST services we have to use the ApiController, basically these controllers return data. For returning views (aspx, cshtml) we have to use the default controller. In our sample we are going to create a service that returns only data and hence we go with ApiController.

Sample

In this sample we are going to create a simple service that manages one s daily tasks. The service exposes operations to get all tasks, get a single task, create a new task, edit a task and delete a task as well.

Before creating any REST service first we have to identify the different resources in the application and map the actions performed over them to the HTTP methods and address. In our example there is only one resource Task and below is the mapping table.

Action Method URI
Get all the tasks GET /tasks
Get a single task GET /tasks/id
Create a new task POST /tasks
Edit a task PUT /tasks/id
Delete a task DELETE /tasks/id

Let's create a new ASP.NET MVC 4 web application.

Create ASP.NET MVC 4 web application

Create ASP.NET MVC 4 web application

There are different templates available for creating a MVC 4 application. I ve selected the Empty template.

Empty Template

Empty Template

The solution contains lot of things that we don t require for creating a service like Content, Scripts, Views etc. After removing them the solution looks clean and lean as shown below.

Solution Explorer

Solution Explorer

If we look into the Global.asax.cs, there will be already two routes defined: one is for the mvc controller and the other one if for the api controller.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

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

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id =
                UrlParameter.Optional }
    );
}

Since we are going to create a service that will return only data remove the mvc controller s route and modify the other accordingly.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.MapHttpRoute(
        name: "Default",
        routeTemplate: "{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );
}

Next thing we have to do is create the resource Task.

public class Task
{
    public int Id { get; set; }

    public string Description { get; set; }

    public int Priority { get; set; }

    public DateTime CreatedOn { get; set; }
}

We have to create a controller that exposes methods performed on our task resource over HTTP. The "Add Controller" window displays different useful scaffolding options but I m interested on the "API Controller with empty read/write actions".

Below is our newly added controller

public class TasksController : ApiController
{
    // GET /api/tasks
    public IEnumerable<string> Get()
    {
       return new string[] { "value1", "value2" };
    }

    // GET /api/tasks/5
    public string Get(int id)
    {
       return "value";
    }

    // POST /api/tasks
    public void Post(string value)
    {
    }

    // PUT /api/tasks/5
    public void Put(int id, string value)
    {
    }

    // DELETE /api/tasks/5
    public void Delete(int id)
    {
    }
}

If you notice our TaskController it is derived from the new ApiController. The controller contains methods to get all the tasks, get a single task, create a task, edit a task and delete a task. One interesting thing to note down is the ASP.NET Web API uses convention over configuration. Any method starts with Get is automatically mapped with the HTTP GET method, any method that starts with Post is automatically mapped with the HTTP POST method and so on.

After modifying the arguments and the return types of the methods the controller looks as below.

public class TasksController : ApiController
{
    public IEnumerable<Task> Get()
    {
        throw new NotImplementedException();
    }

    public Task Get(int id)
    {
        throw new NotImplementedException();
    }

    public HttpResponseMessage<Task> Post(Task task)
    {
        throw new NotImplementedException();
    }

    public Task Put(Task task)
    {
        throw new NotImplementedException();
    }

    public HttpResponseMessage Delete(int id)
    {
        throw new NotImplementedException();
    }
}

Before implementing the methods we need a repository to store our tasks. For simplicity I ve used an in-memory cache repository. Here is our cache repository's interface and implementation.

public interface ITaskRepository
{
    IEnumerable<Task> Get();

    Task Get(int id);

    Task Post(Task Task);

    Task Put(Task Task);

    bool Delete(int id);
}
public class TaskRepository : ITaskRepository
{
    private List<Task> Tasks
    {
        get
        {
            if (HttpContext.Current.Cache["Tasks"] == null)
                HttpContext.Current.Cache["Tasks"] = new List<Task>();

            return HttpContext.Current.Cache["Tasks"] as List<Task>;
        }
        set
        {
            HttpContext.Current.Cache["Tasks"] = value;
        }
    }

    public IEnumerable<Task> Get()
    {
        return Tasks;
    }

    public Task Get(int id)
    {
        return Tasks.Find(t => t.Id == id);
    }

    public Task Post(Task task)
    {
        task.Id = Tasks.Max(t => t.Id) + 1;
        Tasks.Add(task);

        return task;
    }

    public Task Put(Task task)
    {
        var t = Get(task.Id);

        if (t == null)
            throw new Exception(string.Format("Task with id {0} not exists.", task.Id));

        t.Description = task.Description;
        t.Priority = task.Priority;

        return t;
    }

    public bool Delete(int id)
    {
        var t = Get(id);

        if (t == null)
            return false;

        Tasks.Remove(t);

        return true;
    }
}

The TaskRepository class is simple and straight forward, it uses HttpContext.Current.Cache to store the tasks. The TaskController needs an ITaskRepository implementation and we will see at the end how we can inject it to the constructor using the extension points of Web API.

private readonly ITaskRepository _taskRepository;

public TasksController(ITaskRepository taskRepository)
{
    _taskRepository = taskRepository;
}

Now we have a repository for controller to store and maintain tasks. Let s go ahead and implement the controller methods.

Get()

This method returns all the tasks from the repository. The implementation of the method is straight and simple.

public IEnumerable<Task> Get()
{
    return _taskRepository.Get();
}

Get(id)

This method returns a task based upon the id. As per the HTTP standards when the resource requested by the client not exists in the server it should return the status 404.

public Task Get(int id)
{
    var task = _taskRepository.Get(id);

    if (task == null)
    {
        throw new HttpResponseException(new HttpResponseMessage
        {
            StatusCode = HttpStatusCode.NotFound,
            Content = new StringContent("Task not found")
        });
    }

    return task;
}

In the above method if the task not exists we are throwing a HttpResponseException passing the HttpResponseMessage to the constructor. In the HttpResponseMessage we have set the status as HttpStatusCode.NotFound (404) and some optional content that will be written to the response body.

Post(Task task)

The HTTP POST method should be used when you are trying to add a new resource and the address in which the newly added resource can be accessed will be defined by the server. If the resource is created successfully we have to return the status code 201 (means created) along with the URI by which the resource can be accessed.

public HttpResponseMessage<Task> Post(Task task)
{
    task = _taskRepository.Post(task);

    var response = new HttpResponseMessage<Task>(task, HttpStatusCode.Created);

    string uri = Url.Route(null, new { id = task.Id });
    response.Headers.Location = new Uri(Request.RequestUri, uri);

    return response;
}

Put(Task task) and Delete(Task task)

Here are the implementations for the Put and Delete methods.

public Task Put(Task task)
{
    try
    {
        task = _taskRepository.Put(task);
    }
    catch (Exception)
    {
        throw new HttpResponseException(new HttpResponseMessage
        {
            StatusCode = HttpStatusCode.NotFound,
            Content = new StringContent("Task not found")
        });
    }

    return task;
}
public HttpResponseMessage Delete(int id)
{
    _taskRepository.Delete(id);

    return new HttpResponseMessage
    {
        StatusCode = HttpStatusCode.NoContent
    };
}

Normaly we think of returning a 404 when the resource client tries to delete not exists but as per the REST theory the DELETE method should be idempotent, that is how many times the client calls the Delete method the server should behave same as in the initial request.

Here is the complete controller code,

public class TasksController : ApiController
{
    private readonly ITaskRepository _taskRepository;

    public TasksController(ITaskRepository taskRepository)
    {
        _taskRepository = taskRepository;
    }

    public IEnumerable<Task> Get()
    {
        return _taskRepository.Get();
    }

    public Task Get(int id)
    {
        var task = _taskRepository.Get(id);

        if (task == null)
        {
            throw new HttpResponseException(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.NotFound,
                Content = new StringContent("Task not found")
            });
        }

    return task;
    }

    public HttpResponseMessage<Task> Post(Task task)
    {
        task = _taskRepository.Post(task);

        var response = new HttpResponseMessage<Task>(task, HttpStatusCode.Created);

        string uri = Url.Route(null, new { id = task.Id });
        response.Headers.Location = new Uri(Request.RequestUri, uri);

        return response;
    }

    public Task Put(Task task)
    {
        try
        {
            task = _taskRepository.Put(task);
        }
        catch (Exception)
        {
            throw new HttpResponseException(new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.NotFound,
                Content = new StringContent("Task not found")
            });
        }

        return task;
    }

    public HttpResponseMessage Delete(int id)
    {
        _taskRepository.Delete(id);

        return new HttpResponseMessage
        {
            StatusCode = HttpStatusCode.NoContent
        };
    }
}

Our service is pretty much ready and one final thing we have to do is inject the ITaskRepository implementation to the TaskController's constructor. Since the constructor is called by the framework we have to find the extension point to do that. Luckily Web API provides a very easy way to do that through the ServiceResolver class.

The ServiceResolver class has a method SetResolver that comes for the rescue. This method takes two lambda expressions as parameters, the first expression is used to return a single instance of the service and the second one is used to return multiple instances. I ll write in future a separate post about resolving dependencies in Web API. So here is the code that we have to insert into the Application_Start event of Global.asax.cs.

GlobalConfiguration.Configuration.ServiceResolver.SetResolver
(

    t =>
    {
        if (t == typeof(TasksController))
        {
            return new TasksController(new TaskRepository());
        }

        return null;
    },

    t => new List<object>()
);

So our service is finally ready. We can host it either in IIS or through self-hosting.

Download Sample

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

After2050
Software Developer Trigent Software Private Limited
India India
I'm a software developer from south tip of India. I spent most of the time in learning new technologies. I've a keen interest in client-side technologies especially JavaScript and admire it is the most beautiful language ever seen.
 
I like sharing my knowledge and written some non-popular articles. I believe in quality and standards but blames myself for lagging them.
 
I believe in small things and they makes me happy!
Follow on   Twitter

Comments and Discussions

 
QuestionRestful service Pinmembersivakl_20019-Jan-14 17:09 
QuestionError PinmemberZakAttack27-Jun-13 4:42 
AnswerRe: Error PinmemberZakAttack27-Jun-13 6:00 
QuestionCode is not valid since the RC release PinmemberLarry E17-Mar-13 8:49 
AnswerRe: Code is not valid since the RC release PinmemberZakAttack27-Jun-13 7:53 
QuestionHypermedia controls PinmemberJames Chapman22-Jan-13 23:36 
GeneralMy vote of 5 Pinmembercaptainnn8-Jan-13 1:12 
QuestionIOC/DI Pinmembertony bater11-Oct-12 0:11 
AnswerRe: IOC/DI Pinmemberkareljanvh10-Apr-14 2:42 
QuestionThank you for this post. Pinmembernarasimv13-Sep-12 7:44 
QuestionProblem with session PinmemberMarco Rinaldi12-Sep-12 23:23 
AnswerRe: Problem with session PinmemberAfter205012-Sep-12 23:58 
QuestionSample calls? PinmemberJonas K Wik27-Jul-12 10:26 
AnswerRe: Sample calls? PinmemberAfter205027-Jul-12 18:02 
GeneralRe: Sample calls? PinmemberTúlio Cruz9-Jul-13 4:01 
AnswerArticle PinmemberClifford Nelson23-Jul-12 11:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140415.2 | Last Updated 15 Apr 2014
Article Copyright 2012 by After2050
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid