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 to make both the search parameters, that is "name" and "gender" optional. If values for
these search parameters are not provided, we want the search to return all the employees. If a value
for "name" is provided, search just by "name". Along the same lines, if a value for "gender"
is provided, search just by "gender". If both are provided, search by both. Name parameter is
already optional because, "name" is of type string which is a reference type, and for a reference
type we can pass null. "Gender" parameter on the other hand is an enum, which is a value type and
we cannot pass null. To be able to pass null and make it optional, let's make this "gender" enum
a nullable enum by including a question mark. At the moment, this GetEmployees() method
is decorated with [HttpGet] attribute, this means when we navigate to this URI "api/the
name of the controller", in our case "employees", this GetEmployees() method is called and
it returns the list of all employees. Now, take a look at this GetEmployee() method, even
this method is decorated with [HttpGet] attribute, but this attribute has an extension to the
route, so we have the id route parameter here, so with this in place, when we navigate to this
uri "/api/employees/the id of the employee", then this method is called and this method returns
that specific employee. Same idea with "search", we want to include an extension to the route
that we already have at the controller level, so I'm going to decorate this with [HttpGet]
attribute and include an extension to the route that we already have at the controller level.
So with this [HttpGet] attribute in place, when we navigate to this URI "/api/employees/search",
this "Search" method is called. Next let's provide the implementation. First, a "try catch" block. If
there is an exception performing the search, let's return the http status code 500 along with the
message "Error retrieving data from the database". On this injected employee repository, we already
have a "Search" method, so let's call that and to it let's pass both these incoming
search parameters name and gender. Now, if we take a look at the "Search" method in
our employee repository, its implementation is pretty straightforward. Notice, we are building
our "search query" dynamically. We are starting with the full list of employees, and then, if a
value for "name" is provided, we are adding the "where" clause dynamically, basically filtering
by "name". We are looking in both the columns, "FirstName" and "LastName", and at the moment,
they are using "contains" but we can also use "Startswith" or "Endswith" depending
on our search requirement. Similarly, if a value for "gender" is provided,
then we are filtering by gender. Finally, executing the query and returning the search
results. At the moment, as you can see, the search method is an async method, so let's
"await" its execution and then store the result in a variable called "result". Let me paste the
rest of the code and I'll walk you through it. If we have at least one employee in the search
results, we return the search results along with the http status code 200 OK, otherwise 404 not
found. Next, let's test our search using postman. To this URI "api/employees", let's issue a GET
request. There we go, in the database we have four employees and we see all those four
employees. Now, let's navigate to this URI "api/employees/search". This will invoke
our "Search" method and keep in mind, at the moment you're not providing any
values for the search terms, so the list of all employees must be returned. So, let's
execute this. There we go, we have all the four employees as expected. Now, let's include a value
for the "gender" search parameter. We do that using a query string parameter and keep in mind,
"gender" is an enum, so if we look at the enum values, for "male" the underlying integer value is
0, for "female" it's 1, for "other" it is 2. So, this means we can either pass the integer value
like this, for "male" 0. Let's execute this. We get both the "male" employees. We
can also pass the text value "male", again we have the same result. Now,
let's also include a value for "name". We get the one employee whose "name
is john" and "gender is male", and if we change the gender value to "female",
we don't have any such employee, so we get 404. In this project "BlazorProject.Server", in the
"Controllers" folder, in the interest of time, I've already added "DepartmentsController". The
pattern for implementing DepartmentsController is exactly the same as EmployeesController.
Let me open this file and I'll quickly walk you through the code. Take a look at
our "DepartmentsController" class. It's decorated with both these attributes [Route]
and [ApiController]. It also inherits from the built-in "ControllerBase" class because what
we are building here is an API controller, and then we're using the class constructor to
inject department repository which obviously provides database data. Take a look
at this "GetDepartments()" method. It is decorated with [HttpGet] attribute.
So, this means, when we navigate to this uri "api/the name of the controller", in this case
"departments", this method is invoked and it returns the list of all departments. Now, take
a look at this "GetDepartment()" method. This method expects the "id" of the department as
a parameter, and take a look at this [HttpGet] attribute. Using this attribute we are including
an extension to the route that we already have at the controller level. So, in the URI, if we
have "api/departments/the id of the department" then this method is invoked and it returns that
specific department. So, pretty straightforward controller class here. What we have built together
in this course is a simple REST API. I hope you've enjoyed this course as much as I did creating
it. Thank you for listening. Goodbye for now.