Chapter 10. Building HTTP-based Web Services Using ASP.NET Web API
So far, we have learned how to create web applications using ASP.NET Core. But there are times when simply creating a web application is not enough. Let's assume you are using ASP.NET Core to create a web application that provides weather information for all the cities across the world. People access your web application to find out weather information, and they are satisfied with the service. But this weather information may be needed by many other websites or web applications, such as tourism websites, news websites, and many other mobile applications.
Instead of writing the code all over again for their websites, you can create and publish the web services and the websites can consume the required web services whenever they need to.
In this chapter, you are going to learn about the following topics:
- What an HTTP-based service is and how it is useful
- What Fiddler is
- How to compose an HTTP request using Fiddler and fire the same in order to get an HTTP response
- How to design and implement the HTTP service using Web API
Microsoft provides ASP.NET Web API for programmers to build HTTP-based services. But HTTP is not just used to serve the webpages. You can use HTTP as a platform. This brings several advantages:
- As web services built using ASP.NET Web API use HTTP for communication, these web services can be consumed from all kinds of applications from console applications to web applications, and from WCF services to mobile applications
- Whenever there is any change in the logic/code of the web services, the clients (the websites that consume the services) do not need to change anything. They can consume the web services just as they were consuming them earlier
HTTP basics
HTTP is a powerful platform for building services. You can use the existing HTTP verbs to build services. For example, you can use the existing HTTP verb GET to get the list of products or POST to update information about the product. Let's take a quick look at how HTTP works with respect to building the services.
There is no difference in the underlying mechanism between serving the HTML pages in ASP.NET MVC and serving the data in the context of HTTP services. Both follow a request-response pattern and the same routing mechanism.
An HTTP request can be sent from any client (desktop, laptop, tablet, mobile, and so on) to the server and the server will respond back with an HTTP response. An HTTP response can be sent to the client in any format such as JSON or XML. This is shown in the following figure:
In the preceding diagram, a request is sent from the desktop computer (it could equally be sent from a mobile or tablet; it makes no difference) and the server sends back the HTTP response for the request. As HTTP is supported in most of the devices, it is ubiquitous.
HTTP verbs
HTTP verbs describe how the request has to be sent. These are the methods defined in HTTP that dictate how the HTTP requests are sent from the client to the server
GET method
When we use an HTTP GET request, the information is passed through the URL itself:
GET api/employees/{id}
This GET
request gets the employee information based on the passed ID. The advantage of using the GET
request is that it is lightweight, and all the required information will be passed in the URL or header itself, as shown in the following diagram:
PUT method
The PUT
method is used to create a resource or to update it. PUT
is an idempotent operation, meaning that the expected behavior would not change even if it is executed multiple times:
POST method
You can use POST to create or update the resource. Usually, POST is used to create the resource rather than update it. As per HTTP standards, whenever you create a new resource, you should return a 201 HTTP status code:
DELETE method
The DELETE method is used to delete the resource. Usually, when you delete a resource, you would be passing the ID as a parameter, and you would not be passing anything in the body of the request:
Usually, HTTP services would be consumed by other applications and services. Applications that consume services are referred to as clients. One of the options to test HTTP services is to build the clients. But this would be time-consuming, and we may throw away the client code once we test the HTTP services.
Another option, which is widely used, is to use applications that enable us to fire HTTP requests and monitor the responses. There are many applications available, Fiddler being one such widely used application.
Fiddler tool
Fiddler is the proxy server application used to monitor the HTTP and HTTPS traffic. You can monitor the requests that are being sent to the server from the client, the responses that are sent to the client, and the responses that are being received from the server. It is like seeing the traffic in the pipe between the server and the client. You can even compose a request, fire it, and analyze the response received without ever needing to write the client for the services.
You can download Fiddler at http://www.telerik.com/fiddler. You'll see the following window:
Enough theory. Let us create a simple web service using ASP.NET Web API.
Fire up Visual Studio 2015:
When you click OK, a Web API solution will be created. Just as the ASP.NET Core application controller inherits from the Controller class.
The Web API class will also inherit from the same Controller class. This is the difference between ASP.NET Core and earlier versions of ASP.NET MVC. In earlier versions, all Web API controller classes inherited from the ApiController
class. In ASP.NET 5, it has been unified, and the same base Controller class is being used for both building web applications and services.
The following is the ValuesController
class that will be created by default when you choose the Web API template option when creating the project:
Before we create our own custom Controller, let's analyze the default API Controller. In the ValuesController
file, several API methods are already defined.
There are two overloaded GET
methods—one with a parameter and another without a parameter. The GET
method without a parameter returns all the resources of the type. In this case, we are returning just a couple of strings. In the real world, we would be returning the metadata of the resources. For example, if we fire the GET
request on the movies API Controller, it would return information about all the movies. The GET
method with an id
parameter returns the resource whose ID matches with the passed ID. For example, if you pass a movie ID, it would return the information about that movie. The body of the other methods, such as PUT
, POST
, and DELETE
, are empty in this Controller, and we will talk about these methods later.
When you run the application, you will get the following output:
By default, it fires a request to api/values
, and the values are displayed in the browser.
Let's learn how to fire an HTTP request from the Fiddler application. Open the Fiddler application. In the bottom left-hand corner, select the Web Browsers option in the red box. Choosing this option will enable us to view the traffic coming from the Web Browsers:
Select the Composer tab, enter the URL
http://localhost:49933/api/values
, as shown in the following screenshot, and click the Execute button in the top right-hand corner:
Once you click the Execute button, an HTTP session will be created, visible on the left-hand side pane (highlighted in the blue box). Click on the session and select the Inspectors tab on the top right-hand side pane. Select the JSON tab in the bottom right-hand side pane (highlighted by the purple-bordered box in the following screenshot).
You can see the JSON data returned from the HTTP request—value1 and value2 in the following screenshot:
Now it's our turn to write a custom API.
In this custom API, we are going to provide API methods to create an employee object, list all the employee objects, and delete an employee object.
First, let us create a model for the employee. We need to create a folder to hold these models. Right-click on the project, select Add | New folder, and name the folder as Models
:
Right-click on the Models
folder and select Add | New Item… to create an employee model class. This employee model class is just a POCO class. See the following code:
public class Employee { public int Id {get; set;} public string FirstName {get; set;} public string LastName {get; set;} public string Department {get; set;} }
Then, we define the repository interface to handle the model:
public interface IEmployeeRepository { void AddEmployee(Employee e); IEnumerable<Employee> GetAllEmployees(); Employee GetEmployee(int id); Employee RemoveEmployee(int id); void UpdateEmployee(Employee employee); }
Then we implement the interface for this model:
public class EmployeeRepository : IEmployeeRepository { private static List<Employee> employees = new List<Employee>(); public EmployeeRepository() { Employee employee1 = new Employee { FirstName = "Mugil", LastName = "Ragu", Department = "Finance", Id = 1 }; Employee employee2 = new Employee { FirstName = "John", LastName = "Skeet", Department = "IT", Id = 2 }; employees.Add(employee1); employees.Add(employee2); } public IEnumerable<Employee> GetAllEmployees() { return employees; } public void AddEmployee(Employee e) { e.Id = GetNextRandomId(); employees.Add(e); } public Employee GetEmployee(int id) { return employees.Where(emp => emp.Id == id).FirstOrDefault(); } public Employee RemoveEmployee(int id) { Employee employee = employees.Where(emp => emp.Id == id).FirstOrDefault(); if (employee !=null ) { employees.Remove(employee); } return employee; } public void UpdateEmployee(Employee emp) { Employee employee = employees.Where(e => e.Id == emp.Id).FirstOrDefault(); if(employee != null) { employee.Department = emp.Department; employee.FirstName = emp.FirstName; employee.LastName = emp.LastName; } } private int GetNextRandomId() { int id = -1; bool isIdExists; Random random = new Random(); do { id = random.Next(); isIdExists = employees.Any(emp => emp.Id == id); } while (isIdExists); return id; } }
There are few things to be noted in the implementation class:
- We have decided not to use the database as our objective is to create an HTTP service using Web API, and not to write the data access code.
- We are using an in-memory list to hold the data. All the operations will be performed on this list. As a matter of fact, the data could be in any form, ranging from relational databases to a simple in-memory list.
- In the constructor method, we are adding an object to the list. This list will be acting as the database for our HTTP service.
- The
GetAllEmployees
API method will return all the employees as theIEnumerable
interface. - The
AddEmployee
method will add the employee (passed as a parameter) to the list. - The
GetEmployee
method will return the employee whose ID matches that of the parameter. - The
RemoveEmployee
method will remove the employee from the list. - The
UpdateEmployee
method will update the employee information. - The
GetNextRandomId
method will return the next available random integer. This integer value is being used to generate the employee ID.
Dependency Injection
In most real-world projects, we do not instantiate any objects using the new
instance in any of the Controllers, the reason being that we don't want to have tight coupling between the dependent components (between the Controller and the repository). Instead, we pass an interface to the Controller, and the Dependency Injection container (such as Unity) will create an object for us when it is needed for the Controller. This design pattern is commonly referred to as Inversion of Control.
Let's say that a class by the name of ClassA uses another class, ClassB. In this case, it is enough for ClassA to know about the behavior, methods, and properties of ClassB, and it doesn't need the internal implementation details of ClassB. So, we can abstract ClassB and make an interface out of the class, and then have that interface as the parameter instead of the concrete class. The advantage of this approach is that we can pass any class at runtime as long as it implements a commonly agreed contract (interface).
In ASP.NET 5 (including ASP.NET Core and Web API), we have inbuilt support for Dependency Injection. In the ConfigureServices
method, we have added the line (highlighted in bold) that performs the Dependency Injection. We instruct the inbuilt Dependency Injection container to create the EmployeeRepository
class wherever we are referring to the IEmployeeRepository
interface and we also instruct it to be a singleton; meaning that the same object (which is to be created by the Dependency Injection container) is to be shared for the entire lifecycle of the application:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
services.AddSingleton<IEmployeeRepository, EmployeeRepository>();
}
In the preceding code, we have used the Singleton pattern for the Dependency Injection, which creates services only the first time they are requested. There are other types of lifetime services such as Transient and Scoped. Transient lifetime services are created each time they are requested and Scoped lifetime services are created once per request. The following are code snippets created when you use such lifetimes:
services.AddTransient <IEmployeeRepository, EmployeeRepository>(); services.AddScoped < IEmployeeRepository, EmployeeRepository>();
Now it's time to get into the meat of the action creating the API controller. Right-click on the Controllers folder and select Add | New Item. Then select Web API Controller Class from the list, as shown in the following screenshot. Name your Controller, and click the Add button:
Remove the generated code in the Controller and add the following constructor:
public EmployeeController(IEmployeeRepository employeesRepo) { employeeRepository = employeesRepo; } private IEmployeeRepository employeeRepository {get; set;}
In the preceding constructor, we are injecting the dependency. At the time of calling this constructor, the EmployeeRepository
object will be created.
Let us implement a couple of GET
methods—the first one will return all the employees' details and the second GET
method will return the employee based on the passed employee ID:
public IEnumerable<Employee> GetAll() { return employeeRepository.GetAllEmployees(); } [HttpGet("{id}",Name ="GetEmployee")] public IActionResult GetById(int id) { var employee = employeeRepository.GetEmployee(id); if(employee == null) { return NotFound(); } return new ObjectResult(employee); }
Let us call these HTTP methods from Fiddler.
Run the solution, open the Fiddler application, and click on the Composer tab.
Select the HTTP method (we have chosen the GET
method as we have a GET API method) and enter the URL
http://localhost:49933/api/employee
.
Please note that when I run my application, it runs on port 49933
; the port number will be different in your case, so construct your URL accordingly.
Once you enter the URL and the method is selected, click the Execute button as shown in the following screenshot:
Once you click the Execute button, an HTTP session will be created, and the request will be fired.
Click on the session on the left-hand side pane (as shown in the following screenshot) and select the Inspectors tab in the right-hand side pane. You can view the result in the JSON tab in the bottom right-hand side pane:
Let us fire another HTTP request to get a particular employee's information, say the employee whose ID is 2. We would construct the URL by appending the ID
http://localhost:49933/api/employee/2
as following:
Select the recently created HTTP session and click on it:
You can see the result in JSON format in the right-hand side pane.
Now, we are going to add Create
, Update
, and Delete
operations to our service. To start with, we are going to provide the Create functionality to add employees' to our service:
[HttpPost] public IActionResult Add([FromBody] Employee emp) { if (emp == null) { return BadRequest(); } employeeRepository.AddEmployee(emp); return CreatedAtRoute("GetEmployee", new { id = emp.Id }, emp); }
The following points should be considered when following the preceding Add
method:
- We are passing the
Employee
object as a parameter. We are instructing theAdd
method to take that object from the body of the request by specifying a[FromBody]
attribute:- If no employee object is passed, we would be returning the bad request to the calling client
- If it is not null, we would be calling the repository method to add the employee to our list (in the real world, we would be adding it to the database)
- Once we have added the employee, we are returning the 201 status code (as per the HTTP standards) when a new resource is created.
Open the Fiddler application and follow these steps to add the employee:
- Select the HTTP method as
POST
and enter the URLhttp://localhost:54504/api/employee/
. - You need to specify the content type as
application/json
in the request header. Please see the following screenshot, where we have addedContent-Type: application/json
to the request header. - As mentioned in the code, we have to pass the employee object in the form of JSON in the body of the request. In the following request, we have formed a JSON that contains the properties of the
Employee
object with the values in the brackets { "FirstName" : "James", "LastName" : "Anderson","Department" : "IT"}:
Once you have composed the request, you can click the Execute button to fire the request. This will return the 201 HTTP status code, which is the standard HTTP response for creating a new resource:
As soon as we have created the resource in the server, we are redirecting the response to get the newly created resource. This occurs when we call the CreatedAtRoute
method with the newly created employee ID passed as a parameter.
Click on the session on the left-hand side and select the Inspector tab in the right-hand side pane. Now you can see the response of the request. The response contains the Employee
object which was newly created in the server. We have to note that the ID of the Employee
object is generated at the server, and is available in the following response. In this case, the ID generated for the employee is 1771082655
:
In the bottom right-hand side panel in the preceding Fiddler window, we can see the complete JSON response of the newly created resource.
Now we are going to add a Web API method to update the resource. The method for updating the resource is very similar to that used to create the resource, with only a few differences. When we created the resource, we used the HTTP POST
method, whereas when we updated the resource, we used the HTTP PUT
method.
If the passed employee ID could not be found in the repository, we return a 404 error response, the HTTP standard error response for a resource that has not been found.
The following is the Web API controller method code for updating the resource:
[HttpPut] public IActionResult Update([FromBody] Employee emp) { if( emp == null) { return BadRequest(); } Employee employee = employeeRepository.GetEmployee(emp.Id); if(employee == null) { return NotFound(); } employeeRepository.UpdateEmployee(emp); return new NoContentResult(); }
The following is the repository layer code for updating the employee:
public void UpdateEmployee(Employee emp) { Employee employee = employees.Where(e => e.Id == emp.Id).FirstOrDefault(); if (employee != null) { employee.Department = emp.Department; employee.FirstName = emp.FirstName; employee.LastName = emp.LastName; } }
Open the Fiddler application, and compose a request of HTTP PUT
. As we are going to pass the Employee
object in the body of the request, we need to mention the content type as application/json
. In the body of the request, we need to supply the Employee
object in JSON format, as shown in the following screenshot:
When you click the Execute button, the HTTP PUT
request will be fired and our Web API method will get called. Once it succeeds, the HTTP 204 response will be returned:
Delete method
The HTTP DELETE
method should be used when deleting a resource. There is no need to pass anything in the body of the request.
The Web API method for deleting a resource
The Delete
Web API method has a void
return type, which will return an HTTP 200 response:
[HttpDelete("{id}")] public void Delete(int id) { employeeRepository.RemoveEmployee(id); }
Web Repository layer code for deleting the employee data
In the following repository layer method, we are removing the employee (whose ID matches with that of the parameter passed) from the internal list of employees. But in the real world, we would be interacting with the database to delete that particular employee. Consider the following code:
public Employee RemoveEmployee(int id) { Employee employee = employees.Where(emp => emp.Id == id).FirstOrDefault(); if(employee != null) { employees.Remove(employee); } return employee; }
Open the Fiddler application, select the DELETE
HTTP method, pass the URL with the parameter, and click on the Execute button. Please note that we are not passing the content type in the request header as we are not passing any employee object in the body of the request:
As we are returning void, the Web API DELETE
method returns an HTTP 200 status, as you can see in the left-hand side pane of the Fiddler application:
Delete method
The HTTP DELETE
method should be used when deleting a resource. There is no need to pass anything in the body of the request.
The Web API method for deleting a resource
The Delete
Web API method has a void
return type, which will return an HTTP 200 response:
[HttpDelete("{id}")] public void Delete(int id) { employeeRepository.RemoveEmployee(id); }
Web Repository layer code for deleting the employee data
In the following repository layer method, we are removing the employee (whose ID matches with that of the parameter passed) from the internal list of employees. But in the real world, we would be interacting with the database to delete that particular employee. Consider the following code:
public Employee RemoveEmployee(int id) { Employee employee = employees.Where(emp => emp.Id == id).FirstOrDefault(); if(employee != null) { employees.Remove(employee); } return employee; }
Open the Fiddler application, select the DELETE
HTTP method, pass the URL with the parameter, and click on the Execute button. Please note that we are not passing the content type in the request header as we are not passing any employee object in the body of the request:
As we are returning void, the Web API DELETE
method returns an HTTP 200 status, as you can see in the left-hand side pane of the Fiddler application:
The Web API method for deleting a resource
The Delete
Web API method has a void
return type, which will return an HTTP 200 response:
[HttpDelete("{id}")] public void Delete(int id) { employeeRepository.RemoveEmployee(id); }
Web Repository layer code for deleting the employee data
In the following repository layer method, we are removing the employee (whose ID matches with that of the parameter passed) from the internal list of employees. But in the real world, we would be interacting with the database to delete that particular employee. Consider the following code:
public Employee RemoveEmployee(int id) { Employee employee = employees.Where(emp => emp.Id == id).FirstOrDefault(); if(employee != null) { employees.Remove(employee); } return employee; }
Open the Fiddler application, select the DELETE
HTTP method, pass the URL with the parameter, and click on the Execute button. Please note that we are not passing the content type in the request header as we are not passing any employee object in the body of the request:
As we are returning void, the Web API DELETE
method returns an HTTP 200 status, as you can see in the left-hand side pane of the Fiddler application:
Web Repository layer code for deleting the employee data
In the following repository layer method, we are removing the employee (whose ID matches with that of the parameter passed) from the internal list of employees. But in the real world, we would be interacting with the database to delete that particular employee. Consider the following code:
public Employee RemoveEmployee(int id) { Employee employee = employees.Where(emp => emp.Id == id).FirstOrDefault(); if(employee != null) { employees.Remove(employee); } return employee; }
Open the Fiddler application, select the DELETE
HTTP method, pass the URL with the parameter, and click on the Execute button. Please note that we are not passing the content type in the request header as we are not passing any employee object in the body of the request:
As we are returning void, the Web API DELETE
method returns an HTTP 200 status, as you can see in the left-hand side pane of the Fiddler application:
Summary
In this chapter, you learned about the HTTP service and its purpose. We discussed how to design and implement the HTTP service using Web API. We used the Fiddler tool to construct the HTTP request and get the response back. We also learned how to write the Web API method to perform CRUD operations end to end, from writing the Web API methods to firing the requests and getting the responses back.