Web API Tutorial: How to Create a Simple HTTP Service using Web API
Introduction
Web API is a framework where you can create Web Services transmitting data over HTTP. In these series of tutorials, we are going to see how to create simple RESTful service with Web API and test it with Postman.
Web API service operations are accessible via a URL like any other URL which we use to browse a website online. Browsing a website is nothing just an HTTP GET request which the content contains the website and interpreted by our browser (let’s say Google Chrome).
I will use Visual Studio 2017 Enterprise and .NET 4.6.2. With a little twist, you can use the older versions of Visual Studio and .NET framework.
Scenario
We are going to use a simple Employee management scenario to develop our services and test it.
Architecture
A simple multi-layer application is considered to develop the service:
-
Entities: contains the data structure used in the system.
-
Business Layer: contains the logics and program flow.
-
Service Layer: contains the service endpoints exposing the Business Layer functionality.
In real word scenarios, we might be having Data Layer to persist data in database which for simplicity I will use an in-memory list.
Normally a client, a web browser for instance, communicating with the API to provides the functionality of the system to the end user. In this tutorial, we are going to use Postman (https://www.getpostman.com/) which is a very handy tool to test our APIs.
Create the solution in Visual Studio
Create an empty solution in Visual Studio
Open Visual Studio, go to File, New, Project. Under Installed, Templates, Other Projects Types, Visual Studio Solutions, select Blank solution and name it EmployeeManagement.
Create Entities Project
Create a class library project to hold the entities. Right click on the newly created solution, Add, New Project, select Class Library and name it EmployeeManagement.Entities.
Remove Class1.cs and add a new class called Employee.cs to the project and modify it as below:
namespace EmployeeManagement.Entities { public class Employee { public int Id { get; set; } public string Name { get; set; } public byte Age { get; set; } public DateTime DateOfBirth { get; set; } } }
Entities are normally mapped to a database structure and has a DAC which manipulates its CRUD operations. For the sake of this tutorial we will add a static list and manage the CRUD in the same entity.
Modifies the Employee class as below:
using System; using System.Collections.Generic; using System.Linq; namespace EmployeeManagement.Entities { public class Employee { public int Id { get; set; } public string Name { get; set; } public byte Age { get; set; } public DateTime DateOfBirth { get; set; } private static List<Employee> _employees; private static int _id = 10000; static Employee() { _employees = new List<Employee>(); } public static void Seed() { _employees.Add(new Employee() { Id = _id++, Name = "Liza", Age = 24, DateOfBirth = new DateTime(1993, 5, 22) }); _employees.Add(new Employee() { Id = _id++, Name = "Macy", Age = 35, DateOfBirth = new DateTime(1982, 3, 7) }); _employees.Add(new Employee() { Id = _id++, Name = "Joe", Age = 28, DateOfBirth = new DateTime(1989, 4, 10) }); _employees.Add(new Employee() { Id = _id++, Name = "Max", Age = 19, DateOfBirth = new DateTime(1998, 11, 15) }); } public static void Add(Employee employee) { employee.Id = _id++; _employees.Add(employee); } public static Employee Get(int id) { return _employees.Where(e => e.Id == id).FirstOrDefault(); } public static List<Employee> List() { return _employees; } public static void Update(Employee employee) { var currenctEmployee = _employees.Where(e => e.Id == employee.Id).FirstOrDefault(); if (currenctEmployee is null) return; currenctEmployee.Name = employee.Name; currenctEmployee.Age = employee.Age; currenctEmployee.DateOfBirth = employee.DateOfBirth; } public static void Delete(int id) { _employees.RemoveAll(e => e.Id == id); } } }
Create Business Layer
Create a class library project to hold the business logics. Right click on the newly created solution, Add, New Project, select Class Library and name it EmployeeManagement.Business.
Add a reference to EmployeeManagmeent.Entities class from EmployeeManagement.Business.
Remove Class1.cs and add a new class called EmployeeBusiness.cs to the project and modify it as below:
using EmployeeManagement.Entities; using System.Collections.Generic; namespace EmployeeManagement.Business { public class EmployeeBusiness { public Employee Create(Employee employee) => Employee.Insert(employee); public Employee Get(int id) => Employee.Select(id); public IEnumerable<Employee> List() => Employee.List(); public void Update(Employee employee) => Employee.Update(employee); public void Remove(int id) => Employee.Delete(id); } }
The EmployeeBusiness should communicate with a data layer to perform CRUD operations. Here we are simulating the such interaction using our static list of employees.
Create the Service Layer
Create a ASP.NET Web Application project to hold the Web API servives. Right click on the newly created solution, Add, New Project, and select ASP.NET Web Application and name it EmployeeManagement.Service.
In template selection choose Empty, and select Web API under Add folder and code references for.
Add references to the Entities and Business projects from the Service project:
Add the new Web API controller to the service project by right clicking on the Controller folder, Add, Controller, and select Web API 2 Controller – Empty. Name it EmployeeController.
HTTP Verbs
We need to add 5 service operations to our service which do the CRUD operation by communicating to the Business Layer. HTTP uses different verbs which each determines the characteristic of the HTTP message set from client to the service. The most famous one to use are:
-
POST: use to create a resource
-
GET: use to retrieve a resource
-
PUT: use to update a resource
-
DELETE: use to remove a resource
Each service operations need to decorate with what verbs it uses.
Route Prefixes
Route prefixes are used to define the relative URL were a specific service operation is accessible. Web API can automatically map the request to the name of the controller. But in this example, we will specify the route prefix ourselves.
We use RoutePrefixAttribute to specify the relative URL on how the operation is accessible:
[RoutePrefix("api/employee")] public class EmployeeController : ApiController { }
So, this service will be accessible through: http://localhost/api/employee/[opration].
Map Routes to the HTTP Verbs
The best practice in service method naming is we use a unify name, only the verb is telling what that method is doing. For example, to create an employee our route will be api/employee with POST verb, and the route to get an Employee is api/employee/{id} with GET verb and the employee id as the parameter. Below is the of routes and corresponding verbs in our service:
-
[POST] api/employee: creates new employee
-
[GET] api/employee/{id}: gets an employee by id
-
[GET] api/employee: gets the list of all employees
-
[PUT] api/employee: updates and employee
-
[DELETE] api/employee/{id}: deletes an employee by id
Implementing Service Methods
Below is a service operation which is going to create an employee:
[Route("")] [HttpPost] public HttpResponseMessage Create(Employee employee) { try { var bc = new EmployeeBusiness(); var result = bc.Create(employee); return Request.CreateResponse(HttpStatusCode.Created, result); } catch (Exception ex) { return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message); } }
The RouteAttribute specified on each method are the relative url from the RoutePrefix. When specifying RouteAttribute Web API use it rather than the method name, so the method name can be anything.
Each method returns the HttpResponseMessage with creates an HTTP response message with a status code and body content.
Request.CreateResponse creates a response with a status code and result. HTTP Status Codes returns the result of the operation, whether it is successful or not, or many other messages related to how the operation processed by the server and its result.
Full implementation of all methods is as below:
using EmployeeManagement.Business; using EmployeeManagement.Entities; using System; using System.Net; using System.Net.Http; using System.Web.Http; namespace EmployeeManagement.Service.Controllers { [RoutePrefix("api/employee")] public class EmployeeController : ApiController { [Route("")] [HttpPost] public HttpResponseMessage Create(Employee employee) { try { var bc = new EmployeeBusiness(); var result = bc.Create(employee); return Request.CreateResponse(HttpStatusCode.Created, result); } catch (Exception ex) { return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message); } } [Route("{id}")] [HttpGet] public HttpResponseMessage Get(int id) { try { var bc = new EmployeeBusiness(); var result = bc.Get(id); return Request.CreateResponse(HttpStatusCode.OK, result); } catch (Exception ex) { return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message); } } [Route("")] [HttpGet] public HttpResponseMessage List() { try { var bc = new EmployeeBusiness(); var result = bc.List(); return Request.CreateResponse(HttpStatusCode.OK, result); } catch (Exception ex) { return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message); } } [Route("")] [HttpPut] public HttpResponseMessage List(Employee employee) { try { var bc = new EmployeeBusiness(); bc.Update(employee); return Request.CreateResponse(HttpStatusCode.Accepted); } catch (Exception ex) { return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message); } } [Route("{id}")] [HttpDelete] public HttpResponseMessage Remove(int id) { try { var bc = new EmployeeBusiness(); bc.Remove(id); return Request.CreateResponse(HttpStatusCode.Accepted); } catch (Exception ex) { return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message); } } } }
Take note of the HTTP Verbs for each method.
Testing the Service
Set the EmployeeManagement.Service as the starting project and run the solution.
I will use Postman to test the service. Run the Postman.
Get a list of employees
URL (GET): http://localhost:22303/api/employee
Result: 200 (OK)
Get an employee by id
URL (GET): http://localhost:22303/api/employee/10000
Result: 200 (OK)
Create an employee
URL (POST): http://localhost:22303/api/employee
Result: 201 (Created)
Note that the Verb for creating an employee is POST. In POST we need to pass the content, Employee in this case, in its body (as JSON). The result is the newly created Employee.
Update an employee
URL (PUT): http://localhost:22303/api/employee
Result: 202 (Accepted)
In this case the there is no result body, only the status code 202 which states the request is Accepted. You can use the Get endpoint with id 10004 to see the changes.
Remove an employee
URL (DELETE): http://localhost:22303/api/employee/10004
Result: 202 (Accepted)
Note that in all cases out URL hasn’t changed, only the HTTP Verb and whether we need to pass id.