Click here to Skip to main content
15,867,453 members
Articles / Web Development / ASP.NET / ASP.NET Core

Securing ASP.NET CORE Web API using Custom API Key based Authentication

Rate me:
Please Sign up or sign in to vote.
4.79/5 (43 votes)
6 Feb 2018CPOL21 min read 102.9K   5.5K   55   27
In this article, we are going to learn how to create a secure Web API in ASP.NET Core MVC.

Image 1

Credit: Icons made by Smashicons from www.flaticon.com is licensed by CC 3.0 BY

In the fast-growing era of Web technology, everything is changing too fast. With ASP.NET, there was a time when we used to use web service (the .asmx ones), which was SOAP-based, which we can just use for consuming data from other applications, which did have that much of security in it. Most developers would take Username and Password Parameter as input and then they would allow to access web service.

As time passes, Microsoft came up with WCF which was secured but too complex to use.

Further, Microsoft came up with something new called as Web API which we can use by creating ASP.NET MVC application or directly ASP.NET Web API application which was lighter and easy to use.

But moving further, Microsoft introduces ASP.NET Core which is lighter than all its previous versions.

But when we say we are securing a webapi in ASP.NET WEB API, we use Delegate handler for validating API request.

As we jump into ASP.NET Core, there are no more handler and modules, we are introduced to something new called as Middleware, which we are going to write to validating API request.

In this article, we are going to learn that extra part, the process to create an ASP.NET Core WEB API application in which a developer can log into an application and subscribe his own services, then generate API keys, see his own documentation of API how to consume API and finally he will get his own analytics on how many requests he sends in a month. And if request sent count is greater than the user has subscribed, then he will get the response "exceeds request length".

Process

  • Register Developer
  • Login
  • Choose Service with Max request (1000 request, 5000 requests)
  • Get API key
  • API Documents
  • Use API key to Access service

Prerequisites

  • Visual Studio 2017 with ASP.NET CORE 2.0
  • SQL Server 2008 and above

Database parts

In this part, we have created the database with name "MoviesDB" and it has seven tables which we are going to use in the application.

Image 2

Image 3

Image 4

Tables Details

  1. APIManagerTB: Stores all services and API Keys
  2. MoviesTB: Stores all movies details
  3. MusicTB: Stores all music details
  4. RegisterUser: Stores all Registered User details
  5. ServicesTB: Stores all Service details
  6. LoggerTB: Stores all request log of API
  7. HitsTB: Stores all Request values (1000 request, 2000 request)

Creating WEB API Application

Open New Visual Studio 2017 IDE.

Image 5

After opening IDE, next, we are going to create ASP.NET MVC Core project. For doing that, just click File - New - Project.

Image 6

After choosing a project, a new dialog will pop up with the name "New Project". In that, we are going to choose Visual C# Project Templates - .NET Core - ASP.NET Core Web Application. Then, we are going to name the project as "MoviesAPIStore".

Image 7

After naming the project, click on OK button to create the project. A new dialog will pop up for choosing templates for creating "Web Application (Model View Controller)", click on the OK button to create the project.

Image 8

After configuring the project next, we are going to see project structure.

Image 9

Using Entity Framework Core Code First Approach to Connecting Application to Database

The first step we are going to add a connection string in appsettings.json file.

Image 10

The second step we are going to create Models according to MoviesDB database.

Image 11

The third step we are going to add a MoviesContext folder and inside that folder, we are going to add DatabaseContext class which will inherit Dbcontext class.

Image 12

Code Snippet of DatabaseContext Class

C#
using Microsoft.EntityFrameworkCore;
using MoviesAPIStore.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MoviesAPIStore.MoviesContext
{
    public class DatabaseContext : DbContext
    {
        public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options)
        {
          
        }
        public DbSet<RegisterUser> RegisterUser { get; set; }
        public DbSet<ServicesTB> ServicesTB { get; set; }
        public DbSet<HitsTB> HitsTB { get; set; }
        public DbSet<APIManagerTB> APIManagerTB { get; set; }
        public DbSet<MoviesTB> MoviesTB { get; set; }
        public DbSet<MusicTB> MusicTB { get; set; }
        public DbSet<LoggerTB> LoggerTB { get; set; }
    }
}

Adding Repository Folder to Application

In this part, we are going to add Repository Folder to project. In this folder, we are going to keep all project interfaces and a concrete class.

Image 13

Register User

Image 14

Credit: Man free Icons made by monkik from www.flaticon.com is licensed by CC 3.0 BY
  1. RegisterUser Model
  2. Adding IRegisterUser Interface
  3. Adding RegisterUserConcrete class
  4. Adding Controller
  5. Adding View
  6. Setting Dependency injection in startup class for injecting dependence of RegisterUserConcrete class

Let’s start with RegisterUser Model.

1. RegisterUser Model

C#
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;

namespace MoviesAPIStore.Models
{
    [Table("RegisterUser")]
    public class RegisterUser
    {
        [Key]
        public long UserID { get; set; }

        [Required(ErrorMessage = "Required Username")]
        [StringLength(30, MinimumLength = 2, 
         ErrorMessage = "Username Must be Minimum 2 Charaters")]
        public string Username { get; set; }

        [DataType(DataType.Password)]
        [Required(ErrorMessage = "Required Password")]
        [MaxLength(30, ErrorMessage = "Password cannot be Greater than 30 Charaters")]
        [StringLength(31, MinimumLength = 7, 
         ErrorMessage = "Password Must be Minimum 7 Charaters")]
        public string Password { get; set; }
        public DateTime CreateDate { get; set; }

        [Required(ErrorMessage = "Required EmailID")]
        [RegularExpression(@"[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}", 
         ErrorMessage = "Please enter Valid Email ID")]
        public string EmailID { get; set; }
    }
}

After completing with adding RegisterUser model, next we are going to add IRegisterUser interface and in this interface, we are going to declare all methods which we require for Registering users.

2. IRegisterUser interface

In this part, we are going to add IRegisterUser interface to repository folder and this interface contains four methods in it:

  • Add: for creating a new User which takes RegisterUser Model as input
  • ValidateRegisteredUser: for validating username and password entered by the user
  • ValidateUsername: for validating Username entered by user (does similar Username already exist in database)
  • GetLoggedUserID: Get logged in UserID by username and password entered by the user

Code Snippet

C#
using MoviesAPIStore.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MoviesAPIStore.Repository
{
    public interface IRegisterUser
    {
        void Add(RegisterUser registeruser);
        bool ValidateRegisteredUser(RegisterUser registeruser);
        bool ValidateUsername(RegisterUser registeruser);
        long GetLoggedUserID(RegisterUser registeruser);
    }
}

3. RegisterUserConcrete Class

The RegisterUserConcrete class will inherit IRegisterUser interface and implements all methods in it.

Code Snippet of RegisterUserConcrete Class

C#
using MoviesAPIStore.Models;
using MoviesAPIStore.MoviesContext;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MoviesAPIStore.Repository
{
    public class RegisterUserConcrete : IRegisterUser
    {
        private DatabaseContext _context;

        public RegisterUserConcrete(DatabaseContext context)
        {
            _context = context;
        }

        public void Add(RegisterUser registeruser)
        {
            try
            {
                registeruser.UserID = 0;
                _context.RegisterUser.Add(registeruser);
                _context.SaveChanges();
            }
            catch (Exception ex)
            {
                throw;
            }
        }

        public long GetLoggedUserID(RegisterUser registeruser)
        {
            var usercount = (from User in _context.RegisterUser
                             where User.Username == registeruser.Username && 
                                   User.Password == registeruser.Password
                             select User.UserID).FirstOrDefault();

            return usercount;
        }

        public bool ValidateRegisteredUser(RegisterUser registeruser)
        {
            var usercount = (from User in _context.RegisterUser
                             where User.Username == registeruser.Username && 
                                   User.Password == registeruser.Password
                             select User).Count();
            if (usercount > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public bool ValidateUsername(RegisterUser registeruser)
        {
            var usercount = (from User in _context.RegisterUser
                             where User.Username == registeruser.Username
                             select User).Count();
            if (usercount > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}

After adding RegisterUserConcrete class, next we are going to have a look at dependency injection.

Understanding Dependency Injection

In this process, we are going to use constructor injection for injecting dependency.

We are going to inject DbContext as a dependency to RegisterUserConcrete class to access specific entity (RegisterUser).

Snapshot while Injecting Dbcontext

Image 15

After having a look at DatabaseContext injection, you might wonder how I am injecting dependency, right? The ASP.NET Core MVC comes with inbuilt dependency injection framework, you just need to configure it, it is located in startup.cs under ConfigureServices method.

Configuring Dependency

Image 16

In the above snapshot, you can see that we have configured DatabaseContext dependency whenever you use DatabaseContext class, DbContext instance will be injected there.

And another thing you can see is that we have added services.Configure method. This is used for getting connection string as dependency and we are getting connection string as "Configuration.GetSection("ConnectionStrings")" - we are going to use this connection string with dapper ORM.

The last dependency which we have added is AddTransient. In this, we need to configure an interface and concrete class.

Code Snippet

C#
services.AddTransient<Interface, ConcreteClass>();

Whenever we are going to use interface, at that time the concrete class instance will be injected thereby dependency injection framework.

Now we have configured dependency of IRegisterUser interface and RegisterUserConcrete Concrete class.

As we move ahead, we are going to register more dependency in ConfigureServices method in the same way as we need for IRegisterUser interface and RegisterUserConcrete Concrete class.

What is Concrete class?

The class which inherits interface is called Concrete class.

Reference: https://stackoverflow.com/questions/38138100/what-is-the-difference-between-services-addtransient-service-addscope-and-servi

In .NET's dependency injection, there are three major lifetimes:

  1. Singleton which creates a single instance throughout the application. It creates the instance for the first time and reuses the same object in the all calls.
  2. Scoped lifetime services are created once per request within the scope. It is equivalent to Singleton in the current scope. For example, in MVC, it creates one instance per each HTTP request but uses the same instance in the other calls within the same web request.
  3. Transient lifetime services are created each time they are requested. This lifetime works best for lightweight, stateless services.

After completing with understand dependency injection configuration, let’s add RegisterUser Controller.

4. Adding RegisterUser Controller

For adding controller, just right click on controller folder, then choose -> Add -> inside that, choose Add New item. A new dialog will popup of Add New item. Inside that, choose "MVC Controller Class" and name your controller as "RegisterUserController" and click on Add button to create a RegisterUser controller.

After creating controller next, we are going to use constructor injection for inject dependency.

For that, we are going to add a constructor which will take IRegister interface as input and at runtime, dependency injection will inject dependency of IRegister interface which is RegisterUserConcrete.

Image 17

Code Snippet of RegisterUserController

C#
using System;
using Microsoft.AspNetCore.Mvc;
using MoviesAPIStore.Repository;
using MoviesAPIStore.Models;
using MoviesAPIStore.AES256Encryption;

// For more information on enabling MVC for empty projects, 
// visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace MoviesAPIStore.Controllers
{
    public class RegisterUserController : Controller
    {
        IRegisterUser _repository;
        public RegisterUserController(IRegisterUser repository)
        {
            _repository = repository;
        }

        [HttpGet]
        // GET: RegisterUser/Create
        public ActionResult Create()
        {
            return View(new RegisterUser());
        }

        // POST: RegisterUser/Create
        [HttpPost]
        public ActionResult Create(RegisterUser RegisterUser)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    return View("Create", RegisterUser);
                }

                // Validating Username 
                if (_repository.ValidateUsername(RegisterUser))
                {
                    ModelState.AddModelError("", "User is Already Registered");
                    return View("Create", RegisterUser);
                }
                RegisterUser.CreateDate = DateTime.Now;

                // Encrypting Password with AES 256 Algorithm
                RegisterUser.Password = EncryptionLibrary.EncryptText(RegisterUser.Password);

                // Saving User Details in Database
                _repository.Add(RegisterUser);
                TempData["UserMessage"] = "User Registered Successfully";
                ModelState.Clear();
                return View("Create", new RegisterUser());
            }
            catch
            {
                return View();
            }
        }
    }
}

In RegisterUser Controller, we have added two Action Methods of creating one for handling [HttpGet] request and other for handling [HttpPost] request. In [HttpPost] request, we are going to first Validate if Username already exists or not, if not, then we are going to Create User, next we are also taking Password as input which we cannot store in database as clear text. We need to store it in encrypted format, and for doing that, we are going to Use AES 256 algorithm.

5. RegisterUser View

Image 18

Note: After Registering a User, below are the details which get stored in RegisterUser Table.

6. RegisterUser Table

Image 19

After completing the Registration part, next, we are going to create a Login page.

Login

In this part, we are going to create a login controller with two action methods. One to handle get request and other for handling post request and the same way we are going to add a Login view.

Image 20

In controller part of LoginController, we are going to use dependency injection to inject dependency of RegisterUserConcrete such that we can access method inside of it.

We are going to use "ValidateRegisteredUser" method to validate User credentials. If the user is valid, then we are going to push request to the dashboard page, else we are going to show error on the Login page.

Image 21

Code Snippet of LoginController

C#
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using MoviesAPIStore.AES256Encryption;
using MoviesAPIStore.Models;
using MoviesAPIStore.Repository;
using System;

namespace MoviesAPIStore.Controllers
{
    public class LoginController : Controller
    {
        IRegisterUser _IRegisterUser;
        public LoginController(IRegisterUser IRegisterUser)
        {
            _IRegisterUser = IRegisterUser;
        }

        public ActionResult Login()
        {
            return View(new RegisterUser());
        }

        [HttpPost]
        public ActionResult Login(RegisterUser RegisterUser)
        {
            try
            {
                if (string.IsNullOrEmpty(RegisterUser.Username) && 
                   (string.IsNullOrEmpty(RegisterUser.Password)))
                {
                    ModelState.AddModelError("", "Enter Username and Password");
                }
                else if (string.IsNullOrEmpty(RegisterUser.Username))
                {
                    ModelState.AddModelError("", "Enter Username");
                }
                else if (string.IsNullOrEmpty(RegisterUser.Password))
                {
                    ModelState.AddModelError("", "Enter Password");
                }
                else
                {
                   RegisterUser.Password = 
                   EncryptionLibrary.EncryptText(RegisterUser.Password);

                    if (_IRegisterUser.ValidateRegisteredUser(RegisterUser))
                    {
                        var UserID = _IRegisterUser.GetLoggedUserID(RegisterUser);
                        HttpContext.Session.SetString("UserID", Convert.ToString(UserID));

                        return RedirectToAction("Dashboard", "Dashboard");
                    }
                    else
                    {
                        ModelState.AddModelError("", "User is Already Registered");
                        return View("Login", RegisterUser);
                    }
                }

                return View("Login", RegisterUser);
            }
            catch(Exception)
            {
                return View();
            }
        }

        public ActionResult Logout()
        {
            HttpContext.Session.Clear();
            return RedirectToAction("Login", "Login");
        }
    }
}

After login, we are going to see a simple dashboard pages with charts.

Dashboard Page

This is a simple dashboard page which I am going to explain in detail in the upcoming steps.

Image 22

After having a look at the Dashboard page, you can see that we have Generate API KEY Menu. The next thing we are going to do is Create a generate API Key Page where users can generate Key for API.

Generating API Keys

Image 23

Credit: Icons made by Round icons from www.flaticon.com is licensed by CC 3.0 BY.

Adding ApplicationKeys Controller

In ApplicationKeysController, we are going to add two action methods with name GenerateKeys, one for handling get and other for handling post request.

And on GenerateKeys page, we are going to add two dropdown lists, one for displaying service and the other for displaying Max request count.

After the user chooses these dropdowns, then the user is going to click on Create API Key button to generate API Key to access this APIs.

Let’s have a look at GenerateKeysVM Model.

Code snippet of GenerateKeysVM Model

C#
using MoviesAPIStore.Filters;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;

namespace MoviesAPIStore.Models
{
    public class GenerateKeysVM
    {
        [Required(ErrorMessage ="Choose Service")]
        [ServiceValidate]
        public int? ServiceID { get; set; }
        [HitValidate]
        [Required(ErrorMessage = "Choose Max Request")]
        public int? HitsID { get; set; }

        public List<HitsTB> ListHits { get; set; }
        public List<ServicesTB> ListServices { get; set; }

        public string APIKey { get; set; }
    }
}

After adding GenerateKeysVM model, next we are going to add ServicesStore interface and Concrete class.

IServicesStore Interface

In IServicesStore interface, we have added two methods GetServiceList and GetServiceListforDashboard.

Code Snippet of IServicesStore Interface

C#
using MoviesAPIStore.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MoviesAPIStore.Repository
{
    public interface IServicesStore
    {
        List<ServicesTB> GetServiceList();
        List<ServicesTB> GetServiceListforDashboard();
    }
}

After adding IServicesStore interface, next, we are going to add a ServicesStore Concrete class.

Code Snippet of ServicesStoreConcrete Class

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MoviesAPIStore.Models;
using MoviesAPIStore.MoviesContext;

namespace MoviesAPIStore.Repository
{
    public class ServicesStoreConcrete : IServicesStore
    {
        private DatabaseContext _context;

        public ServicesStoreConcrete(DatabaseContext context)
        {
            _context = context;
        }

        public List<ServicesTB> GetServiceList()
        {
            try
            {
                var ServiceList = (from services in _context.ServicesTB
                                   select services).ToList();

                ServiceList.Insert(0, new ServicesTB 
                { ServiceName = "---Choose Service---", ServiceID = -1 });
                return ServiceList;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public List<ServicesTB> GetServiceListforDashboard()
        {
            try
            {
                var ServiceList = (from services in _context.ServicesTB
                                   select services).ToList();

                return ServiceList;
            }
            catch (Exception)
            {

                throw;
            }
        }
    }

}

After completing with adding IServicesStore interface and ServicesStoreConcrete class, next we are going to add Ihits interface.

Adding Ihits Interface

Ihits interface contains methods used for displaying HitsList (Max Request Dropdown) and used for displaying charts on the dashboard.

Code Snippet of Ihits Interface

C#
using MoviesAPIStore.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MoviesAPIStore.Repository
{
    public interface IHits
    {
        List<HitsTB> GetHitsList();
        List<string> GetChartsMoviesreport();
        List<string> GetChartsMusicreport();
    }
}

Adding HitsConcrete Class

In this part, we have implemented all methods which are declared IHits interface. In this concrete class for some methods, we have used Dapper ORM for getting data from the database. And you can see constructor of this class we are taking two inputs, one of DatabaseContext and IOptions<ConnectionStrings>, the DatabaseContext will get an instance of DbContext and IOptions<ConnectionStrings> will get an instance of configuration (Connection string).

Code Snippet of HitsConcrete Class

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MoviesAPIStore.Models;
using MoviesAPIStore.MoviesContext;
using Dapper;
using System.Data.SqlClient;
using Microsoft.Extensions.Options;

namespace MoviesAPIStore.Repository
{
    public class HitsConcrete : IHits
    {
        private DatabaseContext _context;
        private ConnectionStrings _connectionstrings;

        public HitsConcrete(DatabaseContext context, 
                            IOptions<ConnectionStrings> connectionstrings)
        {
            _context = context;
            _connectionstrings = connectionstrings.Value;
        }

        public List<HitsTB> GetHitsList()
        {
            try
            {
                var hitsList = (from services in _context.HitsTB select services).ToList();
                hitsList.Insert(0, new HitsTB 
                { HitsDisplay = "---Choose Max Request---", HitsID = -1 });
                return hitsList;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public List<string> GetChartsMoviesreport()
        {
            try
            {
                using (SqlConnection con = new SqlConnection
                (Convert.ToString(_connectionstrings.DatabaseConnection)))
                {
                    var parameter = new DynamicParameters();
                    return con.Query<string>("Usp_GetChartsMoviesreport", 
                    null, null, false,0, System.Data.CommandType.StoredProcedure).ToList();
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        public List<string> GetChartsMusicreport()
        {
            try
            {
                using (SqlConnection con = new SqlConnection
                      (Convert.ToString(_connectionstrings.DatabaseConnection)))
                {
                    var parameter = new DynamicParameters();
                    return con.Query<string>("Usp_GetChartsMusicreport", 
                    null, null, false, 0, System.Data.CommandType.StoredProcedure).ToList();
                }
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}

After adding IHits interface and HitsConcrete class, next we are going to add IAPIManager interface.

Adding IAPIManager Interface

In this interface, we are going to add a method which is required for generating, saving and validating API Key. Along with that, we have added two methods, Deactivate and Reactivate Service which can be used by the user to activate and deactivate his own services.

Code Snippet of IAPIManager Interface

C#
using MoviesAPIStore.Models;

namespace MoviesAPIStore.Repository
{
    public interface IAPIManager
    {
        int isApikeyAlreadyGenerated(int? ServiceID, int? UserID);

        int GenerateandSaveToken(APIManagerTB APIManagerTB);

        APIManagerVM GetApiDetailsbyServiceIDandUserID(int? ServiceID, int? UserID);

        int DeactivateService(int? ServiceID, int? UserID);

        int ReactivateService(int? ServiceID, int? UserID);
    }
}

After adding IAPIManager interface next, we are going to add APIManagerConcrete class which will implement IAPIManager.

Code Snippet of APIManagerConcrete

C#
using Dapper;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using MoviesAPIStore.Models;
using MoviesAPIStore.MoviesContext;
using System;
using System.Data.SqlClient;
using System.Linq;

namespace MoviesAPIStore.Repository
{
    public class APIManagerConcrete : IAPIManager
    {
        private DatabaseContext _context;
        private ConnectionStrings _connectionstrings;
        public APIManagerConcrete(DatabaseContext context, 
               IOptions<ConnectionStrings> connectionstrings)
        {
            _context = context;
            _connectionstrings = connectionstrings.Value;
        }

        public int isApikeyAlreadyGenerated(int? ServiceID, int? UserID)
        {
            try
            {
                var keyCount = (from apimanager in _context.APIManagerTB
                                where apimanager.ServiceID == ServiceID &&
                                      apimanager.UserID == UserID
                                select apimanager).Count();

                return keyCount;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public int GenerateandSaveToken(APIManagerTB APIManagerTB)
        {
            try
            {
                _context.APIManagerTB.Add(APIManagerTB);
                return _context.SaveChanges();
            }
            catch (Exception)
            {
                throw;
            }
        }

        public APIManagerVM GetApiDetailsbyServiceIDandUserID(int? ServiceID, int? UserID)
        {
            try
            {
                using (SqlConnection con = new SqlConnection
                      (Convert.ToString(_connectionstrings.DatabaseConnection)))
                {
                    var parameter = new DynamicParameters();
                    parameter.Add("@ServiceID", ServiceID);
                    parameter.Add("@UserID", UserID);
                    var apimanagervm = con.Query<APIManagerVM>
                    ("Usp_GetApiDetailsbyServiceIDandUserID", parameter, null, 
                    false, 0, System.Data.CommandType.StoredProcedure).SingleOrDefault();

                    if(apimanagervm ==null)
                    {
                        return new APIManagerVM();
                    }
                    else
                    {
                        return apimanagervm;
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        public int DeactivateService(int? ServiceID, int? UserID)
        {
            try
            {
                using (SqlConnection con = new SqlConnection
                      (Convert.ToString(_connectionstrings.DatabaseConnection)))
                {
                    var parameter = new DynamicParameters();
                    parameter.Add("@ServiceID", ServiceID);
                    parameter.Add("@UserID", UserID);
                    return con.Execute("Usp_DeactivateService_update", 
                           parameter, null, 0, System.Data.CommandType.StoredProcedure);
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        public int ReactivateService(int? ServiceID, int? UserID)
        {
            try
            {
                using (SqlConnection con = new SqlConnection
                      (Convert.ToString(_connectionstrings.DatabaseConnection)))
                {
                    var parameter = new DynamicParameters();
                    parameter.Add("@ServiceID", ServiceID);
                    parameter.Add("@UserID", UserID);
                    return con.Execute("Usp_ReactivateService_update", 
                           parameter, null, 0, System.Data.CommandType.StoredProcedure);
                }
            }
            catch (Exception)
            {
                throw;
            }
        }       
    }
}

Snapshot of Repository

Image 24

After completion of adding all interface and concrete classes which are required for generating, validating and saving API key, next we are going to add ApplicationKeys Controller.

Adding ApplicationKeysController

In this controller, we are going to add two action methods with the name "GenerateKeys", one for handling [HttpGet] and other for handling [HttpPost] request.

In [HttpGet] GenerateKeys method, we are going to assign values of ListServices and ListHits to GenerateKeysVM View model and send that model to view the binding dropdown list.

In [HttpPost] GenerateKeys method, we are going to get values for Service and Hits which the user has chosen. Next, we are going to validate Service and Hits values against the database and check if this service already subscribes, if yes, then we are going to show the alert message "API Key for Chosen Service is Already Generated". If service is not already subscribed, then we are going to generate unique APIKey for that service.

Code Snippet of ApplicationKeysController

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MoviesAPIStore.Repository;
using MoviesAPIStore.Models;
using MoviesAPIStore.AES256Encryption;
using Microsoft.AspNetCore.Http;
using MoviesAPIStore.Filters;
using Newtonsoft.Json;

// For more information on enabling MVC for empty projects, 
// visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace MoviesAPIStore.Controllers
{
    [ValidateUserSession]
    public class ApplicationKeysController : Controller
    {
        IServicesStore _IServicesStore;
        IHits _IHits;
        IAPIManager _IAPIManager;
        public ApplicationKeysController
        (IServicesStore IServicesStore, IHits IHits, IAPIManager IAPIManager)
        {
            _IServicesStore = IServicesStore;
            _IHits = IHits;
            _IAPIManager = IAPIManager;
        }

        [HttpGet]
        // GET: /<controller>/
        public IActionResult GenerateKeys()
        {
            try
            {
                GenerateKeysVM generateKeysVM = new GenerateKeysVM();
                generateKeysVM.ListServices = _IServicesStore.GetServiceList();
                generateKeysVM.ListHits = _IHits.GetHitsList();
                return View(generateKeysVM);
            }
            catch (Exception)
            {
                throw;
            }
        }

        [HttpPost]
        public IActionResult GenerateKeys(GenerateKeysVM generateKeysVM)
        {

            if (ModelState.IsValid)
            {
                var userID = Convert.ToInt32(HttpContext.Session.GetString("UserID"));

                if (_IAPIManager.isApikeyAlreadyGenerated
                   (generateKeysVM.ServiceID, userID) > 0)
                {
                    ModelState.AddModelError("", 
                    "Api Key for Choosen Service is Already Generated");
                    generateKeysVM.ListServices = _IServicesStore.GetServiceList();
                    generateKeysVM.ListHits = _IHits.GetHitsList();
                    return View(generateKeysVM);
                }

                generateKeysVM.ListServices = _IServicesStore.GetServiceList();
                generateKeysVM.ListHits = _IHits.GetHitsList();

                if (GenerateKey(generateKeysVM) == 1)
                {
                    TempData["APIKeyGeneratedMessage"] = "Done";
                }
                else
                {
                    TempData["APIKeyGeneratedMessage"] = "Failed";
                }

                return View(generateKeysVM);
            }

            generateKeysVM.ListServices = _IServicesStore.GetServiceList();
            generateKeysVM.ListHits = _IHits.GetHitsList();

            return View(generateKeysVM);
        }

        [NonAction]
        public int GenerateKey(GenerateKeysVM GenerateKeysVM)
        {
            try
            {
                APIManagerTB aPIManagerTB = new APIManagerTB()
                {
                    APIKey = EncryptionLibrary.KeyGenerator.GetUniqueKey(),
                    HitsID = GenerateKeysVM.HitsID,
                    CreatedOn = DateTime.Now,
                    ServiceID = GenerateKeysVM.ServiceID,
                    UserID = Convert.ToInt32(HttpContext.Session.GetString("UserID")),
                    Status = "A"
                };

                return _IAPIManager.GenerateandSaveToken(aPIManagerTB);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public IActionResult DeactivateService(string ServiceID)
        {
            try
            {
                var result = _IAPIManager.DeactivateService
                             (Convert.ToInt32(ServiceID), 
                             Convert.ToInt32(HttpContext.Session.GetString("UserID")));
                return Json(data: result);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public IActionResult ReActivateService(string ServiceID)
        {
            try
            {
                var result = _IAPIManager.ReactivateService
                             (Convert.ToInt32(ServiceID), 
                              Convert.ToInt32(HttpContext.Session.GetString("UserID")));
                return Json(data: result);
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}

After understanding work of ApplicationKeysController, next we see how we are generating unique APIKey and inserting that key into the database.

Code Snippet of GenerateKey

C#
[NonAction]
public int GenerateKey(GenerateKeysVM GenerateKeysVM)
{
    try
    {
        APIManagerTB aPIManagerTB = new APIManagerTB()
        {
            APIKey = EncryptionLibrary.KeyGenerator.GetUniqueKey(),
            HitsID = GenerateKeysVM.HitsID,
            CreatedOn = DateTime.Now,
            ServiceID = GenerateKeysVM.ServiceID,
            UserID = Convert.ToInt32(HttpContext.Session.GetString("UserID")),
            Status = "A"
        };

        return _IAPIManager.GenerateandSaveToken(aPIManagerTB);
    }
    catch (Exception)
    {
        throw;
    }
}

Next, we are going to add GenerateKeys View and add two dropdown lists and a button on it.

Snapshot for GenerateKey

Image 25

Next for generating a key, we are going to choose Service and Max Request and click on "Create API Key" button to generate the key.

APIManager Table View after Generating API Key

Image 26

After generating Key next, we are going to Add a ServicesStore Controller which will display all services on it. After clicking on that service, you can see your API Key for that service.

Adding ServicesStore Controller

Image 27

Credit: Man free Icons made by monkik from www.flaticon.com is licensed by CC 3.0 BY

In this controller, we have two action methods. Both are [HttpGet] methods, one for displaying service (Service) on view and another for showing details (Details) of that service.

Code Snippet of ServicesStore Controller

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MoviesAPIStore.Repository;
using MoviesAPIStore.Filters;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using MoviesAPIStore.Models;

// For more information on enabling MVC for empty projects, 
// visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace MoviesAPIStore.Controllers
{
    [ValidateUserSession]
    public class ServicesStoreController : Controller
    {
        IServicesStore _IServicesStore;
        IAPIManager _iAPIManager;

        public ServicesStoreController
        (IServicesStore IServicesStore, IAPIManager iAPIManager)
        {
            _IServicesStore = IServicesStore;
            _iAPIManager = iAPIManager;
        }

        // GET: /<controller>/
        public IActionResult Service()
        {
            return View(_IServicesStore.GetServiceListforDashboard());
        }

        public IActionResult Details(string ServiceID, string ServiceName)
        {
            try
            {
                var userID = Convert.ToInt32(HttpContext.Session.GetString("UserID"));
                var apiDetails = _iAPIManager.GetApiDetailsbyServiceIDandUserID
                                 (Convert.ToInt32(ServiceID), userID);
                return View(apiDetails);
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}

View of Service.cshtml

HTML
@model List<MoviesAPIStore.Models.ServicesTB>
@{
    Layout = "~/Views/Shared/_LayoutDashboard.cshtml";
}
<h2>Services</h2>
<div class="row">

    @foreach (var service in Model)
    {
        <div class="col-sm-4">
            <div class="card">
                <div class="content">
                    <div class="row">
                        <div class="col-xs-5">
                            <div class="icon-big icon-info text-center">
                                <i class="ti-settings-alt"></i>
                            </div>
                        </div>
                        <div class="col-xs-7">
                            <div class="numbers">
                                @service.ServiceName API
                            </div>
                        </div>
                    </div>
                    <div class="footer">
                        <hr />
                        <div class="stats">
                            <i class="ti-reload"></i> 
                            <a target="_blank" 
                            href="/ServicesStore/Details/@service.ServiceID/
                            @service.ServiceName">Service Link</a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    }
</div>

Snapshot of Service View

Image 28

Now clicking on Movies API service link, you can see details view of service with API Key for it.

Here, you have an option to deactivate service and reactivate service also by click on Deactivate button.

Image 29

Snapshot of After Deactivating Service

Image 30

Snapshot of After Re-activating Service

Image 31

After completing adding services controller and activating and deactivating services, next we are going to create Movies API.

Adding Movies API

Image 32

Credit for icon: Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY

In this part, before adding Web API Controller, we are going to add Interface and concrete class to get data from the database.

Adding IMovies Interface

The IMovie’s interface contains a single method inside it which is GetMoviesStore which will return a list of Movies.

Code Snippet of IMovies Interface

C#
using MoviesAPIStore.Models;
using System.Collections.Generic;

namespace MoviesAPIStore.Repository
{
    public interface IMovies
    {
        List<MoviesTB> GetMoviesStore();
    }
}

Adding MoviesConcrete Class

The Movies concrete class implement IMovie’s interface.

Code Snippet of MoviesConcrete Class

C#
using MoviesAPIStore.Models;
using MoviesAPIStore.MoviesContext;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MoviesAPIStore.Repository
{
    public class MoviesConcrete : IMovies
    {
        private DatabaseContext _context;
        public MoviesConcrete(DatabaseContext context)
        {
            _context = context;
        }

        // GET: api/LatestMovies
        public List<MoviesTB> GetMoviesStore()
        {
            try
            {
                var listofMovies = _context.MoviesTB.ToList();
                return listofMovies;
            }
            catch (System.Exception)
            {
                throw;
            }
        }
    }
}

Adding Movies Controller

In this part, we are going to add Web API Controller with name "MoviesAPIController" and this controller will return List of Movies. We have only implemented POST method.

Code snippet

C#
using Microsoft.AspNetCore.Mvc;
using MoviesAPIStore.Models;
using MoviesAPIStore.Repository;
using System.Collections.Generic;

namespace MoviesAPIStore.Controllers
{
    [Route("api/[controller]")]
    public class MoviesAPIController : Controller
    {
        IMovies _IMovies;
        public MoviesAPIController(IMovies IMovies)
        {
            _IMovies = IMovies;
        }

        // POST api/values
        [HttpPost]
        public List<MoviesTB> Post([FromQuery]string key)
        {
            return _IMovies.GetMoviesStore();
        }
    }
}

In the same way, we are going to add Music API.

Image 33

After completing adding Movies and Music API, next we are going to added Swagger for API documentation.

Adding Swagger to Application for API Documentation

Image 34

What Is Swagger?

Swagger allows you to describe the structure of your APIs so that machines can read them.

The ability of APIs to describe their own structure is the root of all awesomeness in Swagger. Why is it so great? Well, by reading your API’s structure, we can automatically build beautiful and interactive API documentation. We can also automatically generate client libraries for your API in many languages and explore other possibilities like automated testing.

Referenced: https://swagger.io/docs/specification/2-0/what-is-swagger/

For adding Swagger to project, just right click on Movies API store project and from the list, choose "Manage NuGet packages" a new window of NuGet will pop up. Inside that, choose to browse tab and search "Swashbuckle.AspNetCore" and click on Install button to install Swagger.

Image 35

After adding Swagger, we need to add some middleware of swagger to startup.cs class to make it work.

Below is a snapshot of startup class in which we registered and added swagger middleware.

Image 36

Now let’s save the application and run to see API documentation.

To access Swagger just after localhost port, enter "/swagger".

Swagger UI

Image 37

Wow, we have just configured it not written any code for design and documentation.

API Documentation for Movies API

Image 38

API Documentation for Music API

Image 39

Now we are done seeing the documentation of both APIs. Next, let’s validate API request by create Middleware.

Authenticating API Request Mechanism

Image 40

Credit: Icons made by Eucalyp from www.flaticon.com is licensed by CC 3.0 BY

In this process, we have provided API key to user (client) now while we are creating API authentication mechanism, we are going to validate every request that has come from client, and every request must contain API key which we have provided such that we can validate API key against database and check that user is authorized to access this service. If API key is valid, then the user will get a response from API. If the request is not valid, then the user will get error message about what he is missing in the request.

And one new functionally we have added in this process is we can activate and deactivate our service if user deactivate service and then he tries to access service, then he will get an error message "Service is Deactivated".

Next point is we have max request limit in this API application, if user has subscribed 1000 request and he tries to access API more than 1000 request, he will get an error message "Request Limit Exceeded"

And last scenario use might have subscribed two services and he sends Movies API key to Music API request and vice versa, then he will get an error message "Invalid User Key".

And if the request is valid, then we send movies collection as a response.

Till now, we have completed adding API and its documentation, now let’s have right the main process of this application is middleware to validate the request.

Image 41

Let’s start with adding interface with name IValidateRequest and declaring methods in it.

Adding IValidateRequest Interface

C#
namespace MoviesAPIStore.Repository
{
    public interface IValidateRequest
    {
        bool ValidateKeys(string Key);

        bool IsValidServiceRequest(string Key, string ServiceName);

        bool ValidateIsServiceActive(string Key);

        bool CalculateCountofRequest(string Key);
    }
}

We have declared four methods in the IValidateRequest interface.

  1. ValidateKeys

    In this method, we are going to take API key as input and check if this key exists in the database.

  2. IsValidServiceRequest

    In this method, we are going to check whether the API Key is sent to valid API. For that, we are taking key and Service Name (API Name) as input.

    For example, for Music API, the developer is not send Movies API key.

  3. ValidateIsServiceActive

    In this method, we are taking API key as input and check against a database that this service is Active or deactivate.

  4. CalculateCountofRequest

    In this method, we are taking API key as input and check request count against this API key.

    For example, if the user had subscribed for 1000 request service and if he exceeds the limit.

Adding ValidateRequest Concrete Class

In this part, we are going to add Concrete class with name ValidateRequestConcrete which is going to inherit IValidateRequest interface and implement all methods in it.

Code Snippet of ValidateRequest Concrete Class

C#
using Microsoft.EntityFrameworkCore;
using MoviesAPIStore.MoviesContext;
using System;
using System.Collections.Generic;
using System.Linq;

namespace MoviesAPIStore.Repository
{
    public class ValidateRequestConcrete : IValidateRequest
    {
        private DatabaseContext _context;

        public ValidateRequestConcrete(DatabaseContext context)
        {
            _context = context;
        }

        public bool ValidateKeys(string Key)
        {
            try
            {
                var result = (from apimanagertb in _context.APIManagerTB
                              where EF.Functions.Like(apimanagertb.APIKey, "%" + Key + "%")
                              select apimanagertb).Count();

                if (result > 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        public bool IsValidServiceRequest(string Key ,string ServiceName)
        {
            try
            {
                var serviceID = (from apimanagertb in _context.APIManagerTB
                              where EF.Functions.Like(apimanagertb.APIKey, "%" + Key + "%")
                              select apimanagertb.ServiceID).FirstOrDefault();

                var serviceName = (from servicestb in _context.ServicesTB
                                 where servicestb.ServiceID == serviceID
                                   select servicestb.APIName).FirstOrDefault();

                if (string.Equals(ServiceName, serviceName,
                    StringComparison.InvariantCultureIgnoreCase))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        public bool ValidateIsServiceActive(string Key)
        {
            try
            {
                var result = (from apimanagertb in _context.APIManagerTB
                              where EF.Functions.Like(apimanagertb.APIKey, "%" + Key + "%") 
                              && apimanagertb.Status == "A"
                              select apimanagertb).Count();

                if (result > 0)
                {
                    return true;
                }
                else
                {
                    return false;
                }

            }
            catch (Exception)
            {
                throw;
            }
        }

        public bool CalculateCountofRequest(string Key)
        {
            try
            {
                var totalRequestCount = (from apimanagertb in _context.APIManagerTB
                              join hittb in _context.HitsTB on 
                              apimanagertb.HitsID equals hittb.HitsID
                              where apimanagertb.APIKey == Key
                              select hittb.Hits).FirstOrDefault();

                var totalCurrentRequestCount = (from loggertb in _context.LoggerTB
                              where loggertb.APIKey == Key
                              select loggertb).Count();

                if (totalCurrentRequestCount >= totalRequestCount)
                {
                    return false;
                }
                else
                {
                    return true;
                }

            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}

Snapshot after Adding Interface IValidateRequest and ValidateRequestConcrete

Image 42

Adding Middleware to Validate API Request

Before starting this process, let’s understand why middleware. If we create a simple application in ASP.NET MVC in that for validating request API, we use DelegatingHandler. Now in ASP.NET Core, it has been replaced with middleware.

What is middleware?

Middleware is software that's assembled into an application pipeline to handle requests and responses.

Reference: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?tabs=aspnetcore2x

Let’s start with adding Middleware.

Note: Adding middleware, just right click on Middlewares folder, then choose -> Add ->New Item - a new dialog will pop with name Add New Item, inside that, choose "ASP.NET Core" -> "Web" then in List of templates, you will find "Middleware Class", just select it.

For adding middleware, we are going to create a Middlewares folder in the application. Inside that, we are going to add middleware with name "ApiKeyValidatorsMiddleware".

Image 43

Code Snippet of ApiKeyValidatorsMiddleware

C#
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using MoviesAPIStore.Models;
using MoviesAPIStore.Repository;
using System;
using System.Threading.Tasks;

namespace MoviesAPIStore.Middlewares
{
    // You may need to install the Microsoft.AspNetCore.Http.Abstractions package 
    // into your project
    public class ApiKeyValidatorsMiddleware
    {
        private readonly RequestDelegate _next;
        IValidateRequest _IValidateRequest { get; set; }
        IRequestLogger _IRequestLogger { get; set; }
        public ApiKeyValidatorsMiddleware(RequestDelegate next, 
               IValidateRequest ivalidaterequest, IRequestLogger irequestlogger)
        {
            _next = next;
            _IValidateRequest = ivalidaterequest;
            _IRequestLogger = irequestlogger;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            try
            {
                var remoteIpAddress = httpContext.Connection.RemoteIpAddress;

                if (httpContext.Request.Path.StartsWithSegments("/api"))
                {
                    var queryString = httpContext.Request.Query;
                    StringValues keyvalue;
                    queryString.TryGetValue("key", out keyvalue);

                    if (httpContext.Request.Method != "POST")
                    {
                        httpContext.Response.StatusCode = 405; //Method Not Allowed               
                        await httpContext.Response.WriteAsync("Method Not Allowed");
                        return;
                    }

                    if (keyvalue.Count == 0)
                    {
                        httpContext.Response.StatusCode = 400; //Bad Request                
                        await httpContext.Response.WriteAsync("API Key is missing");
                        return;
                    }
                    else
                    {
                        string[] serviceName = httpContext.Request.Path.Value.Split('/');

                        if(!_IValidateRequest.IsValidServiceRequest(keyvalue, serviceName[2]))
                        {
                            httpContext.Response.StatusCode = 401; //UnAuthorized
                            await httpContext.Response.WriteAsync
                                  ("Invalid User Key or Request");
                            return;
                        }
                        else if (!_IValidateRequest.ValidateKeys(keyvalue))
                        {
                            httpContext.Response.StatusCode = 401; //UnAuthorized
                            await httpContext.Response.WriteAsync("Invalid User Key");
                            return;
                        }
                        else if (!_IValidateRequest.ValidateIsServiceActive(keyvalue))
                        {
                            httpContext.Response.StatusCode = 406; //NotAcceptable
                            await httpContext.Response.WriteAsync("Service is Deactived");
                            return;
                        }
                        else if (!_IValidateRequest.CalculateCountofRequest(keyvalue))
                        {
                            httpContext.Response.StatusCode = 406; //NotAcceptable
                            await httpContext.Response.WriteAsync("Request Limit Exceeded");
                            return;
                        }
                        else
                        {
                            string[] apiName = httpContext.Request.Path.Value.Split('/');

                            var loggertb = new LoggerTB()
                            {
                                LoggerID = 0,
                                ContentType = 
                                Convert.ToString(httpContext.Request.ContentType),
                                APIKey = keyvalue,
                                CreatedDate = DateTime.Now,
                                Host = httpContext.Request.Host.Value,
                                IsHttps = httpContext.Request.IsHttps ? "Yes" : "No",
                                Path = httpContext.Request.Path,
                                Method = httpContext.Request.Method,
                                Protocol = httpContext.Request.Protocol,
                                QueryString = httpContext.Request.QueryString.Value,
                                Scheme = httpContext.Request.Scheme,
                                RemoteIpAddress = Convert.ToString
                                (httpContext.Connection.RemoteIpAddress),
                                LoggerAPI = apiName[2],
                            };

                            _IRequestLogger.InsertLoggingData(loggertb);
                        }
                    }
                }
                await _next.Invoke(httpContext);
            }
            catch (Exception)
            {
                throw;
            }
        }
    }

    // Extension method used to add the middleware to the HTTP request pipeline.
    public static class MiddlewareExtensions
    {
        public static IApplicationBuilder UseMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<ApiKeyValidatorsMiddleware>();
        }
    }
}

Let’s Understand Code of Middleware

In this middleware first, we are injecting two dependencies:

  1. IValidateRequest
  2. IRequestLogger

IValidateRequest interface contains all API validation methods in it.

IRequestLogger interface contains a method for logging all API request.

Next, after adding newly middleware, you will see Invoke method in which we are going to write the entire validation process.

  1. The first step we are validating is we are only going to validate API request.

    Image 44

  2. In the second step, we are going to only allow post request for all API. If it is another request, then we are going to send an error message.

    Image 45

  3. In the third step, we are going to check whether query string ("Key") exists in request URI.

    Image 46

  4. In the fourth step, we are going to check the key user sent in request and API he is accessing are valid. For example, For Music API, developer does not send Movies API key.

    Image 47

  5. In the fifth step, we are going to check whether the key user sent is valid or not.

    Image 48

  6. In the sixth step, we are going to check API Key against a database that this service is Active or deactivate.

    Image 49

  7. In the seventh step, we are going to check API request count against this API key if it exceeds, then we are going to send an error response to the user.

    Image 50

Finally, if request passes all barriers, it means the request is valid.

Registering Middleware in startup.cs

Now let’s register this middleware in a startup.cs class such that every API request can be validated.

Image 51

Now, we are done with adding and registering "ApiKeyValidators" Middleware. Now let’s run the application and check how middleware works.

Accessing Latest Movies API Controller

For accessing Movies API, we need API key which we have generated.

Snapshot of Movies API KEY

Image 52

Now we are going to use POSTMAN APP to access Movies API.

Setting up POST Request Parameters

For downloading Postman APP, click on the below URL:

Image 53

https://www.getpostman.com/postman

Setting Parameters for API.

Image 54

After sending valid key and request, we got response.

Response after Sending Valid API Request

Image 55

Image 56

Send Invalid Request Type

We have sent Get request to Movies API, it shows "Method Not Allowed" because in middleware, we have only allowed POST request for all APIs.

Image 57

Send Invalid Key

We have sent an invalid API key in this request to test what response we get.

Image 58

Deactivate Movies Service and Send Request

We have deactivated Movies API Service. Let’s send a request and test what response we get.

Image 59

The response we get is proper "Service is Deactivated".

Image 60

Sending Request More Than We Subscribed

We have subscribed 1000 requests and we have sent complete 1000 requests. I am trying to send the request again, then it shows error message "Request Limit Exceeded".

Image 61

The table where we have stored a log of all API requests.

Snapshot of LoggerTB Table

Image 62

Now we have completed the security part. Let’s have a look at the Dashboard.

Final Project Structure

This is a final project structure of the application.

In this part, we can see a folder for storing AES256Encryption algorithm, filters folder contains a filter for validating user session. Middleware folder for storing all middleware of application. And repository for storing interface and concrete classes.

Image 63

Note: I have keep repository folder in main application because this is a small demo application in the large application. You need to move it to a separate class library, the same way you need to do for models as well.

Dashboard

On this dashboard, you can see your Request graph for API. For Movies API, we have sent 1000 Requests, that’s why it has to peek at the chart.

These charts are shown on data which is logged in every request.

These charts are CHARTIST.JS.

Snapshot of Dashboard

Image 64

Tools Used in this Project

  1. cDesign Template: Paper Dashboard by Creative Tim
  2. Swagger
  3. Dapper ORM
  4. chartist.js Charts
  5. Icons from https://www.flaticon.com/
  6. AES256 Encryption

Conclusion

In this article, we have learned a complete cycle of API development in ASP.NET Core WEB API. We started with registering User, then we have generated API Key. Further, we have created API, along with that we have provided feature to activate and deactivate service, then we came to the main process of validating API request, and finally we have done logging of each request such that a developer or normal user knows how many times a user has requested an API.

Thank you for reading. I hope you liked my article.

History

  • 6th February, 2018: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
India India
Microsoft Most Valuable Professional
Code Project Most Valuable Author
C# Corner Most Valuable Professional

I am Senior Technical lead Working on.Net Web Technology
ASP.NET MVC,.Net Core,ASP.NET CORE, C#, SQL Server, MYSQL, MongoDB, Windows

Comments and Discussions

 
SuggestionThanks a lot for this article Pin
Cemal Sener31-Mar-21 3:26
Cemal Sener31-Mar-21 3:26 
QuestionGetChartsMoviesreport() Method Pin
Member 1510045815-Mar-21 19:16
Member 1510045815-Mar-21 19:16 
I am Using Mysql then what are the changes?
GeneralGreat Article sir Pin
Member 1137379716-May-20 6:03
Member 1137379716-May-20 6:03 
QuestionGood article Pin
JLDevC#17-May-19 4:47
JLDevC#17-May-19 4:47 
PraiseI would only say one thing 'BRAVO' Pin
Member 1358577013-Jan-19 21:42
Member 1358577013-Jan-19 21:42 
PraiseVery Good Article Pin
Member 1119209930-Dec-18 11:00
Member 1119209930-Dec-18 11:00 
GeneralRe: Very Good Article Pin
Saineshwar Bageri8-Jan-19 19:18
Saineshwar Bageri8-Jan-19 19:18 
QuestionCannot run sample project Pin
Member 1384455629-May-18 10:52
Member 1384455629-May-18 10:52 
QuestionBroken link for readme file Pin
Mou_kol5-Mar-18 21:58
Mou_kol5-Mar-18 21:58 
AnswerRe: Broken link for readme file Pin
Saineshwar Bageri6-Mar-18 17:00
Saineshwar Bageri6-Mar-18 17:00 
GeneralMy vote of 5 Pin
Mou_kol5-Mar-18 21:56
Mou_kol5-Mar-18 21:56 
Questioni have problems charts Pin
lonely999912-Feb-18 3:26
lonely999912-Feb-18 3:26 
AnswerRe: i have problems charts Pin
Saineshwar Bageri12-Feb-18 5:17
Saineshwar Bageri12-Feb-18 5:17 
GeneralRe: i have problems charts Pin
lonely999912-Feb-18 5:41
lonely999912-Feb-18 5:41 
GeneralRe: i have problems charts Pin
Saineshwar Bageri12-Feb-18 18:58
Saineshwar Bageri12-Feb-18 18:58 
GeneralRe: i have problems charts Pin
lonely999913-Feb-18 4:15
lonely999913-Feb-18 4:15 
Question[My vote of 2] Middleware hidden in article with too many distractions and bad practices Pin
Marc Lewandowski10-Feb-18 2:32
professionalMarc Lewandowski10-Feb-18 2:32 
AnswerRe: [My vote of 2] Middleware hidden in article with too many distractions and bad practices Pin
Saineshwar Bageri10-Feb-18 21:52
Saineshwar Bageri10-Feb-18 21:52 
PraiseRe: [My vote of 2] Middleware hidden in article with too many distractions and bad practices Pin
Virshu8-Apr-18 9:27
Virshu8-Apr-18 9:27 
GeneralRe: [My vote of 2] Middleware hidden in article with too many distractions and bad practices Pin
Kelvin Wu14-Aug-18 22:44
Kelvin Wu14-Aug-18 22:44 
GeneralMy vote of 5 Pin
Suvendu Shekhar Giri7-Feb-18 0:16
professionalSuvendu Shekhar Giri7-Feb-18 0:16 
GeneralRe: My vote of 5 Pin
Saineshwar Bageri7-Feb-18 2:51
Saineshwar Bageri7-Feb-18 2:51 
PraiseGood Article Pin
madhan20086-Feb-18 22:29
madhan20086-Feb-18 22:29 
GeneralRe: Good Article Pin
Saineshwar Bageri6-Feb-18 23:34
Saineshwar Bageri6-Feb-18 23:34 
GeneralMy vote of 5 Pin
Member 121976546-Feb-18 22:06
Member 121976546-Feb-18 22:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.