Table of Contents
This article of the series “Web API with ASP.NET Core” will focus on creating Web API with ASP.NET Core. In the last article of the series, we learnt about ASP.NET Core basics and how to set up an empty solution and play with request pipeline and middleware. In this article, we’ll discuss less theory and try to create an API. We’ll make use of ASP.NET Core MVC Middleware, to build the API. We’ll cover in detail how to return resources, data and how to talk to API via HTTP request in this article. We can use the same source code that we got at the completion of the last article of the series.
To build the API, we’ll add a middleware in HTTP request pipeline, and we can do that by adding the middleware in Configure()
method of Startup
class. To do this, we need to add the service to the container. In the earlier version of .NET, we used to build services via Web API in ASP.NET, and for creating an application with user interface and that is used by end user, we used ASP.NET MVC. When we talk about it in terms of ASP.NET Core, we do not have a separate Web API framework now.
The functionalities of MVC and Web API are now combined in one framework, i.e., ASP.NET Core MVC. Its purpose is to build web applications that use APIs and MVC approach. To create an API, we need to configure the middleware and add that to the request pipeline.
We’ll follow a roadmap to learn and cover all the aspects of ASP.NET core in detail. Following is the roadmap or list of articles that will cover the entire series:
- Create API with ASP.NET Core (Day 1): Getting Started and ASP.NET Core Request Pipeline
- Create API with ASP.NET Core (Day 2): Create an API and return resources in ASP.NET Core
- Create API with ASP.NET Core (Day 3): Working with HTTP Status Codes, Serializer Settings and Content Negotiation in ASP.NET Core API
- Create API with ASP.NET Core (Day 4): Understanding Resources in ASP.NET CORE API
- Create API with ASP.NET Core (Day 5): Inversion of Control and Dependency Injection in ASP.NET CORE API
- Create API with ASP.NET Core (Day 6): Getting Started with Entity Framework Core
- Create API with ASP.NET Core (Day 7): Entity Framework Core in ASP.NET CORE API
MVC stands for Model View Controller. MVC is basically an architectural design pattern to define user interfaces. It encourages loose coupling and separation of concern with better testability. It consists of three parts.
The Model takes care of the business logic. In some cases, when the MVC Pattern is only used at the top level of application’s architecture, the model doesn’t contain the logic, but a component of application layer such as business layer handles it. In some implementations, use of View model is made to retrieve and store data. The View is the UI layer, it takes responsibility to show the data in a well-structured format with a rich UI, for e.g., in the form of HTML. The controller takes care of the communication between view and the model, and also makes decision based on user inputs. In terms of dependencies of these components, the controller and the view depend upon model and the controller to some extent also depends upon the view. So a controller makes a decision to choose the view to be displayed to user based on user input and provide the data from the model to the view in case it is needed. But when we talk about building an API, we should see how this structure maps to our need. So in the terminology of MVC, View is basically the data that we get as response, that is, it represents our resources or data in the form of JSON.
In our case, we expect that a request will come from any other user application having a user interface that just wants to access our service, when request comes, an action on our controller will be invoked, the controller will send the input data and a model is returned to the view. View in our case would not be an aspx, razor or html page but will represent result data in JSON format.
Now we’ll try to add ASP.NET Core MVC middleware to our application. In the startup class, we can add the middle ware, but to proceed with that implementation, first add the AspNetCore.MVC
nugget package to the application as the service is defined in the external dependency named Microsoft.AspNetCore.Mvc
Nuget package. Right click on the project and choose “Manage Nuget packages” as shown below.
Now install the latest stable release of Microsoft.AspNetCore.Mvc
package. In our case, it is 1.1.2.
Once installed, all the necessary packages will be added related to ASP.NET MVC. Now in the Startup
class in the ConfigureServices
method, we’ll add the middleware to request pipeline. Just write services.AddMVC()
method to add the service.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
On to the configure
method where we'll now add MVC middleware to the request pipeline. We can call the method app.UseMvc()
in this case; we’ll add this after where we added the exception handler in the first article of the series.
So that our exception handler catches the exception before actually delegating the request to MVC middleware and also handles the MVC related exception and sends the correct response. We can now also comment out the code that we introduced to raise the exception.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler();
}
app.UseMvc();
}
Navigate to project properties and debug tab and change the environment variable to Development (remembered? We changed that to production mode in the last article).
Now run the application, and we get a blank page.
Now open the developer’s tool of the browser by pressing F12. We still see two exceptions there.
We got 404 exception, i.e., Not found status. It is obvious as we just added the MVC middleware but did not do anything else. There is no code to be executed that returns the data, and there is also no routing defined yet. So, let’s create a controller that actually returns the data or resources like Employee
information.
We’ll now add a new controller to our application, since we are in the process of building an Employee
API, we’ll name our controller as EmployeesController
. We can follow the template based approach to add controller with default action methods, but from the learning point of view, we’ll start with a scratch implementation so that we actually know what we are creating and should have full control on our code. So add a new folder in the project and name it Controllers, and add a new class to that Controllers folder and name it EmployeesController
.
The EmployeesController
should derive from Microsoft.AspNetCore.Mvc.Controller
class.
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace EmployeeInfo.API.Controllers
{
public class EmployeesInfoController : Controller
{
}
}
Now we’ll add the action responsible for returning the data. Since we need to return data in JSON format, we can make use of JsonResult
class in that case. This will convert the objects that we pass to JsonResult
class constructor to a JSON format.
public JsonResult GetEmployees()
{
return new JsonResult();
}
Since we need to return the list of employees
, ideally we should have an employee entity class, but for now since we don’t have it, let’s create three anonymous objects and return a list of employees
.
public JsonResult GetEmployees()
{
return new JsonResult(new List<object>()
{
new {employeeid = 01989, Name = "John Patrick"},
new {employeeid = 01987, Name= "Michael"},
new {employeeid = 01988, Name= "Akhil Mittal"}
});
}
When it is required to consume HTTP service, to fetch the data, we need to send an HTTP request to a URI that delegates the request to the “Get
” method of the API. We can send a request from a browser itself or from any tool just for the sake of checking the response of the service. We’ll use Postman for all these request and response tracking operations.
Install Postman to the Chrome Web browser app and open it.
So, let's open up Postman. In Postman, we can create our requests. First run the application and then create a request Uri to fetch the list of employees as http://localhost:2542/api/employees, select GET
method and click Send button to send the request. As a response, we do not get the list of employees
but a status with 404 not found as shown in the following image:
That means that the MVC Framework till now does not know how to map the requested URI, i.e., api/employees to the Get
method or GetEmployees
method. Here comes the concept of routing. Let’s explore that in detail.
The purpose of routing is to match the Uri request to the particular method of the controller. MVC Framework takes the responsibility of parsing the received request and maps it to controllers and respective actions. Routing could be done in two ways, the first is convention based and the second is attribute based routing.
For convention based routing, we need to follow certain conventions as shown below:
app.UseMvc(config => {
config.MapRoute(
name: "Default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" }
);
});
This is similar to what we used in earlier versions of .NET with MVC or Web API. In this case, we need to pass the configuration in app.UseMvc
method to follow the convention based routing. This would map the URI employees/index to the index
method on a controller named Employees
controller. These are traditional ways of routing more suitable for typical MVC application where view will have to be returned. For API based applications, this conventional based routing is not preferred and recommended, but instead attribute based routing is recommended.
Attribute based routing allows us to decorate our controller and actions with the attributes that we want, therefore giving more control over the routing operations. These attributes are provided with a template and Uri is then matched through that template to the specific action/method of the controller.
The most common functionalities of an API are create, read, update and delete operations.
For reading the resources, the suitable HTTP method is “GET
”, so in the code at action level, we use HttpGet
attribute on the code that is responsible for reading and returning the resources. For example, api/employees
will return all the employees
and api/employees/01989
will return the employee
with employee id 01989
.
POST
is specified by HttpPost
attribute at action level. It is responsible for creating a resource or persisting a resource. For example, api/employees
Uri could be used to create an employee
resource.
PUT
and PATCH
are used for update URIs, both are used at action level. There is a minor difference between both of the Http
methods. When PUT
is used, it means it is used for complete update, i.e., all the fields coming in request will overwrite the existing ones, HTTP attribute for PUT
is HttpPut
. In case of PATCH
, it is HttpPatch
, PATCH
means partial update. Unlike PUT
, it does not do a complete update, but a partial one, i.e., few fields can also get updated if we use PATCH
.
The last one is DELETE
, and HTTP attribute is HttpDelete
. It is, as the name says, used for delete URIs, for example, api/employees/01989
will delete the employee
having id 01989
.
There is one more attribute that does not map to any specific HTTP attribute and that is called, “Route
” attribute. This attribute is used at controller level to provide a common template to be prefixed to all action level attributes. For example, if we know that our API URI starts from “api/employees
”, the Route
attribute could be used at controller level with “api/employees
” as a template value for all actions, so we can skip providing this particular value to all the actions.
Now in the earlier to earlier section, we were trying to get the resource via URI. Now we know how routing works, so just add the routing attribute to our action and see how does it works. Since we need to get the employees
, we’ll use HttpGet
attribute over the action. Place [HttpGet("api/employees")]
over the action.
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace EmployeeInfo.API.Controllers
{
public class EmployeesInfoController : Controller
{
[HttpGet("api/employees")]
public JsonResult GetEmployees()
{
return new JsonResult(new List<object>()
{
new {employeeid = 01989, Name = "John Patrick"},
new {employeeid = 01987, Name= "Michael"},
new {employeeid = 01988, Name= "Akhil Mittal"}
});
}
}
}
Compile the application, run it and then again request the resource via URI from postman.
This time, we get the desired result, i.e., the list of employees from our action. Hence, it is proved that the routing and action both are working fine. So we have a working API now?
The controller typically contains other methods that map to CRUD operations and we want each of our actions to have a consistent URI. If we know that all our actions will have “api/employees
” as an attribute, we can make use of Route
attribute at controller level and provide this part of Uri there as shown below:
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace EmployeeInfo.API.Controllers
{
[Route("api/employees")]
public class EmployeesInfoController : Controller
{
[HttpGet()]
public JsonResult GetEmployees()
{
return new JsonResult(new List<object>()
{
new {employeeid = 01989, Name = "John Patrick"},
new {employeeid = 01987, Name= "Michael"},
new {employeeid = 01988, Name= "Akhil Mittal"}
});
}
}
}
In this case, by default, with the Get
Http Verb in the request, our get
method will be called and return the result. Run the application and send the request from postman again to check if this is working or not.
In most cases, one would work with model classes, POCOs, or DTOs, that are then serialized to JSON. We’ll see now how we can improve this implementation.
In this section, we’ll try to avoid returning JSON directly and anonymous objects. So we’ll create a model/entity class for Employee
. Add a new folder named Models to the project and add a class named EmployeeDto
. Add properties like ID
, Name
, Designation
and Salary
to that EmployeeDto
class. One can also add the calculated fields property to the DTO class for e.g., the details of the companies he has worked in the past.
EmployeeDto Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace EmployeeInfo.API.Models
{
public class EmployeeDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Designation { get; set; }
public string Salary { get; set; }
public int NumberOfCompaniesWorkedWith { get; set; }
}
}
There is an important point here that what is returned from or accepted by an API is not the same as the models used by the underlying data store. For now, we’ll work with in-memory data, as focus is on API only.Therefore the in-memory data store will simply work on these DTO classes that we are creating. In the later articles of the series, we’ll see how we can use Entity Framework core to work with database and objects. Since now we need to return the data, so we’ll create an in-memory data repository containing list of employees. Later, we can always use database to fetch the data from and use Entity Framework for communication.
So add a new class named EmployeesDataStore
and create a property named Employees
, i.e., a list of Employee
DTO. And add some dummy data to it.
Create a static
property on our EmployeesDataStore
, and name that Current
, it will return the instance of EmployeesDataStore
. Since it is a static
property and instance remains in memory, we can continue to work on the similar data until we start the application again. Note that this is not an encouraged approach, but we are temporarily following it to understand the topic.
EmployeesDataStore.cs:
using EmployeeInfo.API.Models;
using System.Collections.Generic;
namespace EmployeeInfo.API
{
public class EmployeesDataStore
{
public static EmployeesDataStore Current { get; } = new EmployeesDataStore();
public List<employeedto> Employees { get; set; }
public EmployeesDataStore()
{
Employees = new List<employeedto>()
{
new EmployeeDto()
{
Id = 1,
Name = "Akhil Mittal",
Designation = "Technical Manager",
Salary="$50000"
},
new EmployeeDto()
{
Id = 2,
Name = "Keanu Reaves",
Designation = "Developer",
Salary="$20000"
},
new EmployeeDto()
{
Id = 3,
Name = "John Travolta",
Designation = "Senior Architect",
Salary="$70000"
},
new EmployeeDto()
{
Id = 4,
Name = "Brad Pitt",
Designation = "Program Manager",
Salary="$80000"
},
new EmployeeDto()
{
Id = 5,
Name = "Jason Statham",
Designation = "Delivery Head",
Salary="$90000"
}
};
}
}
}
Now we can move to our controller and modify the implementation. Instead of anonymous properties in the json list, we directly call now EmployeesDataStore Current
property and Employees
from there.
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace EmployeeInfo.API.Controllers
{
[Route("api/employees")]
public class EmployeesInfoController : Controller
{
[HttpGet()]
public JsonResult GetEmployees()
{
return new JsonResult(EmployeesDataStore.Current.Employees);
}
}
}
Now run the application and again request the response from postman.
So, we get all the employees
details as we specified in the DataStore
class.
Now we can also create new actions for e.g. to return a single entity. Add a new action named GetEmployee
, since this is also a Get
, we decorate it with HttpGet
attribute. The URl should be then “api/employees
” i.e., the default one + the id of the employee that is to be fetched, [HttpGet("api/employees/{id}")]
, but since “api/employees
” is the part of default route, we can skip that as it will automatically be appended to the action route. The id is the parameter passed via URI and it has to be the parameter to action as well to fetch the required employee
with ID
.
[HttpGet("{id}")]
public JsonResult GetEmployee(int id)
{
return new JsonResult
(EmployeesDataStore.Current.Employees.FirstOrDefault(emp => emp.Id == id));
}
In the above action method, we find that particular employee
from the list of employee
data store whose id
matches with the passed id
parameter. Now compile the project. Run the application and fire a GET
request from postman, but with a different URI now i.e., http://localhost:2542/api/employees/1.
With http://localhost:2542/api/employees/2:
So, we get only one employee
from the list of employees
, and the id
of the employee
is 1
as passed through the URI. So Routing template matches the correct route to the action and then invokes the action.
Now imagine a scenario where you fire a request with non-existing route or employee, for e.g. http://localhost:2542/api/companies, we get the following 404 response, i.e., Not Found, which is perfectly fine.
If we remember from the previous implementation, we got 500 internal server errors. So, these codes are basically HTTP status codes that are in this case, automatically returned by the framework.
And if we try to get an employee that does not exist, for e.g., http://localhost:2542/api/employees/8, we get 200 OK results with null result data.
The framework itself doesn't return a 404 just because our URI can be routed. But returning null is the incorrect result. So if the employee doesn’t exist, we should also ideally get 404 response from the code or API. We should return correct status codes in every case. We’ll cover status codes and a lot more in the next article of the series. Stay tuned?
In this article, we learned about the MVC pattern and how conventional Web API and MVC is different from what we have in ASP.NET Core MVC. We started by knowing what MVC pattern is, Model-View-Controller. The Model takes care of the business logic of the application data. The View signifies the area that display data, which, in case of an API, is typically in JSON format returned from APIs and the Controller handles the Communication between View and Model. This pattern enables us to write a code which is re-usable and testable. We learned to add MVC middleware and how we can use an HttpGet
to get data from our API. We learned how routing takes care of request URIs and maps to our actions of the controller. We learnt how to create an API from scratch and how to deal with entity objects and return resources. In the next article, we’ll cover topics like HTTP Status Codes, returning sub resources, serializer strings and content negotiation.
<< Previous Article Next Article >>
My series of articles are listed below: