how-create-asp-dotnet-core-app

How to create ASP.net Core web application to consume a web Service?

In my previous post I have created a Web Service Soap  (WebServiceSoapDemo)

In this article I am going to create an ASP.net Core  web application project and add it  to WebServiceSoapDemo Solution.  I shall functionality to consume the WebService (Web Service Soap)

I. Create a new ASP.NET Core Web Application

open WebServiceSoapDemo solution.

Right click on  solution and click Add new project in the new GUI search field search for web and then select ASP.NET Core Web Application.(Model, view, controller) as displayed bellow:

Press to Next and give the project name WebIdentityDemo and press to Next button

in the new GUI, slect  Target framework: .NET 5.0 and  uncheck Configure for HTTPS as shown bellow:

Press to Create button to create your project then the new project shall be displayed in your solution as following:

II. Create SQL Database and Setup Project Connection String

  1.  Create database WebIdentityDemo by starting SQL Server Management Studio and create a database and name it: WebIdentityDemo
  2. Create a table IdentityUser with script in the bellow which we need  for this project
USE [WebIdentityDemo]
GO
_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[IdentityUser](
[ID] [int] IDENTITY(1,1) NULL,
[Email] nvarchar(50)   NULL,
[Password] nvarchar(50)  NULL,
[Role] nvarchar(50)  NULL,
[Reg_Date] [datetime]  LNULL
) ON [PRIMARY]

Database and table shown as bellow:

 

3. Create sp_loginUser stored procedure. This procedure will handle the query for the validation of the user. We use MD5 hashing for the password to make it more secure. To create the stored procedure, you can execute the SQL query below.

USE [WebIdentityDemo]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[sp_loginUser]
@email Nvarchar(50),
@password nvarchar(200)
AS
BEGIN
SET NOCOUNT ON;
Select * FROM IdentityUser where Email = @email and [Password] = CONVERT(VARCHAR(32), HashBytes('MD5', @password), 2) 
END

4.  Create sp_registerUser Stored Procedure. This procedure will register a new user for our web application. See the code snippet below.

USE [WebIdentityDemo]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[sp_registerUser]
@email Nvarchar(50),
@password nvarchar(200),
@role nvarchar(50),
@retval int OUTPUT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO IdentityUser(Email,[Password],[Role],Reg_Date) VALUES(@email,CONVERT(VARCHAR(32), HashBytes('MD5', @password), 2),@role,GETDATE())
if(@@ROWCOUNT > 0)
BEGIN
SET @retval = 200
END
ELSE
BEGIN
SET @retval = 500
END
END

5. lets set up our web application to use this database. To do that, open your appsettings.json and add the connection string for this database. You can visit this link to know the default SQL connection strings.

"ConnectionStrings": {
"default": "Server=CDN7496N83\SQLEXPRESS;Database=WebIdentityDemo;User Id=Sahand;Password=Sahand01;"
}

III. Create Model properties

This model classes will be used later on the repository class and view models.

  1. IdentityModel.cs » This is the model that we are going to map with the dapper query in the login method on the Repository class.
public class IdentityModel
{
public int ID { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string Role { get; set; }
public string Reg_Date { get; set; }
}

2. LoginViewModel.cs » Model that will be bind with our login view.

public class LoginViewModel
{
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Remember Me")]
public bool RememberMe { get; set; }
public string Role { get; set; }
}

4. Response.cs » we will use this as default response on Repository Methods.

public class Response<T>
{
public T Data { get; set; }
public string message { get; set; }
public int code { get; set; }
}

 

IV. Create IRepository Interface and a Repository Class

Repository class will handle the Query to the database. This repository is IN charge of the communication between our web application and the SQL Stored procedure we created awhile ago.

  1. Create a Repository folder. Under that folder, create an interface class and name it IRepository. Initialise implementation method for this class using the code below.
public interface IRepository
{
Task<Response<IdentityModel>> LoginAsync(LoginViewModel loginView);
Task<Response<string>> RegisterAsync(RegisterViewModel registerView);
}

This is the full code inside my IReposirtory Interface.

using WebIdentityDemo.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebIdentityDemo.Repository
{
public interface IRepository
{
Task<Response<IdentityModel>> LoginAsync(LoginViewModel loginView);
Task<Response<string>> RegisterAsync(RegisterViewModel registerView);
}
}

2- Create the implementation for this interface. To do that, create a class file under the Repository folder with Repository as the filename and paste the codes below.

Get the connection string from appsettings.json

private readonly IConfiguration _configuration;
public Repository(IConfiguration configuration)
{
 _configuration = configuration;
 }

LoginAsync Method

This will consume the sp_loginUser stored procedure using dapper

public async Task<Response<IdentityModel>> LoginAsync(LoginViewModel loginView)
 {
Response<IdentityModel> response = new Response<IdentityModel>();
var sp_params = new DynamicParameters(); sp_params.Add("email",loginView.Email,DbType.String); sp_params.Add("password", loginView.Password, DbType.String);
try
{
 using IDbConnection dbConnection = new SqlConnection(_configuration.GetConnectionString("default")); response.Data = await dbConnection.QueryFirstOrDefaultAsync<IdentityModel>("sp_loginUser", sp_params, commandType: CommandType.StoredProcedure); response.message = (response.Data is null) ? "Login failed.Please check Username and / or password" : "data found";
response.code = (response.Data is null) ? 500 : 200;
}
catch (Exception ex)
 { response.code = 500;
   response.message = ex.Message;
}
 return response;
}

RegisterAsync Method

This will consume the sp_registerUser stored procedure using dapper

public async Task<Response<string>> RegisterAsync(RegisterViewModel registerView)
{
Response<string> response = new Response<string>();
var sp_params = new DynamicParameters();
sp_params.Add("email", registerView.Email, DbType.String);
sp_params.Add("password", registerView.Password, DbType.String);
sp_params.Add("role", registerView.Role, DbType.String);
sp_params.Add("retVal", DbType.String,direction:ParameterDirection.Output);
using (IDbConnection dbConnection = new SqlConnection(_configuration.GetConnectionString("default")))
{
if (dbConnection.State == ConnectionState.Closed) dbConnection.Open();
using var transaction = dbConnection.BeginTransaction();
try
{
await dbConnection.QueryAsync<string>("sp_registerUser", sp_params, commandType: CommandType.StoredProcedure, transaction: transaction);
response.code = sp_params.Get<int>("retVal"); //get output parameter value
transaction.Commit();
response.message = (response.code == 200) ? "Successfully Registered" : "Unable to register user";
}
catch (Exception ex)
{
transaction.Rollback();
response.Data = ex.Message;
response.message = "An error encountered during saving!";
response.code = 500;
}
};
return response;
}

V. Register IRepository Interface and Repository Class

To configure an Interface and a Repository Class, we need to let our application know that this two is connected. To do that open Startup.cs and add a scope under ConfigureServices method using the code snippet below.

services.AddScoped<IRepository, Repository.Repository>(); 

VI. Set up different role in HomeController

This steps will help us test the role of the user that we will create later. Open HomeController and setup authorise role using the code snippet below.

[Authorize(Roles = "Admin")]
public IActionResult Index()
{
return View();
}
 [Authorize(Roles = "User")]
public IActionResult Privacy()
{ 
return View();
 }
[Authorize(Roles = “Admin”)] This means that only user with Admin role can access
[Authorize(Roles = “User”)] This means that only user with User role can access

VII. Add Cookie Authentication on startup.cs

To add cookie authentication, copy the code below inside ConfigureServices method from your startup.cs.

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(option => {
option.LoginPath = new PathString("/Auth/index");
 option.AccessDeniedPath = new PathString("/Auth/AccessDenied");
 });
option.LoginPath This option will redirect user to login page when user is not authenticated
option.AccessDeniedPath This is the path if the user role is denied

 

Add the following code to Configure method. in startup.cs

 app.UseAuthentication();

the whole code in the startup.cs file:

using WebIdentityDemo.Repository;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebIdentityDemo
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(option => {
option.LoginPath = new PathString("/Auth/index");
option.AccessDeniedPath = new PathString("/Auth/AccessDenied");
});
services.AddScoped<IRepository, Repository.Repository>();
services.AddControllersWithViews();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}

VIII. Create and Configure AuthController

This controller will handle the HTTP request for login, registration, logout, and access denied. This controller is the bridge from the view to the repository that we created just awhile ago.

  1. Right-click on the Controllers folder then choose Add » Controller
  2.  Choose MVC Controller – empty. See the image below.  3. give the name of the controller,  AuthController. Then click the Add button.

IX. Login Method – AuthController

We will use the index method for the login. This will handle the HTTP GET and POST request of login.

  1. We shall use the Repository that we created awhile ago, we need to inject the IRepository to the AuthController. To do that, copy the code snippet below:
private readonly IRepository _repository;
public AuthController(IRepository repository)
{
_repository = repository;
}

2- Create Index Method. We will create two index method, one for the get request and the other one is for the POST request.

Index() HTTPGET Display the login view design
Index() HTTPPOST Perform validation of the user credential
HttpContext.SignInAsync Sign in user using cookie authentication

We use claims to store user information like role, username and email. This information will be stored on a cookie and can be used later in checking if the user is logged in or not.

See the code snippet below:

public IActionResult Index()
{ return View();
}
 [HttpPost]
public async Task<IActionResult> IndexAsync(LoginViewModel loginViewModel)
 {
if (ModelState.IsValid) { var username = loginViewModel.Email; var password = loginViewModel.Password;var result = await _repository.LoginAsync(loginViewModel);
 if (result.code == 200)
{
var claims = new[]
{
new Claim(ClaimTypes.Name, username), new Claim(ClaimTypes.Role, result.Data.Role),
 new Claim(ClaimTypes.Email,username)};
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity), new AuthenticationProperties
{
 IsPersistent = false //remember me
});
return Redirect("~/Home/Index");
}
else
{
ModelState.AddModelError("", result.message);
 }
}
return View(loginViewModel);
 }

3- Create a simple form for our login (Index.cshtml) and bind it with the LoginViewModel. To do that, see the code snippet below:

@model WebIdentityDemo.Models.LoginViewModel
@{
ViewData["Title"] = "Login";
}
<div class="row">
<div class="col-md-12">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<div class="custom-control custom-checkbox">
<input asp-for="RememberMe" type="checkbox" class="custom-control-input">
<label class="custom-control-label" asp-for="RememberMe">@Html.DisplayNameFor(m => m.RememberMe)</label>
</div>
</div>
<button type="submit" class="btn-primary">Login</button>
</form>
</div>
</div>

This shall be displayed  as following  (select Index.cshtml and press to show in browser).

X. Register Method – AuthController

Add register method  in the AuthController (Register(), and AsyncResgiter())  as we have done login method before.

  1. Create two method a GET (Register() method)  and a POST ( AsyncResgiter()) Register Method. See the code snippet below.
//[HttpGet] // by default
public IActionResult Register()
{
return View();
}
[HttpPost]
public async Task<IActionResult> RegisterAsync(RegisterViewModel registerViewModel)
{
if (ModelState.IsValid)
{
var result = await _repository.RegisterAsync(registerViewModel);
if (result.code == 200)
{
return RedirectToAction("Index");
}
else
{
ModelState.AddModelError("", result.message);
}
}
return View(registerViewModel);
}

 

2. Create a View for the Register and use the snippet below to create a simple register form.

 

@model WebIdentityDemo.Models.RegisterViewModel
@{
ViewData["Title"] = "Register";
}
<h1>Register</h1>
<div class="row">
<div class="col-md-12">
<form method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Role"></label>
<input asp-for="Role" class="form-control" />
<span asp-validation-for="Role" class="text-danger"></span>
</div>
<button type="submit" class="btn-primary">Register</button>
</form>
</div>
</div>

This is register view will look like as bellow (if you select file: Register.cshtml and press to Show in Browser):

XI. SignOut Method – AuthController

for user sign out from cookie authentication we need to add SignOutAsync() method() in the  AuthController see the code snipp bellow

AuthController

<preclass=”line-numbers”>public async Task SignOutAsync() {
await HttpContext.SignOutAsync(
CookieAuthenticationDefaults.AuthenticationScheme);

return RedirectToAction(“Index”);
}

You  can add  sign out menu, by adding the codesnipp bellow in the file _Layout.cshtml under Folder: “View: Shared” which is the next step.

<li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Auth" asp-action="SignOut">Logout</a> </li>

XII. Modify _Layout.cshml – Menu

Layout.cshtml can be found under the Views » Shared. This layout is the master layout of an ASP.NET MVC template. You can customise the menu here if you want something to show if a user is authenticated. To do that you can use the code snippet below.

@if (User.Identity.IsAuthenticated)
 {
 //code here 
} 
else 
{ //code here }

The whole layout of file: Layout.cshtml file loke likes in the bellow:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Identity Demo | WebIdentityDemo</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">WebItentityDemo (Identity)</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
@if (User.Identity.IsAuthenticated)
{
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
</ul>
<ul class="navbar-nav float-left">
<li class="nav-item">
<a class="nav-link text-dark" >@User.Identity.Name</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Auth" asp-action="SignOut">Logout</a>
</li>
</ul>
}else
{
<ul class="navbar-nav flex-grow-1 float-right">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Auth" asp-action="Register">Register</a>
</li>
</ul>
}
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
&copy; 2021 - WebIdentityDemo - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>

 

Welcome 

useremail@domain.com

Conclusion

In this post we have set up a login web application in ASP.net Core using Cookie Authentication . We also use dapper on consuming the stored procedure that we use to register and log in.

The source code can be find in Githup.

In my Next Post shall describe consuming of WebServiceSoapDemo via WebIdentityDemo

This post was part of Web and WCF Services step by step

Back to home page