Creating a REST service using ASP.NET Web API
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.
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 addresses. 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.
There are different templates available for creating a MVC 4 application. I’ve selected the 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.
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 we are storing the tasks in Cache. Here is our 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 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.