Looking into the ASP.Net Web API - part 2
This is the second post discussing the ASP.Net Web API. I will continue building a small ASP.Net MVC 4.0 application that I started implementing in my last post.
You can have a look at the first post here. In this hands-on example I will show you how to create new footballer items,update a new footballer item,delete a new footballer item.
I will also refactor the implementation for the GET operations-methods from my last post.I will present you with a full CRUD hands-on example. This will be based in the RESTful WEB API paradigm and its four main HTTP methods. GET will retrieve the footballer items from the specified URI.PUT updates a resource (footballer item) at a specified URI.POST will create a new resource (footballer item).DELETE will delete a resource (footballer item) at a specified URI.
Let's move on to actually building the application.
1) Launch Visual Studio , I have named my application "WebApi", and open the application.
2) As I said earlier I will refactor the code I created in the first post.
Inside the Models folder my class file Footballer.cs looks exactly the same.
public class Footballer
{
public int FootballerID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public double Weight { get; set; }
public double Height { get; set; }
public DateTime JoinedClub { get; set; }
public string PositionPlayed { get; set; }
public int GoalsScored { get; set; }
}
I need to create a collection of objects - footballer objects.I will use the Repository Pattern to separate the collection of objects from our service implementation.
In Solution Explorer, right-click the Models folder. Select Add, then select New Item. From the available Templates pane, select Installed Templates. Under C#, select Code. In the list of code templates, select Interface. Name the interface IFooballerRepository.cs.
The code for the interface implementation follows
public interface IFootballerRepository
{
IEnumerable<Footballer> GetPlayers();
Footballer GetFooballerById(int id);
Footballer AddFootballer(Footballer item);
void RemoveFootballer(int id);
bool UpdateFootballer(Footballer item);
}
3) Now, we obviously need to implement this interface. We need to add another class to the Models folder, named FootballerRepository.cs. This class will implement the IFootballerRepository interface. Add the following implementation:
public class FootballerRepository :IFootballerRepository
{
private List<Footballer> footballers = new List<Footballer>();
private int _nextId = 1;
public FootballerRepository()
{
footballers.Add(new Footballer { FootballerID = 1, FirstName = "Steven", LastName = "Gerrard", Height = 1.85, Weight = 85, JoinedClub = DateTime.Parse("12/12/1999"), PositionPlayed = "Attacking Midfielder", GoalsScored = 23 });
footballers.Add(new Footballer { FootballerID = 2, FirstName = "Jamie", LastName = "Garragher", Height = 1.89, Weight = 89, JoinedClub = DateTime.Parse("12/02/2000"), PositionPlayed = "Central Defender", GoalsScored = 2 });
footballers.Add(new Footballer { FootballerID = 3, FirstName = "Luis", LastName = "Suarez", Height = 1.72, Weight = 73, JoinedClub = DateTime.Parse("12/01/2012"), PositionPlayed = "Striker", GoalsScored = 27 });
}
public IEnumerable<Footballer> GetPlayers()
{
return footballers;
}
public Footballer GetFooballerById(int id)
{
return footballers.Find(f => f.FootballerID == id);
}
public Footballer AddFootballer(Footballer item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
item.FootballerID = _nextId++;
footballers.Add(item);
return item;
}
public void RemoveFootballer(int id)
{
footballers.RemoveAll(f => f.FootballerID == id);
}
public bool UpdateFootballer(Footballer item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
int index = footballers.FindIndex(f => f.FootballerID == item.FootballerID);
if (index == -1)
{
return false;
}
footballers.RemoveAt(index);
footballers.Add(item);
return true;
}
}
Let me explain the implementation above.
I create a generic collection of Fooballer objects (footballers)
private List<Footballer> footballers = new List<Footballer>();
Then I populate the list with objects that live in the computer's memory
public FootballerRepository()
{
footballers.Add(new Footballer { FootballerID = 1, FirstName =
"Steven", LastName = "Gerrard", Height = 1.85, Weight = 85, JoinedClub =
DateTime.Parse("12/12/1999"), PositionPlayed = "Attacking Midfielder",
GoalsScored = 23 });
footballers.Add(new Footballer {
FootballerID = 2, FirstName = "Jamie", LastName = "Garragher", Height =
1.89, Weight = 89, JoinedClub = DateTime.Parse("12/02/2000"),
PositionPlayed = "Central Defender", GoalsScored = 2 });
footballers.Add(new Footballer { FootballerID = 3, FirstName = "Luis",
LastName = "Suarez", Height = 1.72, Weight = 73, JoinedClub =
DateTime.Parse("12/01/2012"), PositionPlayed = "Striker", GoalsScored =
27 });
}
Well, I trust you have some knowledge of C# and collection initializers which is a C# 3.0 feature. Have a look here if you want to learn more about it.The individual object initializers are enclosed in braces and separated by commas.
Next, I implement two simple methods. GetPlayers() returns a list of players.GetFooballerById(int id) returns a single footballer by its ID.
public IEnumerable<Footballer> GetPlayers()
{
return footballers;
}
public Footballer GetFooballerById(int id)
{
return footballers.Find(f => f.FootballerID == id);
}
Next I am implementing the AddFootballer method which is pretty straightforward method. If there is no item-object we throw an exception. If there is an item we give it a new ID and add the object to the collection.
public Footballer AddFootballer(Footballer item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
item.FootballerID = _nextId++;
footballers.Add(item);
return item;
}
Next I am implementing the RemoveFootballer method.I just remove an object from the collection with a specific id.
public void RemoveFootballer(int id)
{
footballers.RemoveAll(f => f.FootballerID == id);
}
Finally I implement the UpdateFootballer method.If there is no item-object we throw an exception. Then I find the index of the object in the collection array according to its ID and then remove it and add the new one.
public bool UpdateFootballer(Footballer item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
int index = footballers.FindIndex(f => f.FootballerID == item.FootballerID);
if (index == -1)
{
return false;
}
footballers.RemoveAt(index);
footballers.Add(item);
return true;
}
4) Now we need to change the code we have written for our controller FootballerController.cs in the Controllers folder.
Comment everything inside this class and just leave the code below
public class FootballerController : ApiController
{
}
The complete implementation follows
public class FootballerController : ApiController
{
static readonly IFootballerRepository repository = new FootballerRepository();
public IEnumerable<Footballer> GetPlayers()
{
return repository.GetPlayers();
}
public Footballer GetFooballerById(int id)
{
var footballer = repository.GetFooballerById(id);
if (footballer == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return footballer;
}
public HttpResponseMessage PostFootballer(Footballer footballer)
{
footballer = repository.AddFootballer(footballer);
var response = Request.CreateResponse<Footballer>(HttpStatusCode.Created, footballer);
string uri = Url.Link("DefaultApi", new { id = footballer.FootballerID });
response.Headers.Location = new Uri(uri);
return response;
}
public void PutFootballer(int id, Footballer footballer)
{
footballer.FootballerID = id;
if (!repository.UpdateFootballer(footballer))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
public void DeleteFootballer(int id)
{
Footballer footballer = repository.GetFooballerById(id);
if (footballer == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
repository.RemoveFootballer(id);
}
}
In ASP.NET Web API, a controller is a class that handles HTTP requests from the client.Now I will explain what I have implemented in this class and what methods have been created.
I am adding a field that holds an IFootballerRepository instance.
static readonly IFootballerRepository repository = new FootballerRepository();
This is the method to get a list of footballers.Well, nothing really to explain here.
public IEnumerable<Footballer> GetPlayers()
{
return repository.GetPlayers();
}
This is the method to get a footballer item by id.This method name also starts with Get.This method has a parameter named id. This parameter is mapped to the id segment of the URI path.
The method will throw an exception of type HttpResponseException if id is not valid. This exception will be translated by Web API as a 404 (Not Found) error.
public Footballer GetFooballerById(int id)
{
var footballer = repository.GetFooballerById(id);
if (footballer == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return footballer;
}
Now I would like to explain again how the ASP.NET Web API knows how to map URIs to our controller methods.
The ASP.NET Web API framework for each HTTP message decides which controller receives the request by consulting a route table. The Web API project contains a default route that you can find in the WebApiConfig.cs file.
/api/{controller}/{id}
The {controller} and {id} are just placeholders.
{controller} is matched to the controller name. {controller} in my case is footballer.
The HTTP request method is matched to the method name. (This rule applies only to GET, POST, PUT, and DELETE requests.)
/api/footballer will match the GetPlayers() method
/api/footballer/1 will match the GetFooballerById(1) method
Next I am implementing the PostFootballer method.This will create a new footballer item.The new item is created when the client sends a HTTP POST request to the server with the new footballer object in body of the request message.
public HttpResponseMessage PostFootballer(Footballer footballer)
{
footballer = repository.AddFootballer(footballer);
var response = Request.CreateResponse<Footballer>(HttpStatusCode.Created, footballer);
string uri = Url.Link("DefaultApi", new { id = footballer.FootballerID });
response.Headers.Location = new Uri(uri);
return response;
}
The way POST requests are getting handled, we define a method whose name starts with Post. The method takes a parameter of type Footballer. The clients to sends to the server a serialized representation of a footballer object, using either XML or JSON for the serialization.
Next we must think of the response code.The Web API framework sets the response status code to 200 (OK). HTTP/1.1 protocol dictated that when a POST request results in the creation of a resource, the server should reply with status 201 - Created.When the server creates a resource, it should include the URI of the new resource in the Location header of the response.
Next I am implementing the PutFootballer method.This method will update a footballer item.This method name starts with Put which makes the Web API to match it to PUT requests. The method takes two parameters, the footballer Id and the updated footballer object. The id parameter is taken from the URI path, and the footballer parameter is deserialized from the request body. The ASP.NET Web API framework takes simple parameter types from the route. Complex types are taken from the request body.
public void PutFootballer(int id, Footballer footballer)
{
footballer.FootballerID = id;
if (!repository.UpdateFootballer(footballer))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
}
Next I am implementing the DeleteFootballer method.We define a method whose name starts with Delete so the Web API matches it to DELETE requests.
Τhe method has a parameter named id. This parameter is mapped to the "id" segment of the URI path. Ιf the footballer object is not found an exception is thrown. If the deletion is successful then status code 204 (No Content) will be returned.
public void DeleteFootballer(int id)
{
Footballer footballer = repository.GetFooballerById(id);
if (footballer == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
repository.RemoveFootballer(id);
}
In the next and final post in this series I will build the Index.cshtml using Knockout which is a JavaScript library that helps developers to create rich, responsive displays when a clean underlying data model exists.
I think that this is enough material for one post and before I implement Index.cshtml I must introduce Knockout library to you.
Hope it helps!!!