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
- Create database WebIdentityDemo by starting SQL Server Management Studio and create a database and name it: WebIdentityDemo
- 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.
- 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.
- 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.
- Right-click on the Controllers folder then choose Add » Controller
- 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.
- 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.
- 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">
© 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