.NET 5 REST API Tutorial
Hey guys, I'm Venkat. In this video, we'll discuss how to build a REST API from scratch. Along the way you'll learn various aspects of building effective APIs using the latest framework from Microsof, .NET 5. You can download the complete project source code from the link shown on the screen. I'll have this link available in the description box below this video. Before we proceed, a quick tip. If a YouTube video is too slow or too fast, you can adjust
the playback speed using this settings icon. If a video is too fast you can reduce the playback speed maybe to 0.75 or even 0.5, if it is too slow you can bump it up to 1.25 or 1.5. I play most YouTube videos at this speed - 1.25. So, in this video, we want to build a REST API from scratch
using .NET 5. Basically, we want to build an API that provides Employee and Department data. For example, we want the employee data to be available at an endpoint that looks like the following. The protocol that is used here is "HTTP", you can also use "HTTPS" to be a bit more secure. "pragimtech.com" is the domain. The path "/api" in the URI indicates that this is an API, this is just a convention that most people follow. With this convention, just by looking at the URL, we can say this is a REST API URL. Finally, "/employees" is the endpoint at which we have the resource, that is list of employees in this case. Similarly,
we want the department's data to be available at an endpoint that looks like the following. So, in a REST API, each resource is identified by a specific URI. For example, the list of employees are available at this URI "api/employees". Similarly, the list of departments are available at this URI - "api/departments". Along with the URI, we also need to send an http verb to the server. The following are the common http verbs. It is this http verb that tells the API what to do with the resource. The following are the four common operations that we do on any
resource. For example - Create, Read, Update or Delete a resource. To create a resource we use the http verb POST. To read - GET, to update - PUT or PATCH and to delete - DELETE. So, the combination of the URI and the http verb that is sent with each request tells the API what to do with the resource. For example, the http verb "GET" to the URI "api/employees" gets the list of all employees. The same http verb GET to this URI, that is "/api/employees/1" gets the employee with ID = 1. POST verb to the URI "api/employees" creates a new employee. The verb DELETE to the URI "api/employees/1" deletes the employee with ID = 1. The verb PUT or PATCH to the same URI updates
employee with ID = 1. What's the difference between PUT and PATCH? Well, PUT updates the entire object, that is FirstNname, LastNname, DateOfBirth, Gender, Email etc of an employee, basically all the properties of the object are updated. PATCH is used when you want to do a partial update, that is only a subset of the properties, maybe just FirstName and Gender of an employee object. If you're new to REST APIs, we discussed "What is a REST API" in detail in this video. Solution Layout. This is the same project we've been working with so far in this video series. Take a look at the projects we have in the solution explorer. We created this project using "Blazor Webassembly App" template. We have three projects generated by visual studio 2019.
BlazorProject.Client - This is a Blazor web project that runs on the client side in the browser. BlazorProject.Server - this project does two things, contains REST API that provides data to Blazor client project and also hosts the Blazor client project. BlazorProject.Shared - as the name
implies, this project is shared both by the client and server projects. It contains the model classes used by both the projects. At the moment, as you can see in the shared project, we have two classes, that is Department class in this file Department.cs, and as you can see this class is pretty straightforward, it has just two properties "DepartmentId" and "DepartmentName" and, if we take a look at this file "Employee.cs", we have Employee class here. We have several properties
here - EmployeeId, FirstName, LastName, Email etc. These are pretty straightforward. Notice this property here, DepartmentId, basically it is of type integer, so it contains the integer value of the department to which this employee belong to and then, we also have a navigation property here "Department" and the type is Department. So, basically this property contains both the integer DepartmentId value as well as the DepartmentName. So, if we have a web page where we are displaying a department name, we don't have to make another round trip to the database, this property comes in very handy and then, in this file "Gender.cs", we have our Gender enum with three options - Male,
Female, Other. Next, adding database support. To add database support, we'll use Entity Framework Core 5. Install these three nuget packages in the order specified. First, "EntityFrameworkCore", followed by that, "EntityFrameworkCore.SqlServer" and finally, "EntityFrameworkCore.Tools". In visual studio, there are several ways to install nuget packages. I'm going to use package manager console. If you don't see the package manager console option here, click on "View - Other Windows" and then "Package manager console". In the package manage console,
from the "Default project" drop-down list, make sure you have "BlazorProject.Server" selected, because it is this server project that uses Entry Framework to retrieve data from the underlying database and here is the command to install our first package - "Microsoft.EntityFrameworkCore". Finally "EntityFrameworkCore.Tools". Done, all the three required packages are installed. One of the very important classes in Entity Framework Core is the "DbContext" class. This is the class that we use in our application code to interact with the underlying database. It is this
class that manages the database connection and is used to retrieve and save data in the database in the server project. First, let's create "Models" folder. In this folder, let's add a new class file. Name it "AppDbContext". To use Entity Framework Core built in "DbContext" class
within our application, we create a class that derives from the DbContext class. Bring in the required namespace - Microsoft.EntityFramework. Next, include the constructor. In the constructor, include DbContextOptions object and then pass this options object to this base class, that is DbContext class constructor and for that, we simply use the "base" keyword and to it, pass the options object. If you're wondering, why is this options object required? Well, for the DbContext class to be able to do any useful work, it needs an instance of the "DbContextOptions" class. It is this instance that carries configuration information such as the connection string, database provider to use etc. At the moment, in our application we have two entities - Department and Employee. So, within this AppDbContext class, we need two DbSet properties - one for
the Employee entity and the other for Department. Let's bring in the required namespace. We'll use these DbSet properties to query and save instances of Employee and Department classes. So, the LINQ queries against these DbSet properties will be translated into SQL queries against the underlying database. Now, here's the important point to keep in mind, for each of these DbSet properties, Entry Framework will create a table in the underlying database. Since we have two DbSet properties,
two tables will be created - Employees and Departments. Now, what we want to do is, include some initial seed data in both these tables and we do that by overriding - OnModelCreating method. Notice, the moment I type "override" and press spacebar, we see all the methods that we can override and the method that we want to override is "OnModelCreating". In the interest of time, I'm going to paste some code here and if you look at this code, it's pretty straightforward.
First, we have the code to seed Departments table. In the departments table we'll have four rows - IT, HR, Payroll and Admin, and then we are seeding Employees table. Next, we need to include the database connection string in this file - "appsettings.json". Let me
include the database connection string at the top. I'm using Microsoft SQL Server Localdb. So, the server is "(localdb)\MSSQLLocalDB", the database name is "EmployeeDB", but you can give it any meaningful name you want. I'm using integrated windows authentication, so "Trusted_Connection=true". Next, we need to configure SQL Server services and we do that in ConfigureServices() method of the Startup class, and the Startup class is present in this file - Startup.cs. In this ConfigureServices() method, to configure SQL Server, all we need is, this one line of code.
Notice, on this incoming "IServiceCollection" object, we're using AddDbContext method to specify our specific application db context class, and if you remember, our application db context class is present in this file AppDbContext.cs, and the class name is "AppDbContext" and this is the namespace in which it is present, so let's first bring in that namespace by pressing ctrl period and "UseSqlServer" is present in a different namespace, again, let's bring in the required namespace. If we take a look at our application configuration file, that is this "appsettings.json", notice our database connection string key is "DBConnection" and we're using this same key here in ConfigureServices() to read the database connection string from appsettings.json.
Next, we need to create and execute database migrations. To create a database migration, we use "Add-Migration" command. To execute and apply a migration on the database, we use "Update Database" command. If you're new to migrations, we discussed them in detail in this Part 50 of ASP.NET Core Tutorial. In visual studio, to create a migration, go to package manager console. In the "Default project" drop down list, make sure our Blazor server project is selected and the command to add a migration is "Add-Migration". We also have intellisense and auto completion available. Type part of the command and then press the "tab" key, the command that we want
is "Add-Migration". Use the up and down arrow keys to select the command and then press the "tab" key again. Let's name this migration "InitialCreate" and then press the enter key. There we go, we have migration added and if we take a look at our server project, notice, now we have this "Migrations" folder and within this, we have this "InitialCreate" migration and here, we have all the code required to create the database and the respective tables. Next, we need to apply this migration and, to apply the migration, again, we go to the package manager console and the command is, Update-Database. Migration successfully applied. If we now go to "SQL Server Object Explorer", by the way,
if you don't find "SQL Server Object Explorer" here, click on "View" and then, "SQL Server Object Explorer". So, within "SQL Server Object Explorer", expand "SQL Server" node and then "(localdb)\MSSQLLocalDB", "Databases", "EmployeeDB" database, that's the database name we specified in our application configuration file, that is appsettings.json and if we expand our database, and then "Tables", we see both the tables here - "Departments and Employees". Let's see the data that we have in the "Departments" table. Notice, we have our seed data in it and similarly, in the "Employees" table also we have our seed data. Next, we're going to use Repository Pattern to work with data our API needs. So, what is a repository pattern? Well, it's an abstraction of the data access
layer. It hides the details of how exactly the data is saved or retrieved from the underlying data source. The details of how the data is stored and retrieved is in the respective repository. For example, you may have a repository that stores and retrieves data from an in-memory
collection. You may have another repository that stores and retrieves data from a database like SQL Server for example, yet another repository that stores and retrieves data from an xml file. Now, take a look at this interface. This is the repository interface, as you can see, this interface only specifies what operations, that is methods are supported by the repository. The data required for each of the operations, that is, the parameters that need to be passed to the method and the data the method returns. As you can see, the repository interface only contains what it can do but not, how it does what it can do. The actual implementation details
are in the respective repository class that implements this repository interface. We want this employee repository to support all these operations - Search employees by name and gender, get all the employees, get a single employee by id, get an employee by their email address, add a new employee, update and delete an employee. The details of how these operations are implemented are in the repository class that implements this IEmployeeRepository interface.
We want to add employee repository interface in the "Models" folder. So, let's right click on the "Models" folder, add new item, we want to add an interface, so, select that and the name of the interface is "IEmployeeRepository" click "Add". Make the interface public and let me paste all the operations that we have just seen on the slide, bring in the required namespace of this "Employee" class and "Gender" enum. Next, we need to provide implementation for this interface "IEmployeeRepository". So, let's add the implementation class again in the same "Models" folder.
We want to be able to retrieve and store employee objects in a sql server database. So, let's make this class implement the interface "IEmployeeRepository". To be able to interact with SQL Server database, we need an instance of our application db context class and that class is again present in this file "AppDbContext.cs" in the "Models" folder and this class inherits from the built-in "DbContext" class. So, this class knows how to retrieve and store employees from a sql server database. So, let's inject this class into our employee repository using a constructor Let's call the parameter "appDbContext" and use control period to generate the private field.
We are using dependency injection here to inject an instance of AppDbContext class. If you're new to dependency injection, we discussed it in detail in part 19 of asp.net core tutorial. First, let's provide the implementation for this "AddEmployee()" method.
On the injected "appDbContext" instance, we have "Employees" collection property. To this collection, we want to add a new employee. So, for that we have AddAsync() method. To this method, let's pass the incoming "employee" object. As the name implies, this method is an async method. So, let's await its execution and then store the result in a variable called "result". Since,
we are using "await" keyword here, we have to turn this method into an "async" method. Next, to save this "employee" object in the underlying SQL Server database, on the injected AppDbContext instance, we have SaveChangesAsync() method, again, this is an async method, so let's "await" its execution as well, and then, finally we want to return the added employee back to the caller. For that, on the "result" object, we have "Entity" property, we want to return it, so let's use the "return" keyword. Next, let's provide implementation for
this "DeleteEmployee()" method and here is the code for that. First, let's fix these compilation errors, we are using "await" keyword, so let's turn this method also into an "async" method and this FirstOrDefaultAsync() method is present in "Microsoft.EntityFrameworkCore" namespace, so let's bring that in. To be able to delete an employee we need the respective employee id and that is passed into this method as a parameter. We're using that parameter and trying to find if we have such an employee in the underlying "Employees" database table. If result not equal to null, meaning if we have found the employee, we are then removing that employee from the "Employees" collection property on our "AppDbContext" instance and then calling SaveChanges() to remove that employee permanently from the underlying database table. Next, let's
provide implementation for this GetEmployee() method. As usual, to fix the compilation errors, let's turn this method into "async". This method finds employee by id and returns that employee. The employee id is passed as a parameter, so, as usual on the DbContext instance, on the Employees collection property, we are trying to find if we have an employee with the incoming employee id. If we have, we are returning that employee back, but what does this "include" doing here? Well, if you take a look at the definition of our "Employee" class, notice, we have Department property. So, when we return the employee instance, we also want to populate this "Department" property and to do that, we are using this "Include" property to include Department data from the underlying "Departments" table as well. Next, let's provide implementation for this GetEmployeeByEmail() method.
Same idea here, on the "Employees" collection property, on our AppDbContext instance, check if we have an employee with the provided email address? If we do, then return that employee. Let's not forget to turn this method into an "async" method. Similarly, we want our GetEmployees() method also to be "async", so, first let's convert it into an async method. As the name implies, this method returns all employees. So, on the Employees collection property, we are calling ToListAsync() and returning that list. Next, Search(). As usual, let me paste the code and turn this method into an async method. We want to be able to search employees by both "Name" and "Gender". Both of them are being passed as parameters and then,
we are building our query dynamically here. First, the query will contain the collection of all employees and then, if the incoming "name" is not null or empty, meaning, if a name is provided, we are building the "where" clause dynamically, and if you look at the "where" clause here, we are checking both FirstName and LastName, and at the moment, we're using "contains", we can also use "Startswith" or "Endswith" depending on our search requirement. Similarly, if the incoming "gender parameter" is not null, meaning, if a value is provided for "gender", then we are adding another condition here. Finally, returning that list back to the caller.
So, pretty straightforward search here. Finally, let's provide implementation for "Update" method and here's the code for that. As usual, let's convert this method to "async" first. We pass the employee object that contains our changes as a parameter to this "UpdateEmployee()" method and then, we are using the "Employee ID" property on this incoming employee object to find that respective employee in the underlying database table. If we have found the employee, then we are overriding all the properties of the existing employee with the values that we have in this incoming employee object and then finally call SaveChangesAsync() on the "AppDbContext" instance and then return the updated employee object back. Just like employee repository, we need another repository for "Departments". Here's the Department repository interface,
pretty straightforward, at the moment it only supports two operations, GetDepartments() - as the name implies this method is going to return us the list of all departments. GetDepartment() - this method returns a single department by id. We provided the ID, and this method returns the matching department. Here is the implementation. The pattern is very similar to Employee repository. First, we inject our application db context class using
dependency injection and then this GetDepartment() method looks up the department by id and returns that department, and GetDepartments() returns the list of all departments. In the interest of time, I've already added both these files, that is IDepartmentRepository and its implementation, DepartmentRepository. Again, both these files are present in this same "Models" folder. In ConfigureServices() method of the "Startup" class we need to include these two lines of code, why? Well, we need to tell dotnet which implementation to use. With these two lines of code in place, an instance of DepartmentRepository class is provided, when an instance of IDepartmentRepository is requested. Similarly, an instance of EmployeeRepository class is provided
when an instance of IEmployeeRepository is requested. At the moment, if you notice, we're using AddScoped() method because we want the instance to be alive and available for the entire scope of the given http request. For another new http request, a new instance of EmployeeRepository class will be provided and it will be available throughout the entire scope of that second http request. In addition to AddScoped(), we also have AddSingleton() and AddTransient() methods. We discussed the difference between these three methods in detail in this part 44 of asp.net core tutorial. In the interest of time, in ConfigureServices() method of our "Startup" class, I've already included those two lines of code we have just seen on the slide. So, basically, these
two lines of code tie the repository interfaces with their respective implementation classes. What are the benefits of a repository pattern? Well, there are many. The code is cleaner and easier to reuse and maintain. Enables us to create loosely coupled systems, for example,
if we want our application to work with Oracle database instead of sql server database, implement an OracleRepository that knows how to read and write to Oracle database and register "OracleRepository" with the dependency injection system. In an Unit Testing project, it is easy to replace a real repository with a fake implementation for testing. Our next step, is to create the REST API itself. In .NET, to create a REST API,
we create a controller class that derives from the built-in "ControllerBase" class. Actually, our controller class can either derive from the built-in "Controller" or "ControllerBase" class. One confusion here is, which built-in class to use as the base class? Well, the answer is very simple. If you are creating a REST API, make your controller class derive from "ControllerBase" and not "Controller" class. "Controller" actually derives from "ControllerBase" and it adds support for MVC views. So, create a controller that derives from "Controller" class if you're building
an MVC web application. On the other hand, if you are creating a REST Web API, create a controller class that derives from "ControllerBase" class. So, in short, "Controller" is for MVC web applications and "ControllerBase" is for MVC Web APIs. If you are planning to use the controller
both for a web application and for a web API, then derive it from the "Controller" class. In our Blazor server project, in the "Controllers" folder, let's add a new "Controller". Let's name it "EmployeesController". In our case, we're building a Web API and not a web application,
so let's make our controller class derive from controllerBase". Bring in the required namespace. Since we are building a web api controller, we also need to decorate our controller class with [ApiController] attribute. To specify the route at which this controller is available, we use the [Route] attribute. We want all our API controllers to be available at this path - "api/the name of the controller". To specify the name of the controller, within square brackets, we use
the word controller. With this in place, if the name of the controller is "EmployeesController", then this controller is available at the path - "api/employees", and if the name of the controller is "Departments", then it's available at the path - "api/departments". Now, we want our employees controller to be able to retrieve data from sql server database. If you remember, this employee repository does exactly that. So, we want to inject the interface IEmployeeRepository into our "EmployeesController". For that, we need a constructor. Using this constructor, let's
inject the interface "IEmployeeRepository". We need to bring in the required namespace as well, let's do that by pressing ctrl period and let's call the parameter "employeeRepository" and also generate the required private field by pressing control period again and then select the second option. Next, let's include a method that's going to return the list of all employees. This method is going to be - "public async", and, it's going to return
Task<ActionResult> and let's call this method "GetEmployees()". When a GET request is issued to this path "api/the name of the controller", in our case "employees", we want this GetEmployees() method to be called and to specify that, we decorate our method with [HttpGet] attribute. If we want a method to handle http post request, then we decorate it with [HttpPost] attribute.
We'll discuss these common http attributes, that is, http post, put and delete in just a bit. For now, let's make this GetEmployees() method return the list of employees, and for that, we're going to use this injected EmployeeRepository. Notice, on this EmployeeRepository instance, we have GetEmployees() method that's going to return the list of all employees and this GetEmployees() method is an async method, so let's await its execution. When I hover the mouse over this GetEmployees() method, notice, it returns an IEnumerable<Employee> objects and this is what we want to return from this GetEmployees() method, so let's use the "return" keyword, and I'm also going to wrap this result using the built-in "Ok()" method. If you're wondering, where is this "Ok()" method coming from? Well, it's coming from this "ControllerBase" class from which our EmployeesController is deriving from. When I hover the mouse over this Ok() method,
notice, this method returns the http status code "200 OK" along with the list of employees. What we are building here is an API, it's common for an API to return an http status code. These http status codes tell the client, that is the caller of our API, the status of the request.
For example, when this GetEmployees() method completes execution successfully, it returns the list of employees along with the status code 200 OK. Some of the common http status codes are listed here. 200 OK, if the request has completed successfully. When we create a new resource, for example when we create a new employee, we use the status code "201 Created". If a resource
cannot be found, for example if we cannot find an employee with the provided id, we use the status code "404 Not Found". Similarly, if there is an internal server error processing our request, we use the status code 500. You can find the complete list of http status codes and their use on this Wikipedia article. When this method, GetEmployees() completes execution successfully, then we want to return the list of employees along with the http status code "200 OK", but, what if there is an error processing this request? Well, in that case we want to return the http status code 500 which indicates there is an internal server error processing this request. So, let's wrap this line of code in a "try catch" block, for that, simply type the word "try" and then press the "tab" key twice, it automatically generates the stub as you can see right here, and let's move this call to "GetEmployees()" method inside the "try" block. If there is an
exception executing this line, we want to return the http status code 500, for that we use StatusCode() method and we also use the enum "StatusCodes". This enum is in a different namespace, so let's bring the namespace in, and notice from the intellisense, there are several status codes here, status 200 OK, status 204 no content, in our case we want to return, status 500 internal server error and then we can also include a message. So, if the request completes successfully, then we return the list of employees along with the status code 200 OK. If there is an exception, we return the status code 500. On this slide, you can see the common http status codes along with the built-in helper methods that we can use to return these status codes. For example, to return the status code 200, we use
the built-in method Ok(), for 201 created(), for 404 NotFound() and for returning 500 series http status codes, we use the StatusCode() method, we've seen this method in action just now. At this point, let's run our application by pressing ctrl f5 There we go, we see the Blazor web page, but what we really want to do is invoke this GetEmployees() method of EmployeesController and the path to get to that is, "api/the name of the controller", the name of our controller is "employees", so we use this path to get to our api "/api/employee" There we go, we have all our four employees in JSON format as expected. Now, let's quickly test our API in postman as well. I have postman up and running. In this text box, enter the API URL and we want to issue a GET request. So, from this drop down list, make sure the http verb GET
is selected and then click this button "Send". Request completed and we are looking at the response body at the moment, and notice, we are on "Pretty" tab, meaning, the JSON result that we have is formatted so it's pretty and easier on the eyes. If you want to see the raw JSON data, click on this tab "Raw". So here we see all of our four employees in raw JSON format, and here on the
right hand side, we also see the http status code 200 OK. Next, let's see how to retrieve a resource by id, for example, employee by id. At the moment, when we issue a GET request to this URI "api/employees", we get the list of all employees. Now, we want to retrieve a specific employee,
for example employee whose id is one. For this, again, we issue a GET request, but this time, the URI is, "/api/employees/1". The value "1" is the id of the employee. In the EmployeesController, let's include another method. This method is going to retrieve employee by id and it's going to be very similar to this GetEmployees() method. So, let's make a copy of this and then change the bits that are required. First, let's change the name of the method from GetEmployees() to GetEmployee(), singular, because this method is going to return just one employee, and we want to pass the id of the employee whose details we want to retrieve as a parameter.
Let's also change the return type. We know this method is going to return a single employee, so let's change the return type to Task<ActionResult<Employee>>. Now, here's the important bit. I'm going to slightly modify this [HttpGet] attribute. First, I'm going to include a pair of parentheses, and then a pair of double quotes, and then finally, a pair of curly braces and then id. What we are doing here
is including an extension to our API route. So, if we navigate to this route "api/employees", then this "GetEmployees()" method is called and it returns the list of all employees, but in the URI, if we have the "id" of the employee, for example, if we navigate to "api/employees/10", 10 is the id of the employee, then we want this GetEmployee() method to be called, and it should return that employee whose id is one, and to specify that, we are including an extension to our route. So, all that is left right now is to use this incoming "id". So, whatever "id" value that we have in the URI is automatically mapped to this "id" method parameter, and using that we can retrieve the specific employee. On our employee repository, we already have GetEmployee() method, and to this method we pass the incoming "id". Let's store the result that we get back in a variable, name the variable "result". If "result" is null
that means, we have not found the employee with the provided id, so we want to return, 404 not found, http status code, for that, we use NotFound() built-in method else, we return the result, in our case, the result is the single employee object we have found. Dotnet is going to automatically serialize this employee object to json format and write it to the response body. This response body along with the http status code 200 ok is then returned to the client, that is to the caller of our API. Now, here's another important point to keep in mind, on this employee "id" route parameter, we can also include a route constraint, and we do that by including a colon and then the data type that we are expecting, in our case employee id is an integer, so we specify "int". With this change in place, this URI
is only mapped to GetEmployee() method, if the id value data type is integer. If it's of any other data type, then this URI is not mapped to GetEmployee() method. At this point, let's run our project and test this GetEmployee() method in Postman. In the URI, let's include employee
id value. In this case, I included "1" and we want to issue a GET request. So, let's click "Send". There we go, we have the respective employee details along with the http status code 200 OO. Now, let's include an "id" value that does not exist, for example we don't have an employee with value 10, so let's click "Send". Notice, now we have status code 404 not found.
Next, let's see, how to create a new employee, that is implement POST in a REST API. Now, to get the list of resources, in our case list of employees, we issue a GET request to this URI "api/employees". To get a specific employee, again, we issue a GET request, but this time in the URI, we include the "id" of the employee whose details we want to retrieve.
To create a new resource, that is in our case to create a new employee, we issue a POST request to this URI. Notice, the word "employees" is plural, and posting to this collection URI "api/employees" makes sense because to this collection of employees, we want to add a new employee. In EmployeesController, we need a new method to implement post. The signature of this new method is going to be somewhat similar to this GetEmployee() method, so let's make a copy of this method and then change the bits that are required. First, to keep the method name meaningful, let's change it from GetEmployee() to CreateEmployee().
Next, pass the employee object that we want to create as a parameter to this CreateEmployee() method. So, the data type is Employee and let's also call the parameter "employee". The created employee object will be returned back, so the return type of this method is Task<ActionResult<Employee>> and to implement POST, we use the [HttpPost] attribute. Now, let's replace all these lines of code in the "try" block with this one line. What we are doing here is returning 200 status code for now. So, let's place a breakpoint here on this line and then run this API project in debug mode.
To create a new employee, to this collection URI "/api/employees" we issue a POST request and we also need to send employee data along with the request. We do that using request body, so click on "Body" and we're going to send the JSON data in raw format. So, from this drop-down list, we select "Raw" and then we also need to select the format. We are going to send the data in JSON format. So, I select that and then we include our employee object right here.
We don't have to provide a value for this "employeeId" property, why? Well, because "EmployeeId" column in the underlying "Employees" database table is an identity column, this means SQL Server will automatically provide the value. It also automatically populates this property upon successful employee creation. We'll see that in action in just a bit. For now, let's remove this property and then issue a POST request by clicking the "Send" button.
Our breakpoint is hit and notice, when I hover the mouse over this "employee" parameter, we can see, the employee data that we have in the request is automatically mapped to the properties on this "employee" object. Notice, the value of "EmployeeId" property, it's "0", why? because we didn't supply a value for this property and the data type is integer, the default value for integer is "0" which is what is used as the value at the moment, but upon successfully creating a row for this employee in the "Employees" database table, sql server will automatically provide a new identity value and this property will be updated with that new identity value. We'll see that in action in just a bit, but here is the important question that we should be asking at this point. How is dotnet able to map the employee data that we have in the request in JSON format to the respective properties on this "employee" parameter? Well, that's happening by model binding and model binding is working as expected at the moment because we have decorated our EmployeeController with [ApiController] attribute. So, for model binding to work,
that is for dot net to be able to map the employee data that we have in the request to the respective properties on this "employee" parameter, we should do one of the two things - either decorate our [EmployeesController] with [ApiController] attribute or decorate this method parameter with [FromBody] attribute. So, now let's stop debugging and implement the rest of the code. In the interest of time, let me paste the required code. I'll walk you through this code in just a bit. Before we forget, let's change the error message here to "Error creating new employee record". Now, we're first checking if this incoming "employee" parameter is null, if it is null, then the request is a bad request, why? because we cannot create a new employee row without employee data, and as you can see from intellisense, the status code for bad request is 400. If employee parameter is not null, then we are passing it to AddEmployee() method of our EmployeeRepository. This method will create a new row for the employee
in the database table and the newly created employee object is then stored in this variable. Now, here's the important bit to understand, what is this line of code doing? Well, when a new resource is created, we usually do the following three things. Return the http status code 201, to indicate that the resource is successfully created. We also return the
newly created resource, in our case the newly created employee object. Finally, location header in the response. The location header specifies the URI of the newly created employee object. This seems like a lot of work, but it's actually very easy to implement than it sounds. We are using the built-in CreatedAtAction() method. Notice, when I hover the mouse over this method, from the intellisense you can see this method returns the status code 201, to indicate that the resource is successfully created. Keep in mind, on a successful post one of the things that we have to do is, in the response, include the location header, that is the URI at which the newly created employee is available. For example, let's say this method creates a
new employee with id value of 5, so this newly created employee will be available at this uri "api/employees/the employee id value", in this case "5". If you recollect, it is this GetEmployee() method that returns employee by id and we are using this method to generate the location URI, and notice, here we're using "nameof" keyword instead of hard coding the method name in a string, and the obvious benefit of this is, later if we rename GetEmployee() method and we forget to change it here, the compiler will immediately flag it as an error, and for this GetEmployee() method to be able to retrieve employee, it needs the employee id value and notice the parameter is named "id" and we have to supply the newly created employee id value, so we are using an anonymous object for that, and obviously the parameter name is "id", and where are we getting the newly created employee id from? Well, we have it in this variable. So, "createdEmployee.EmployeeId" and then the last parameter is the newly created employee object
itself. So, with all these changes in place, let's build our project and test it again using postman. In postman, to this collection URI "api/employees", we want to issue a POST request. Along with the POST request, you also want to send the employee data and we do that using request body, and I already have the employee object here, and within request body make sure from the first drop down you have "raw" selected and in the second drop down "JSON" selected. When creating a new employee, we don't have to supply a value for "employeeId" property, so let's remove this from the object we are sending to the server and then click "Send". Request completed, but we have http status code 500 internal server error, and the error message is, error creating new employee record. It is this same exception message that we have
right here. So, let's see what exception we are getting. For that, let me include a variable for this exception parameter, put a breakpoint and then run our project in debug mode. Issue POST request again. Our breakpoint is hit. When I have the mouse over this
exception parameter, take a look at the exception message we have, "Cannot insert explicit value for identity column in table departments". We are trying to insert a row in "Employees" table, why is it complaining about "Departments" table? Well, that's because, if we take a look at the request we have in postman, notice, we're sending an entire "Department" here. So what entity framework is trying to do is, create a row for department with department id 1, and if you remember "DepartmentId" column in the "Departments" table is an identity column and we don't have to supply a value for the identity column explicitly and that is the exception that we are getting. There are several ways to fix this. One of the ways is to simply set "Department" property to null when we are issuing a request. Since, we already have "DepartmentId" integer property here, this value will be stored in the "DepartmentId" column in "Employees" table, and another way is to tell entity framework to ignore this "Department" entity and this is better because, when a client sends "department" data, we don't want an exception like this. So, let's tell entity framework to not do anything when a department is sent along with the employee object, and we do that in "EmployeeRepository". So, we have our EmployeeRepository here
and AddEmployee() method. This is where we tell entry framework to ignore the "department" entity. So, let's stop debugging. If "Department" property on the "employee" object is not null, we are telling, you know, the state of "Department" entity is unchanged. So,
entity framework is not going to try and create a new entry for the department in the "Departments" table. With these changes in place, let's build our project and test again in postman. Issue POST request again. Request completed with http status code "201 created", and we have our newly created employee object here. Take a look at the "EmployeeId" property, the value is 5, and if we take a look at the response headers, specifically the location header, we have the URI where our newly created employee object is available "/api/employees/5". Let's copy this URI and issue a GET request to it. There we go, status code 200 OK along with the employee object in the response body. Next, let's understand how to implement model validation in a REST API. ASP.NET 5, provides
several built-in attributes for model validation. Required attribute, this attribute specifies a field is required. Range attribute specifies the minimum and maximum allowed value. Minlength specifies the minimum length of a string. MaxLength, maximum length of a string. Compare, compares two properties of a model, for example, compare "email" and "confirm email" properties. Regular expression validates if the provided value matches the pattern specified by the regular expression. Let's see some of these validation attributes in action.
In our solution, model classes are present in this project "BlazorProject.Shared". To be able to use the built-in validation attributes, we'll have to bring in a nuget package, so let's do that using package manager console. Within the package manager console, from this "Default project" drop down list, select "BlazorProject.Shared" and then execute this command "Install-Package System.ComponentModel.Annotations" There we go, package installation complete. Now, in a .NET 5 REST API,
to implement model validation, all we need to do is, decorate the respective model class properties with validation attributes. In our case, we want to implement model validation for our "Employee" model class, so let's open "Employee.cs" from our "BlazorProject.Shared". Let's make this FirstName property, a required property, and for that, all we need to do is decorate it with [Required] attribute. This attribute is in a different name
space, so let's bring that in by pressing ctrl period. let's also make "LastName" required. While we are here, let's also enforce minimum length validation on FirstName, we want first name to contain at least 2 characters. In postman, notice, I have deliberately set "FirstName" to just one character. Let's remove "LastName" and then send this request to the server. Request completed with http status code 400 bad request, and if we take a look at the response body, notice we have our validation errors - LastName field is required. The field FirstName must be a string with a minimum length of 2. Now, if you don't like these error messages, you can very easily change them using
the "ErrorMessage" property of the respective validation attribute. For example, I am changing the "MinLength" validation error message to "FirstName must contain at least 2 characters". Now, if it's an ASP.NET MVC web application that we are developing, then we explicitly check if "ModelState" has succeeded or failed by using ModelState.IsValid property. In an ASP.NET REST API, there is no need to explicitly check "ModelState.IsValid" property. Now, if we take
a look at EmployeesController, notice, it is decorated with the [ApiController] attribute, so it is this [ApiController] attribute that takes care of checking if the model state is valid. If it is not valid, it automatically returns the http status code 400 along with the validation errors. Now, most of our validation requirements can be implemented using these built-in attributes. However, there are few use cases which we cannot implement using these built-in validation attributes, for example, let's say we do not want to allow a new employee to be created if the provided email is already in use. Let's see how to implement this now. If we take a look at our EmployeeRepository class, notice we already have a method here "GetEmployeeByEmail()", so we provide it the email address, this method will check if there is an employee already with that provided email address. So, within our EmployeesController, in this CreateEmployee()
method, before we create the employee, let's check if we already have an employee with the provided email. So, let's create a variable "emp" equals employeeRepository.GetEmployeeByEmail(), and to this method we need to pass the email, we have that on the employee object. If employee is not equal to null, it means you already have an employee in our system with the provided email address, so to the ModelState object we want to add model error, the key is "email" and the error message is "Employee email already in use", and then we return bad request along with the model state object. In our system, we already have an employee with this provided email address "david@pragimtech.com". Let's try to create another employee with the same email address
and see what's going to happen. There we go, bad request with the http status code 400 along with our validation error message "Employee email already in use". Next, let's discuss how to update an existing resource, that is implement [httpPut] in a REST API. We've already discussed how to retrieve the list of all employees, a specific employee
by id and even how to create a new employee. To update an existing employee we use the http verb PUT and in the URL we pass the "id" of the employee whose details we want to update. UpdateEmployee() is going to be somewhat similar to this "CreateEmployee()" method. So, let's make a copy of this method and then change the bits that are required. First, change the name of the method to UpdateEmployee(). This method needs two parameters, the "id" of the employee whose details we want to update and the "employee" object itself. This object contains our changes and this method returns
Task<ActionResult<Employee>>, basically the updated employee object. We'll see that in action in just a bit and remember, it is the http "PUT" verb that we use to update data. In the URI, we also pass the "id" of the employee whose details we want to update as a route parameter. So, on this [HttpPut] verb, let's also include the "id" route parameter. Employee "id"
is integer, so let's also include the "int" route constraint. Remember the employee id value that is passed in the URI is automatically mapped to this "id" parameter. So, let's check if "id" equals the "EmployeeId" property on the employee object. So, basically we are checking if
this "id" equals the "EmployeeId" property on this employee object. If they are not equal then that means something went wrong and we do not want to continue updating employee data, instead we want to return a bad request with the error message "Employee id mismatch". If the id values match, our next step is to retrieve the respective employee details from the database table, for that on EmployeeRepository we have GetEmployee() method and this method expects employee id whose details we want to retrieve. So, let's pass the incoming employee id value
and then let's also rename this variable, let's call it "employeeToUpdate". GetEmployee() within our employee repository is an asynchronous method, we forgot to use the "await" keyword and I think, even in CreateEmployee() method, we forgot to use "await" keyword on this GetEmployeeByEmail() method, so let's include it here as well. Now, if this variable "employeeToUpdate" is null, it means we cannot find employee with the provided id, so let's return not found. with the message employee with id equals, whatever is the id not found.
On the other hand, if we have found the employee, we can proceed with the update. So, on employee repository we have "UpdateEmployee()" method, to it we pass this incoming employee object which contains our changes, and if you take a look at this UpdateEmployee() method on our employee repository, this method updates the data in the underlying database table and returns that updated employee object back. So, let's use the "return" keyword here. We don't need this last line anymore, so let's delete that. Finally, let's also change the error message here to "Error updating employee record". Run the project and test http POST using "Postman". From postman we want to issue a "PUT" request and here is the URI "api/employee/1". "1"
is the id of the employee whose details we want to update, and we want to send the employee object that contains our changes using the request body, and we're going to use "raw json" format for that, and here is the employee object. Let's change firstName to "John" with letter "h" and lastName to "Hastings" and let's also change email to "john@pragimtech.com". Let's click "Send". There we go, status code 200 OK and in the response body, we also have the updated employee object. Notice, these three properties firstName, lastName and email, we have the updated values. Now, take a look at this UpdateEmployee() method. Within our EmployeesController, it calls UpdateEmployee() method on our employee repository and we have that method right here, and to this method we pass the employee object that contains our changes as a parameter. We
first retrieve the respective employee object from the database table using the employee id and then overwrite each property with the updated values that we have on this incoming parameter "employee" object and for "DepartmentId" we are using the integer "DepartmentId" property, but if we take a look at postmen, there are two ways to send "DepartmentId" value, we can either use the integer property "departmentId" or this complex object "department". Notice, it also has "departmentId" property. At the moment, this method within our employee repository is simply ignoring the "department" object. So, we want to make this method a bit more intelligent, so I'm going to replace this one line of code, with these four lines. Pretty straightforward logic, nothing too complex here. We're first checking the integer "DepartmentId" property value, if it's not the default integer value which is 0, that means we have got a value within the "DepartmentId" property, so let's use that integer property, else check the "Department" property.
If it is not null, then use the "DepartmentId" property on the "Department" object. Next, let's discuss how to implement "Delete" in a REST API. To retrieve the list of all employees, issue a GET request to this URI. To create a new employee, again to the same URI, we issue a POST request. To retrieve, update or delete a specific employee, we issue either GET, PUT or DELETE request to this same URI. Notice, in these last three cases we are passing the "id" of the employee in the URI.
DeleteEmployee() is going to be somewhat similar to this UpdateEmployee() method. So, let's make a copy of this and then change the bits that are required. To delete an employee we just need employee id, we don't need this "employee" object parameter, so let's delete that, and to keep the method name meaningful, let's change the name of the method to "DeleteEmployee". This method is not going to return anything, so let's remove this "Employee" parameter. We can return the deleted employee object back, but to keep the implementation simple, this method is not going to return anything and it is the [HttpDelete] verb that we use to delete a resource. Inside the "try" block, we don't need this "if" check.
To delete an employee, we first retrieve the respective employee from the database using this incoming "id". To keep it meaningful, let's change the name of this variable to "employeeToDelete". If this variable is not null, it means we did not find the respective employee in the database with this provided "id", so we return http status code 404 with the message, employee with id equals whatever is the id not found. On the other hand, if we have found the employee, we want to delete that respective employee. So, on our employee repository for that we have "DeleteEmployee()" method, and to this method, we pass the incoming "id" parameter.
Upon successfully deleting the employee, let's return the http status code 200 okay, for that we use the built-in method Ok(), and we can also return a custom message if you want. Let's actually copy and paste this message, employee with id equals whatever is the id deleted. Finally, to keep it meaningful, let's not forget to change the exception message here to "Error deleting employee record". At the moment, within our system, we have five employees. Let's delete this last employee with employee id 5. So, in the URI, we include the id of the employee and the http verb that we use to delete is DELETE, and then let's send this to the server. There we go, we get status code 200
OK along with the message "Employee with id = 5 deleted". Now, let's try to delete this employee again and see what's going to happen. We get 404 not found with our custom error message "Employee with id = 5 not found", and if we try to issue a GET request to retrieve this specific employee, again we have 404 not found. At this point we have all the CRUD operations implemented, that is create, read, update and delete. Next, let's discuss how to implement "Search" in a REST API. We want to be able to search by both - employee name and gender.
As usual, in our employees controller, let's include another method for search. This method is also going to be "public async", and it's going to return Task<ActionResult<IEnumberable<Employee>>> objects. If you're wondering, why is the return type "IEnumerable<Employee> objects? Well, that's because, we want this method to return the list of all employees that match our search criteria. Let's name this method "Search". Remember, we want to be able to search both by "name" and "gender". "Name" is of type string and "Gender" is of type enum. Now, here is the important point to keep in mind, we want
2021-06-29 15:25