Creating an Application from Scratch using .NET Core and Angular — Part 4
In this article we are going to see how to create the API. We are going to implement the Controllers in the Application Layer, create the DTOs which will be used to transfer data between the requests and the results, we are going to use AutoMapper to do the mapping from the Entities to the DTOs, and we are going to use Swagger to document and execute the API.
In this layer we need to add the reference for the Domain and the Infrastructure projects:
Select both projects and click in ‘Ok’:
We are going to work with Data Transfer Objects (DTOs). We are going to use DTOs to send and return data from the API. We never should return our entity (the model class) in the result of the API, and some of the reasons are:
- The model class can have sensitive data that you do not want to return in the API
- The model class have information that is not necessary to return, so you just must return what is really necessary. This way you will not spend resources with unnecessary processes
Let’s create the Dtos folder, and create two folders, one for Book and the other for Category. Inside of these folders, we are going to create the specific DTO for the operation that we need:
As you can see in the image above, we have three kinds of DTOs: one for add, one for edit and one for the result. We need that because we need three different data in each operation. For example, in the Add operation, we do not have the Id of the object, because the object does not exist yet, but in the Edit operation, we need to pass the id together with the other properties from the object. So we need to add in the DTO classes the properties that we need to send to the API, and also we have a specific DTO for the result, that is the DTO that the API will return.
This is the CategoryAddDto:
When we create a category, the only property we need to inform is the name, so in this DTO we just need the name property.
We use DataAnnotation (you can see it on line 7) to set the property as required, and also two return the message from the API if the user tries to make a request without informing the property Name. We also have a DataAnnotation (on line 8) to validate the minimum length and the maximum length of the name.
This is the CategoryEditDto:
To edit a category we need to inform the Id and the Name of the category, so in this class we have those two properties.
This is the CategoryResultDto:
This is the DTO that we will use to return the data from the API.
This is the BookAddDto:
Not all properties of the Book entity is required, so we do not need to set all properties as required. Note that in this DTO we do not have the id property, because when the user create a book, there is no yet for it yet.
This is the BookEditDto:
On this DTO we need to have the id property because when the user tries to edit some property on the Book, it’s necessary to inform the id.
This is the BookResultDto, that is the DTO that we are going to return from the API:
Now that we have the DTOs, we also need to map the properties from the Entity to the DTO. You can do it manually (what I do not recommend), or you can do that using a tool like AutoMapper, which will automatically do the mapping from the entities to the DTOs.
To use AutoMapper we need to install it on the API layer. To do it, open the Package Manage Console, select the API project and execute the command:
Now we need to add the configuration on the Startup class.
Configurations on Startup.cs
All the dependency injection we need to configure in the Startup class. To avoid that this class get too many lines of code, we can create separated files for the configurations that we want to add on the project. So let’s create a ‘Configuration’ folder, and inside of it we are going to create the class DependencyInjectionConfig and the class AutomapperConfig:
This is the DependencyInjectionConfig class, where we have an extension method that we are going to call in the Startup class:
ResolveDependencies it’s an extension method from IServiceCollection. Inside of this method we are adding the Dependency Injection configuration.
There are three lifetimes that can be used to register a service:
- Transient — Transient lifetime services (AddTransient) are created each time they’re requested from the service container.
- Scoped — Scoped lifetime services (AddScoped) are created once per client request (connection).
- Singleton — Singleton lifetime services (AddSingleton) are created the first time they’re requested.
We are using Scoped because then the objects are created just once per request, this way we avoid to use more memory, once it will always refer to the same memory location.
This is the AutomapperConfig class:
On this class we are configuring the AutoMapper, to execute the automatic mapping from our entities to the DTOs. We use the ReverseMap() to set that the mapping can be in both directions, can go from Entity to DTO and also from DTO to Entity.
Now in the Startup class, we need to call in the ConfigureServices method, the extension methods that we created. They are on lines 8 and 12:
Don’t forget to also include the using:
Swagger for Documentation
Before we create the Controllers, we can already configure the Swagger to document the API. You can also use Swagger to execute the calls on your API.
To install Swagger, open the Package Manager Console, select the API project and execute the command:
Now we also need to configure the Swagger in the Startup class. In the method ConfigureServices, add the configuration that are on the lines 12 to 19:
Remember to add the using:
And in the method Configure, add the configuration that is on lines 8 to 12:
We also need to configure our project to open the Swagger page when we start the application. For that, open the launchSettings.json, in the Properties:
In the ‘launchUrl’ (you can see in line 5 and 13), change the value to ‘swagger’ and save the file:
We can now execute the application and we are going to see the Swagger running. To do that, set the API project as the startup project (right click in BookStore.API > Set as Startup Project):
And you also need to select the BookStore.API and click in the green arrow to start the application:
You should see this screen now:
We do not have our Controllers yet, so for now there is only the default controller that is created when we create the project. If you click in the GET /WeatherForecast and click on ‘Try it out’ and click in ‘Execute’ it will be executed the get method, and will return the data:
Now that we have our project configured, and Swagger working, let’s stop the application and create the Controllers.
First we can delete the class ‘WeatherForecast’ and the ‘WeatherForecastController’ that were created when we create the project:
Inside the Controllers folder, we are going to create all our controllers.
To create a controller, right click on the Controllers folder > click on Add > click on Controller:
You can select the empty template:
We are going to create the ‘MainController’. The others Controllers will inherit from this Controller. Decore this controller with ApiController and inherit it from ControllerBase:
We also need two controllers, the CategoriesController and the BooksController. It’s a good practice to create the name of the controllers in the plural.
Before we create the controllers, I will give a basic overview about REST API, the HTTP VERBS, and the Status Code that will be returned from the API.
REST — Representational State Transfer
A RESTfull API is an application program interface (API) that uses HTTP requests to get or manipulate data.
REST, or REpresentational State Transfer, is an architectural style for providing standards between computer systems on the web, making it easier for systems to communicate with each other.
In the REST architecture, the client sends a request to get or modify resources, and the server sends a response to those requests. To do those requests, it is used the HTTP Verbs.
HTTP defines a set of request methods to indicate the desired action to be performed for a given resource. Although they can also be nouns, these request methods are sometimes referred to as HTTP verbs.
The basics verbs are:
- GET — which request a representation of a specific resource. It is used only to return data.
- POST — which is used to submit an entity to a specific resource. We generally use POST to create a new resource.
- PUT — which is used to replace all the properties of a resource. We generally use PUT to update a resource.
- PATCH — which is used to apply partial modifications.
- DELETE — which is used to delete a resource.
There are other verbs, but those are the most common verbs that are used for the basic operations.
The status code is returned in the result of the API. When the client makes a request to an API, it will return a status code from the server after the request is made. The mains status code are:
- 200 — Ok: This status means that the request was successful.
- 201 — Created: This status means that the request has succeeded and a new resource has been created as a result. This is usually returned after a POST request.
- 204 — No Content: We can return this status when we do not want to return anything.
- 400 — BadRequest: This is a generic status for error. It means that the server could not understand the request due to invalid syntax.
- 401 — Unauthorized: This status means that the client is not authenticated, and he should authenticate to do the request.
- 403 — Forbidden: This status means that the client is authenticated but he does not have permission to do what he is trying to do. Unlike 401, the client’s identity is known to the server.
- 404 — Not found: This status means that the server could not find the requested resource.
- 500 — Internal Server Error: This is a generic answer from REST API, it means that the server has encountered a situation it doesn’t know how to handle.
- 503 — Service Unavailable: This status means that the server is not ready to handle the request.
There is others status code, but those are the most commons.
Creating the Controllers
Let’s continue now with the creation of the Controllers. I will show the code and explain what is happening. Let’s start creating the CategoriesController:
On line 12, we are defining the route to access this controller. Besides the URL from the application, we need to add ‘/api/controller-name’ to make the calls. For example, when we start the application, we can call the get books using this URL: https://localhost:5001/api/books. Because we will use the Get HTTP verb, the API understands that need to call the GetAll method.
On lines 15 and 16 we have the private properties, that we are going to use to call the service method, and do the mapping from our model (entity) to the DTO object. Those properties are private because it only be used on this controller.
On line 18 we have the constructor, where we can do our injections to make the calls to the service class and to do the mapping of the entity.
Note that above each method, the attribute
[ProducesResponseType(StatusCodes.xxx)] was added. This is because a method can have more than one type of return, for example, can return a NotFound in case if the recorded doesn’t exist, or can return an Ok if the value is returned. Adding this attribute will produce more descriptive response details for the Web API, so when we check the API in Swagger, we will see more descriptive details about which status codes can be returned by the API.
On line 27 we have the Get method. Note that there is a HttpGet attribute above the name of the method, this means that when we execute a GET operation it will reach this method.
On this method we have a call to the service class, to get all categories. If not found any category, it will return 404 (Not Found).
On line 37 we have the GetById method. We also have a HttpGet attribute, but also have the parameter id and the type of the parameter.
On this method, we have a similar situation from the GetAll method, but in this case, we filter by the category id.
On line 49 we have the Add method, which has a HttpPost attribute. This method will receive an object as a parameter. We do not have a validation to check if already exists a category with the informed name, because this logic is in the service class. We just have a validation to check if the properties were sent correctly, based on the rules that we added on the Dto class.
On line 64 we have the Update method, which has a HttpPut attribute. This method received an id and an object as a parameter. The reason why we have these two parameters, it’s because this is a way to confirm if the parameter id is the same id used in the updated object.
On line 78 we have the Remove method, which has a HttpDelete attribute and will call the method to delete the category.
Now let’s create the BookController:
This controller basically has the same operations as the CategoryController. It just has one extra method (you can see it on line 51), which is the GetBookByCategory. In this method, the client will send a category id as a parameter and the API will return all books that have that category.
You will see that when we try to delete a category that is being used, or when we try to add/update a book or a category using a name that is already being used, we will just receive a bad request, but at this moment it will not return the reason of the bad request. To improve that, you can implement a notification system, which can get the message errors and return on the API.
Now we can execute the application and test our API using Swagger, or you can also use another tool like Postman to do the calls to the API. In the next examples, we are going to use both of them, to show how they work. If you want to use Postman, you can download it here.
This is the BookStore API on Swagger:
First, let’s create a new category using Postman. After you open Postman, you first you need to set the Http Verb that you want to use, in this case is a POST. Then you need to add the correct URL of your API, then you need to click in ‘Body’, select ‘raw’ and select ‘JSON’, add the JSON to send to the API, and click in ‘Send, and the category will be create :
Now let’s add a new book using the Category id 1. For that we are going to use Swagger:
The book was created successfully. Note that in both cases, when we add a new category or a new book, we do not send the id property. But after it is created, we receive the id in the result.
If we try to create a category or a book without inform the name for example, we will receive an error in the result of the API. Those messages it’s from the Data Annotation that we added on the Dtos object:
Let’s create a new valid category:
And let’s create a new book using Postman:
If we call the GET books, it will return all books:
We can also filter the books by category:
We can get the book or category by id:
If we try to update a category or a book, and we inform different id’s, it will return error 400:
So we need to inform the same id:
If we try to delete the category 2, which already has a book using this category, it will not allow:
So first we need to delete all books that are using category id 2, which in this case is just one book:
And now we can delete the category id 2:
If we get the categories, now it will return just one category:
Now we saw that the API is working, so we can commit the code in GitHub.
In the next articles we are going to implement the unit tests and create the SPA application using Angular. You can access the part 5 clicking here.
This is the link for the project in GitHub:
Thanks for reading!