.NET 5 REST API Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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.
Info
Channel: kudvenkat
Views: 78,998
Rating: 4.9695587 out of 5
Keywords: how to build web api in c#, asp.net core 5 rest api tutorial, build web api in .net core, asp.net 5 rest api tutorial, web api asp.net mvc 5, build web api c#, asp.net 5 web api tutorial, asp.net core 5 web api, how to build web api in asp.net core, asp.net core 5 web api tutorial, how to build web api in .net 5, .net 5 web api tutorial, asp.net mvc 5 web api tutorial, rest api with .net 5, build web api with .net 5, c# .net 5 web api tutorial, .net core 5 web api tutorial
Id: Jvu60R-AWQc
Channel Id: undefined
Length: 73min 25sec (4405 seconds)
Published: Fri Jun 25 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.