European ASP.NET 4.5 Hosting BLOG

BLOG about ASP.NET 4, ASP.NET 4.5 Hosting and Its Technology - Dedicated to European Windows Hosting Customer

European ASP.NET Core Hosting :: Send Email Using ASP.NET Core 5 Web API

clock June 21, 2021 07:08 by author Peter

In this article, we will learn how to send an email in ASP.Net Core Web API. We will code the endpoint that will send the email to a user of your business customers. I am implementing the Cloud Native Microservices architecture in my project. And this service is one of the microservices of my project and I will extend this Microservice to handle all types of emails in my project.

Create the ASP.Net Core Web API Project
First, you need to create the ASP.Net core web project using the ASP.Net core 5 web API.
Install the MailKit Nuget Package

Right click on your Solution File. Now click on select the option Nuget Package and then find the MailKit Library for your project configuration. After the installation of this library, add the reference in the code.

Install-Package NETCore.MailKit

C#

You need to include the following namespaces in your code for the correct configuration.
using MailKit.Net.Smtp;
using MailKit.Security;

Add the Folder Model
After the creation of the Model, now create the class MailRequest with the following information.
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace _101SendEmailNotificationDoNetCoreWebAPI.Model
{
    public class MailRequest
    {
        public string ToEmail { get; set; }
        public string Subject { get; set; }
        public string Body { get; set; }
        public List<IFormFile> Attachments { get; set; }
    }
}


Not Add the Folder Services
After the creation of the folder now add the following classes.

IMailService
using _101SendEmailNotificationDoNetCoreWebAPI.Model;
using System.Threading.Tasks;
namespace _101SendEmailNotificationDoNetCoreWebAPI.Services
{
    public interface IMailService
    {
        Task SendEmailAsync(MailRequest mailRequest);

    }
}


using MimeKit;
using System.IO;
using System.Threading.Tasks;
using MailKit.Net.Smtp;
using MailKit.Security;
using Microsoft.Extensions.Options;
using _101SendEmailNotificationDoNetCoreWebAPI.Settings;
using _101SendEmailNotificationDoNetCoreWebAPI.Model;
namespace _101SendEmailNotificationDoNetCoreWebAPI.Services
{
    public class MailService : IMailService
    {
        private readonly MailSettings _mailSettings;
        public MailService(IOptions<MailSettings> mailSettings)
        {
            _mailSettings = mailSettings.Value;
        }
        public async Task SendEmailAsync(MailRequest mailRequest)
        {
            var email = new MimeMessage();
            email.Sender = MailboxAddress.Parse(_mailSettings.Mail);
            email.To.Add(MailboxAddress.Parse(mailRequest.ToEmail));
            email.Subject = mailRequest.Subject;
            var builder = new BodyBuilder();
            if (mailRequest.Attachments != null)
            {
                byte[] fileBytes;
                foreach (var file in mailRequest.Attachments)
                {
                    if (file.Length > 0)
                    {
                        using (var ms = new MemoryStream())
                        {
                            file.CopyTo(ms);
                            fileBytes = ms.ToArray();
                        }
                        builder.Attachments.Add(file.FileName, fileBytes, ContentType.Parse(file.ContentType));
                    }
                }
            }
            builder.HtmlBody = mailRequest.Body;
            email.Body = builder.ToMessageBody();
            using var smtp = new SmtpClient();
            smtp.Connect(_mailSettings.Host, _mailSettings.Port, SecureSocketOptions.StartTls);
            smtp.Authenticate(_mailSettings.Mail, _mailSettings.Password);
            await smtp.SendAsync(email);
            smtp.Disconnect(true);
        }
    }
}


Add the Folder Settings
Add the class setting in which you define the following Properties.
namespace _101SendEmailNotificationDoNetCoreWebAPI.Settings
{
    public class MailSettings
    {
        public string Mail { get; set; }
        public string DisplayName { get; set; }
        public string Password { get; set; }
        public string Host { get; set; }
        public int Port { get; set; }
    }
}


Add the Controller in the project
Create the Email Controller in which you define the endpoint. Send Email is responsible for sending the email to your business customers.
using _101SendEmailNotificationDoNetCoreWebAPI.Model;
using _101SendEmailNotificationDoNetCoreWebAPI.Services;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Threading.Tasks;

namespace _101SendEmailNotificationDoNetCoreWebAPI.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class EmailController : Controller
    {

        private readonly IMailService mailService;
        public EmailController(IMailService mailService)
        {
            this.mailService = mailService;
        }

        [HttpPost("Send")]
        public async Task<IActionResult> Send([FromForm] MailRequest request)
        {
            try
            {
                await mailService.SendEmailAsync(request);
                return Ok();
            }
            catch (Exception ex)
            {

                throw ex;
            }

        }


    }
}


Configure Each Service In Start-up Class
using _101SendEmailNotificationDoNetCoreWebAPI.Services;
using _101SendEmailNotificationDoNetCoreWebAPI.Settings;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace _101SendEmailNotificationDoNetCoreWebAPI
{
    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.Configure<MailSettings>(Configuration.GetSection("MailSettings"));
            services.AddTransient<IMailService, Services.MailService>();

            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "_101SendEmailNotificationDoNetCoreWebAPI", Version = "v1" });
            });
            services.AddCors(c =>
            {
                c.AddPolicy("AllowOrigin", options => options.AllowAnyOrigin());
            });
        }

        // 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();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "_101SendEmailNotificationDoNetCoreWebAPI v1"));
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}


Configure the Mail Setting in AppSetting.Json
Configure the SMTP Setting in AppSetting.Json in the following way,

"MailSettings": {
    "Mail": "[email protected]",
    "DisplayName": "Your Brand Name",
    "Password": "YourPassword",
    "Host": "SMTP Host",
    "Port": 587
  },

  •  Mail Define your email Address
  •  Display Name is your Brand Name
  •  The password of your SMTP Server
  •  The host is your SMTP Host
  •  Port No is Your SMTP PORT No

Run your Project and hit the End Point. Now Swagger UI Exposes the End Point


After hitting the endpoint now add the required information in the request body of the endpoint.


Now click on the execute button when the endpoint successfully sends the request then the status is == 200 so our email is successfully sent to the required email address.


In the case of the GMAIL address now click on the Spam folder and your email is there.

In the case of an Outlook/Microsoft Account, just click your Inbox and receive your email there.

Now, check your Email address inbox folder.



European ASP.NET Core Hosting :: Manage Primary Key And Foreign Key Relationship During Migration

clock June 14, 2021 07:14 by author Peter

In this article, we will learn how to create models for database table migration. When we build a simple database for our project, then it is easy to build class models. But when we develop a complex database for our project, then it's hard to maintain PK & FK relationship over the model. It is not easy to maintain, the string length and datatypes over the model.

So, let's see a few points while creating a model class.
Okay, here are the three tables, names - UserDetails, Customer, and Order. Here, we want to create the ID column as the primary key and a foreign key to the UserId column with the AspNetUser table. Like in the database here, also we want to restrict columns with string length. And also want to create two primary keys in a single model.
like below,
 
 Manage Primary Key And Foreign Key Relationship During Migration  Manage Primary Key And Foreign Key Relationship During Migration
 
 Manage Primary Key And Foreign Key Relationship During Migration
 
Here, Firstly I am going to create the UserDetails table. In which few properties and a foreign key relationship exit with the AspnetUser table.
    public class UserDetails {  
        [Key]  
        public int Id {  
            get;  
            set;  
        }  
        [StringLength(100)]  
        [Column(TypeName = "varchar(100)")]  
        public string Name {  
            get;  
            set;  
        }  
        [StringLength(50)]  
        [Column(TypeName = "varchar(50)")]  
        public string Email {  
            get;  
            set;  
        }  
        [StringLength(1)]  
        [Column(TypeName = "char(1)")]  
        public string Gender {  
            get;  
            set;  
        }  
        public string ProfilePic {  
            get;  
            set;  
        }  
        [ForeignKey("IdentityUser")]  
        public string UserId {  
            get;  
            set;  
        }  
        public virtual IdentityUser IdentityUser {  
            get;  
            set;  
        }  
        public byte Role {  
            get;  
            set;  
        }  
        public DateTime ? CreatedOn {  
            get;  
            set;  
        }  
        public bool ? IsActive {  
            get;  
            set;  
        }  
    }   


Here are a few packages for model attribute properties, which we will use to decorate class properties.

    using System.ComponentModel.DataAnnotations;  
    using System.ComponentModel.DataAnnotations.Schema;  

In this model, we are making a Foreign Key relationship with IdentityUser class, which is available in the database with the name AspnetUsers.

    using Microsoft.AspNetCore.Identity;  

If we want to set the string length of 100/50 or something else varchar characters in the database of the Name property, then we define attribute like this,
    [StringLength(100)]    
    [Column(TypeName = "varchar(100)")]    
    public string Name { get; set; }    


If we desire to set the string length of 1 character only in the database of the Gender property, then we define attribute like this,
    [StringLength(1)]    
    [Column(TypeName = "char(1)")]    
    public string Gender { get; set; }   


If we desire to set the DateTime datatype with nullable constraint in the database of the CreatedOn property, then we define attribute like this,
    public DateTime? CreatedOn { get; set; }  

If we desire to make a foreign Key relationship with IdentityUser and store the IdentityUser table Id in the UserId column, then we define attribute like this,
    [ForeignKey("IdentityUser")]  
    public string UserId { get; set; }  
    public virtual IdentityUser IdentityUser { get; set; }  


Now, we will create the customer table. Which contains FullName, Description & Address properties, and the one foreign Key relationship with UserDetails table in the UserDetailsId column.
    public class Customer {  
        [Key]  
        public int Id {  
            get;  
            set;  
        }  
        [StringLength(100)]  
        [Column(TypeName = "varchar(100)")]  
        public string FullName {  
            get;  
            set;  
        }  
        [StringLength(500)]  
        [Column(TypeName = "varchar(500)")]  
        public string Description {  
            get;  
            set;  
        }  
        [StringLength(500)]  
        [Column(TypeName = "varchar(500)")]  
        public string Address {  
            get;  
            set;  
        }  
        [ForeignKey("UserDetails")]  
        public virtual int UserDetailsId {  
            get;  
            set;  
        }  
        public virtual UserDetails UserDetails {  
            get;  
            set;  
        }  
    }   


If we wish to create two foreign Keys in a single table like UserDetailsId, CustomerId in the Order table, then we write two properties for a single foreign key like this,
    public class Order  
       {  
           [Key]  
           public int Id { get; set; }  
      
           [StringLength(100)]  
           [Column(TypeName = "varchar(100)")]  
           public string OrderNumber { get; set; }  
      
           [ForeignKey("Customer")]  
           public virtual int CustomerId { get; set; }  
           public virtual Customer Customer { get; set; }  
      
           [ForeignKey("UserDetails")]  
           public virtual int UserDetailsId { get; set; }  
           public virtual UserDetails UserDetails { get; set; }  
       }  


Register all tables in ApplicationDbContext class set the proper connection string in appsettings.json, register properly this class to Startup.cs class, and then run migration by migration command.
 
ApplicationDbContex.cs class
    public class ApplicationDbContext: IdentityDbContext {  
        public ApplicationDbContext(DbContextOptions < ApplicationDbContext > options): base(options) {}  
        public DbSet < UserDetails > UserDetails {  
            get;  
            set;  
        }  
        public DbSet < Customer > Customer {  
            get;  
            set;  
        }  
        public DbSet < Order > Order {  
            get;  
            set;  
        }  
    }   

appsettings.json class
    "ConnectionStrings": {  
       "DefaultConnection": "Server=MyServer;Database=db_A;user=sa;password=admin@1234;Trusted_Connection=False;"  
     },  

Startup.cs class
    public void ConfigureServices(IServiceCollection services) {  
        services.AddDbContext < ApplicationDbContext > (options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));  
        services.AddDefaultIdentity < IdentityUser > (options => options.SignIn.RequireConfirmedAccount = true).AddEntityFrameworkStores < ApplicationDbContext > ();  
        services.AddControllersWithViews();  
        services.AddRazorPages();  
    } 



European ASP.NET Core Hosting :: A Peek Into .NET 6's DateOnly And TimeOnly Structure

clock June 7, 2021 08:26 by author Peter

If there was a poll to find that one little feature every C# developer would love to have in .Net, then it is most likely the ability to store Date and Time individually. For years now, we had to use DateTime to represent Date when the Time part of the object had to be ignored. The same thing happened when we wanted to save time and had to ignore the Date of the DateTime component.
 

And with .Net 6, that long agonizing wait has come to an end. .Net 6 has now introduced DateOnly and TimeOnly Structures which could store Date and Time components.
 
DateOnly
The DateOnly structure allows you to save a Date property with a more explicit structure rather than using the DateTime and ignoring the Time part.
    var dateOnlyFirst = new DateOnly(2020, 2, 16);  
    var dateOnlySecond = DateOnly.FromDateTime(DateTime.Now);  
    Console.WriteLine(dateOnlyFirst);  
    Console.WriteLine(dateOnlySecond);  
    // Output  
    16-02-2020  
    04-06-2021  


We could create the instance either using the Constructors or static helper methods like DateOnly.FromDateTime. It is also interesting to know what happens behinds the scenes. Internally, the DateOnly structure stores the value in an Int32 field that represents the number of days since 1/1/0001. The field is designed to store values from 1 Jan,0001 to 31st Dec 9999. If you take a glimpse of code in the .Net Github source code for DateOnly, you could find some of the following,
    private readonly int _dayNumber;  
    // Maps to Jan 1st year 1  
    private const int MinDayNumber = 0;  
    // Maps to December 31 year 9999. The value calculated from "new DateTime(9999, 12, 31).Ticks / TimeSpan.TicksPerDay"  
    private const int MaxDayNumber = 3_652_058;  
    public DateOnly(int year, int month, int day) => _dayNumber = DayNumberFromDateTime(new DateTime(year, month, day));  
    public static DateOnly FromDateTime(DateTime dateTime) => new DateOnly(DayNumberFromDateTime(dateTime));  
    private static int DayNumberFromDateTime(DateTime dt) => (int)(dt.Ticks / TimeSpan.TicksPerDay);  

The DateOnly provides most of the (applicable) functionality that DateTime provides including AddDays(),AddMonths(),AddYears, ParseExact and many more.
 
TimeOnly
TimeOnly Quite similar to DateOnly, the TimeOnly structure provides a way to represent the Time component effectively in .Net.
    var timeOnlyFirst = new TimeOnly(10,10,10);  
    var timeOnlySecond = TimeOnly.FromDateTime(DateTime.Now);  
    Console.WriteLine(timeOnlyFirst);  
    Console.WriteLine(timeOnlySecond);  
    //Output  
    10:10  
    05:35  


Internally, TimeOnly uses a long to represent the ticks elapsed since midnight. Let us know to take a glimpse of the internal code of TimeOnly.
     // represent the number of ticks map to the time of the day. 1 ticks = 100-nanosecond in time measurements.  
    private readonly long _ticks;  
    // MinTimeTicks is the ticks for the midnight time 00:00:00.000 AM  
    private const long MinTimeTicks = 0;  
    // MaxTimeTicks is the max tick value for the time in the day. It is calculated using DateTime.Today.AddTicks(-1).TimeOfDay.Ticks.  
    private const long MaxTimeTicks = 863_999_999_999;  
    public TimeOnly(int hour, int minute, int second) : this(DateTime.TimeToTicks(hour, minute, second, 0)) {}  
    public TimeOnly(long ticks)  
       {  
          if ((ulong)ticks > MaxTimeTicks)  
          {  
             throw new ArgumentOutOfRangeException(nameof(ticks), SR.ArgumentOutOfRange_TimeOnlyBadTicks);  
          }  
       _ticks = ticks;  
    }  

As with the DateOnly structure, the TimeOnly too comes in with a lot of helper methods that would aid the developer.
 
It is quite safe to assume that a lot of developers would be quite happy with the introduction of these structures. These would definitely improve the code base a lot and make the representations more explicit, rather than using DateTime and ignoring a part of it.



European ASP.NET Core Hosting :: What Is AutoMapper In ASP.NET Core ?

clock June 2, 2021 08:14 by author Peter

In this article, we are going to see what is AutoMapper in .NET core, what problems it solves, how to use AutoMapper, AutoMapper ForMember method, AutoMapper ReverseMap method, and Conditional Mappings.

What is AutoMapper?
AutoMapper is a simple C# library that transforms one object type to another object type, which means, it’s a mapper between two objects. AutoMapper is the convention-based object to object mapper. It maps the properties of two different objects by transforming the input object of one type to the output object of another type.
 
How to add AutoMapper?
The first step is to install the corresponding NuGet package in the Package Manager console, using the command “Install-Package Automapper.Extensions.Microsoft.DependencyInjection”. This command will install all AutoMapper Packages.
 
The next step configures the AutoMapper services into our application. Open startup.cs class file, add “services.AddAutoMapper(typeof(Startup))” in configure services method.
 
Now the AutoMapper Package was installed and configured in our project.
 
How to use Automapper?
Let's take a new user model class, this class will have several properties.
    Public class User {  
        Public int Id {  
            get;  
            set;  
        }  
        Public string FirstName {  
            get;  
            set;  
        }  
        Public string LastName {  
            get;  
            set;  
        }  
        Public string Email {  
            get;  
            set;  
        }  
        Public string Address {  
            get;  
            set;  
        }  
        Public int Age {  
            get;  
            set;  
        }  
    }  


Let’s create a new user view model class, and display the user information,
    Public class UserViewModel {  
        Public string FirstName {  
            get;  
            set;  
        }  
        Public string LastName {  
            get;  
            set;  
        }  
        Public string Email {  
            get;  
            set;  
        }  
    }  


Now let’s see how to convert our domain object to a view model. By using the concept called Profiles, we can organize our mapping configurations.
 
Let’s create a new user profile class, in this class, we can create the mapping configuration inside the constructor.
    Public class UserProfile: Profile // this class inherits from AutoMapper profile class  
    {  
        CreateMap < User, UserViewModel > ();  
    }  


So, basically, we create the mapping from our User domain object to the UserViewModel. That’s it.
 
As soon as our application starts AutoMapper service will scan the application and look for classes that inherit from the profile class and load their mapping configurations.
 
Now, let’s create new controller with the name UserController,
    Public class UserController: controller {  
        Private readonly IMapper _mapper;  
        Public UserController(IMapper mapper) {  
            _mapper = mapper;  
        }  
        Public IActionResult Index() {  
            var userInfo = GetUserInfo();  
            var userViewModel = _mapper.map < UserViewModel > (userInfo);  
            return View(userViewModel);  
        }  
        Private static User GetUserInfo() {  
            Return new User {  
                Id = 1,  
                    FirstName = “John”,  
                    LastName = “Smith”,  
                    Email = “john.smith @gmail.com”  
            }  
        }  
    }  


Now if we run the application (localhost:5001/user), we can see the below result,
 

That’s all, our AutoMapper was successfully converting from the User domain model object to the UserViewModel object. Very simple isn’t it?
 
But if we have different property names in our source object and destination object ??
 
Here comes the ForMember Method concept.
 
Let’s modify the property names in the above userviewmodel class,
    Public class UserViewModel {  
        Public string FName {  
            get;  
            set;  
        } // previously FirstName  
        Public string LName {  
            get;  
            set;  
        } // previously LastName  
        Public string Email {  
            get;  
            set;  
        }  
    }  


So, now we must map user FirstName to FName and user LastName to LName, to make this work we have to change the mapping in the UserProfile class file.
    Public class UserProfile : Profile // this class inherits from AutoMapper profile class  
    {  
    CreateMap<User, UserViewModel>()  
        .ForMember(dest =>  
            dest.Fname,  
            opt => opt.MapFrom(src => src.FirstName))  
        .ForMember(dest =>  
            dest.Lname,  
            opt => opt.MapFrom(src => src.LastName));  
    }  


Above, we customized the configuration for individual members, we can use the ForMember method which has the destination member parameter of type expression and member options parameter of type action.
 
So far we have only looked at one-directional mapping, which means if we have two types, typeA, and typeB then we only map type A to type B.
 
But by using automatic Reverse mapping (using ReverseMap method), it’s possible to achieve Bi-Directional mapping.

    Public class UserProfile : Profile // this class inherits from AutoMapper profile class  
    {  
    CreateMap<User, UserViewModel>()  
        .ForMember(dest =>  
            dest.Fname,  
            opt => opt.MapFrom(src => src.FirstName))  
        .ForMember(dest =>  
            dest.Lname,  
            opt => opt.MapFrom(src => src.LastName))  
        .ReverseMap();  
    }  

Once the reverse map is configured, we can map back from destination to source type.
 
UserController.cs
    Public IActionResult Index() {  
        var userInfo = GetUserInfo();  
        var userViewModel = _mapper.map < UserViewModel > (userInfo);  
        var userViewModelReverse = _mapper.map < Userl > (userViewModel); // Bi-directional Mapping  
        return View(userViewModel);  
    }  


AutoMapper allows you to add Conditional Mapping to properties that must be met before that property will be mapped.
    Public class UserProfile : Profile // this class inherits from AutoMapper profile class  
    {  
    CreateMap<User, UserViewModel>()  
        .ForMember(dest =>  
            dest.Fname,  
            opt => opt.MapFrom(src => src.FirstName))  
        .ForMember(dest =>  
            dest.Lname,  
            opt => opt.MapFrom(src => src.LastName))  
        .ForMember(dest =>  
            dest.IsAdult,  
            opt => opt.Condition (src => src.Age > 18 ? true:false))  
        .ReverseMap();  
    }  


In the above example, the IsAdult property value is based on the condition of Age > 18.
In this article, we have seen what is AutoMapper, how to install and Configure, AutoMapper ForMember method, AutoMapper ReverseMap method, and Conditional Mappings.
Also, AutoMapper provides so many other features to simplify complex mappings. You can check the complete AutoMapper documentation. I hope this article will help you to understand the AutoMapper in ASP.NET Core.



European ASP.NET Core Hosting :: Authorization In Razor Pages ASP.NET Core

clock May 31, 2021 07:03 by author Peter

Authorization is a process that determines what a user is able to do. For example, an Admin user is allowed to install/remove a software from a computer and a non-Admin user can use the software from the computer. It is independent and orthogonal from authentication. However, authorization requires an authentication mechanism. For applications, the first step is always authentication and then authorization.

Using AuthorizeFilter, we can control the access in our MVC / Web API application by specifying this attribute in controller or action method. Similarly, there is a way to control access in our Razor pages, i.e., to use authorization conventions at startup. These conventions allow us to authorize users to access individual pages or folders of the pages. In the same way, these conventions allow anonymous users to access individual pages or folders of pages.
 
To demonstrate the concept, I have created an application with ASP.NET Core Identity. For guidance on how to create an application with ASP.NET Core Identity, please refer to my article "Overview Of Identity In ASP.NET Core 2.0" .
 
Using AddRazorPagesOptions, we can add an AuthorizeFilter to the page at the specified path. With AddRazorPagesOptions, we have a couple of methods that can be used to authorize or allow anonymous access to the page or folder.
 
AuthorizePage
This adds an AuthorizeFilter to the specified page. There are two extension methods: one is "to accept the page name" and other one is " to accept page name and authorization policy".
    public static PageConventionCollection AuthorizePage(this PageConventionCollection conventions, string pageName)  
    public static PageConventionCollection AuthorizePage(this PageConventionCollection conventions, string pageName, string policy)  


AuthorizeFolder
This adds AuthorizeFilter to all pages under a specified folder. There are two extension methods - accept the folder path, and accept the folder path and authorization policy.
    public static PageConventionCollection AuthorizeFolder(this PageConventionCollection conventions, string folderPath)  
    public static PageConventionCollection AuthorizeFolder(this PageConventionCollection conventions, string folderPath, string policy)  


AllowAnonymousToPage
This adds AllowAnonymousFilter to the specified page.
    public static PageConventionCollection AllowAnonymousToPage(this PageConventionCollection conventions, string pageName)  

AllowAnonymousToFolder
This adds AllowAnonymousFilter to all the pages under the specified folder.
    public static PageConventionCollection AllowAnonymousToFolder(this PageConventionCollection conventions, string folderPath)  

AuthorizeAreaPage
This is the same as AuthorizePage method. It adds AuthorizeFilter to the specified page under the specified Area. It also has an extension method that accepts the authorization policy.
    public static PageConventionCollection AuthorizeAreaPage(this PageConventionCollection conventions, string areaName, string pageName)  
    public static PageConventionCollection AuthorizeAreaPage(this PageConventionCollection conventions, string areaName, string pageName, string policy)  

AuthorizeAreaFolder
This is the same as the AuthorizeFolder method. It adds AuthorizeFilter to the all the pages under the specified folder under the specified Area. It also has extension method that accepts the authorization policy.
    public static PageConventionCollection AuthorizeAreaFolder(this PageConventionCollection conventions, string areaName, string folderPath)  
    public static PageConventionCollection AuthorizeAreaFolder(this PageConventionCollection conventions, string areaName, string folderPath, string policy)  

AllowAnonymousToAreaPage

This adds AllowAnonymousFilter to the specified page that is located under the specified area.
    public static PageConventionCollection AllowAnonymousToAreaPage(this PageConventionCollection conventions, string areaName, string pageName)  

AllowAnonymousToAreaFolder

This adds AllowAnonymousFilter to all the pages that are located under the specified area folder.
    public static PageConventionCollection AllowAnonymousToAreaFolder(this PageConventionCollection conventions, string areaName, string folderPath)  

Example
To demonstrate the concept, I have created a few pages and the following snippet shows the folder structure of defined authorization in the project.


    public void ConfigureServices(IServiceCollection services)  
    {  
        ....  
        ....  
        services.AddMvc().AddRazorPagesOptions(options =>  
        {  
            options.Conventions.AuthorizePage("/Page3");  
            options.Conventions.AuthorizeFolder("/Public");  
            options.Conventions.AllowAnonymousToPage("/page4");  
            options.Conventions.AllowAnonymousToFolder("Private");  
      
            options.Conventions.AuthorizeAreaPage("MyFeature", "/AuthPage1");  
            options.Conventions.AllowAnonymousToAreaPage("MyFeature", "/AuthPage2");  
        })  
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);  
    }  


The authorization conventions can be also applied to Razor class Library.

    public void ConfigureServices(IServiceCollection services)  
    {  
        ....  
        ....  
        services.AddMvc().AddRazorPagesOptions(options =>  
        {  
            ....  
            ....  
            //This page defined in Razor Class Library project  
            options.Conventions.AuthorizeAreaPage("MyFeature", "/Page1");  
            ....  
            ....  
        })  
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);  
    }  

Combining authorized and anonymous access
 
We can also specify authorization for the folder and within this folder it allows anonymous access.

    services.AddMvc()  
        .AddRazorPagesOptions(options =>  
        {  
            ....  
            options.Conventions.AuthorizeFolder("/Private").AllowAnonymousToPage("/Private/page8");  
            ....  
        }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);  


The reverse case is not allowed; i.e., we cannot define folder for anonymous access and specify a page within it for authorization. In this case, AllowAnonymousFilter is always applied and AuthorizeFilter is ignored.

    .AllowAnonymousToFolder("/Private").AuthorizePage("/Private/page8");  

Summary
 
Using this authorization convention, we can apply authorization on Razor pages. This convention can also be applied to Razor class Library (RCL) project if we use it in our project. In the case of RCL, the conventions are defined in the project that used RCL.



European ASP.NET Core Hosting :: Email Send Using ASP.NET With C#

clock May 25, 2021 07:10 by author Peter

In this article, you will learn how to send email using ASP.NET C# through the various SMTP servers. In this article, I also explained the way of sending emails from the HostForLIFE server.

 

 
Below is the step to send an email using ASP.NET C#,
 
Step 1
Create a new website.
 
Step 2
Create a webform aspx page.
 
Step 3
Add CSS file as attached in source code.
 
Step 4
Add reference of AjaxToolKit as attached in source code.
 
Step 5
Design the webpage like below,
    <!DOCTYPE html>  
    <html  
        xmlns="http://www.w3.org/1999/xhtml">  
        <head id="Head1" runat="server">  
            <title>Email Send Sample</title>  
            <link href="bootstrap.min.css" rel="stylesheet" />  
        </head>  
        <body>  
            <form id="form1" runat="server">  
                <div class="container">  
                    <div class="row">  
                        <div class="col-md-4 col-md-offset-4">  
                            <div class="panel panel-primary">  
                                <div class="panel-heading">  
                                    <h3 class="panel-title">Send Email Sample</h3>  
                                </div>  
                                <div class="panel-body">  
                                    <label>Name</label>  
                                    <asp:TextBox ID="txtname" runat="server" CssClass="form-control" placeholder="Name"></asp:TextBox>  
                                    <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ErrorMessage="Please Provide Name" ControlToValidate="txtname" ForeColor="Red"></asp:RequiredFieldValidator>  
                                    <br />  
                                    <label>Subject</label>  
                                    <asp:TextBox ID="txtbody" runat="server" CssClass="form-control" placeholder="Subject"></asp:TextBox>  
                                    <asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ErrorMessage="Please Provide Subject" ControlToValidate="txtbody" ForeColor="Red"></asp:RequiredFieldValidator>  
                                    <br />  
                                    <label>Email([email protected]</label>  
                                    <asp:TextBox ID="txtemail" runat="server" CssClass="form-control" placeholder="Email"></asp:TextBox>  
                                    <asp:RequiredFieldValidator ID="RequiredFieldValidator4" runat="server" ErrorMessage="Please Provide Email-ID" ControlToValidate="txtemail" ForeColor="Red"></asp:RequiredFieldValidator>  
                                    <br />  
                                    <asp:RegularExpressionValidator ID="RegularExpressionValidator1" runat="server" ErrorMessage="Please Proide Valid Email-ID" ControlToValidate="txtemail" ForeColor="Red" ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"></asp:RegularExpressionValidator>  
                                    <br />  
                                    <asp:Button ID="Button1" runat="server" Text="Send Email" CssClass="btn btn-block btn-primary" OnClick="Button1_Click" />  
                                    <asp:Button ID="Button2" runat="server" Text="Send Email from HostForLIFE" CssClass="btn btn-block btn-primary" OnClick="Button2_Click" />  
                                    <asp:Label ID="lblmsg" runat="server" Text=""></asp:Label>  
                                </div>  
                            </div>  
                        </div>  
                    </div>  
                </div>  
            </form>  
        </body>  
    </html>  


Step 6
Add Server-side validation for every field as name validation, email validation as written in the above webpage source code.
 
Step 7
Add Server-side RegularExpressionValidator to valid the correct email id.
 
Step 8
Added below namespace in .aspx.cs page,
    using System.Net;  
    using System.Net.Mail;  

Step 9
Add the below source code on button click event of the webpage.
 
Send email from local server,
    string to = txtemail.Text; //To address  
    string from = "[email protected]"; //From address  
    MailMessage message = new MailMessage(from, to);  
    string mailbody = message.Subject = "This article will help you on how to send email using asp.net c# code";  
    message.Body = mailbody;  
    message.IsBodyHtml = false;  
    SmtpClient client = new SmtpClient();  
    client.Host = "smtp.gmail.com";  
    client.Port = 587;  
    System.Net.NetworkCredential basicCredential1 = new  
    System.Net.NetworkCredential("[email protected]", "password");  
    client.EnableSsl = false;  
    client.UseDefaultCredentials = false;  
    // client.Credentials = basicCredential1;  
    try {  
        client.Send(message);  
        lblmsg.Text = "Email Send Successfully";  
    } catch (Exception ex) {}  

Send email from HostForLIFE Server,
    string to = txtemail.Text; //To address  
    string from = "[email protected]"; //From address  
    MailMessage message = new MailMessage(from, to);  
    string mailbody = "This article will help you on how to send email using asp.net c# code";  
    message.Subject = txtbody.Text;  
    message.Body = mailbody;  
    message.IsBodyHtml = false;  
    SmtpClient client = new SmtpClient();  
    client.Host = "relay-hosting.secureserver.net"; //This is code SMTP Host server name  
    client.Port = 25; //This is default port  
    client.EnableSsl = false;  
    client.UseDefaultCredentials = false;  
    try {  
        client.Send(message);  
        lblmsg.Text = "Email Send Successfully";  
    } catch (Exception ex) {}  

Step 10
Run the application.
If you get the below error then follow step 11.
 
WebForms UnobtrusiveValidationMode requires a ScriptResourceMapping for 'jquery'. Please add a ScriptResourceMapping named jquery(case-sensitive).
 
Step 11
Add the below code in web.config,
    <appSettings>  
       <add key="ValidationSettings:UnobtrusiveValidationMode" value="None" />  
    </appSettings>  


Step 12
Run the website and you will get the below screen. Fill in all the details and click on the button Send Email for Local server email like Gmail and Click on Send Email from HostForLIFE to send email from HostForLIFE server that will not work from local work only on the HostForLIFE server.

Step 13
Once you click on the button after filling in the data you get the message "Email Send Successfully".
 
Some of SMTP Class Properties,
    Host
    SMTP Server( Here you need to give the SMTP server name from where the email going to send).

    Enable SSL
    Check your host accepted SSL Connections Value is either True or False.

    Port
    SMTP Server Port (Here you need to give SMTP server port from where the email going to send).

    Credentials
    SMTP servers login credentials such as Email and Password.

    UseDefaultCredentials
    When we set to True in UseDefaultCredentials then that specifies to allow authentication based on the credentials of the account used to send emails.

In this article, we learned how to send email using ASP.NET C# through the SMTP server. I hope this article is useful for those who want to send email using ASP.NET C#.



European ASP.NET Core Hosting :: Elegant API Versioning In ASP.NET Core

clock May 17, 2021 07:37 by author Peter

In this tutorial, we are going to learn how to develop simple Asp Net Core 3.1 Web API and API Versioning techniques such as URL-based, header-based, and Query string-based versioning.

 
What Is Web API Versioning? And Why Do We Need It?
Assume that we have an API available in the production box and a few clients are already consuming it. Post-production deployment, we should be very careful with any new changes. These changes should not impact the existing customer applications that are already consuming our API. It is not a good practice to advise clients to change the API calls in each of their applications where our API has been integrated. In order to address such issues, API versioning came into the picture.
 
API versions ensure that the integrity and availability of data for our existing client applications.
 
To Begin with,
 
Step 1

Create a simple ASP.NET Core 3.1 API Project.
 
Step 2
Add a model ProductResponse.cs
    namespace AspNetCoreVersioning.Contracts {  
        public class ProductResponse {  
            public Guid Id {  
                get;  
                set;  
            }  
            public string Name {  
                get;  
                set;  
            }  
        }  
    }  

Step 3
Add a Controller ProductsController.cs
    namespace AspNetCoreVersioning.Controllers {  
        [ApiController]  
        [Route("api/products")]  
        public class ProductsController: ControllerBase {  
            [HttpGet("{productId}")]  
            public IActionResult GetProduct([FromRoute] Guid productId) {  
                var product = new ProductResponse {  
                    Id = productId,  
                        Name = "Sanitizer"  
                };  
                return Ok(product);  
            }  
        }  
    }  


Let us test this API, According to your environment, the port number will vary.
https://localhost:12345/api/products/00000000-0000-0000-0000-000000000000
 
Result
    {"id":"00000000-0000-0000-0000-000000000000","name":"Sanitizer"}   

We have received the expected result.
 
Assume that we need to make a breaking change on our API, but we don’t want to break anything in our existing API which our customers are already using. How can we achieve this? Let us go and resolve a NuGet package Microsoft.AspNetCore.Mvc.Versioning.
 
Use the below command from the package manager console,
 
Install-Package Microsoft.AspNetCore.Mvc.Versioning
 
Once the package installation complete, let us configure the API to support versioning. This package basically has everything that we need.
Without any delay, let us go to class Startup.cs, where we are going to update the method ConfigurationServices as below
    public void ConfigureServices(IServiceCollection services) {  
        services.AddControllers();  
        services.AddApiVersioning();  
    }  


By adding these services.AddApiVersioning() method, we have some sort of versioning. However, we haven’t really told ASP.NETCore how we versioning the API. Without making any additional changes, let us run the API, we will get the below error message
    {"error":{"code":"ApiVersionUnspecified","message":"An API version is required, but was not specified.","innerError":null}}   

This is because we haven’t configured our API versioning to a default version. For us the first version apparently V1 or V1.0. The default way of this works in ASP.NET Core is to have the “API-version” query string parameter as below.
https://localhost:12345/api/products/00000000-0000-0000-0000-000000000000?api-version=1
 
Result will as below,
    {"id":"00000000-0000-0000-0000-000000000000","name":"Sanitizer"}   

This would work fine. However, this is not ideal because if we make this “api-version” query string is mandatory, many of our consumers’ code break. What we want to do instead is we want to enable the API versioning default to a version. How we can achieve this? Let us to the Startup.cs class again and make the below changes.
    public void ConfigureServices(IServiceCollection services) {  
        services.AddControllers();  
        services.AddApiVersioning(options => {  
            options.AssumeDefaultVersionWhenUnspecified = true;  
        });  
    }  

Let us execute the API again,
https://localhost:12345/api/products/00000000-0000-0000-0000-000000000000

    Result: {"id":"00000000-0000-0000-0000-000000000000","name":"Sanitizer"}   


Assume that, if we want to have a different version instead of V1.0 or V1. How can we achieve this?
 
First, we will make the below changes in Startup.cs class as below,
    public void ConfigureServices(IServiceCollection services) {  
        services.AddControllers();  
        services.AddApiVersioning(options => {  
            options.AssumeDefaultVersionWhenUnspecified = true;  
            options.DefaultApiVersion = ApiVersion.Default;  
        });  
    }  


Then make the below two changes.
 
Step 1
Add the below models in ProductResponse.cs Class,
    namespace AspNetCoreVersioning.Contracts {  
        public class ProductResponseV1 {  
            public Guid Id {  
                get;  
                set;  
            }  
            public string Name {  
                get;  
                set;  
            }  
        }  
        public class ProductResponseV2 {  
            public Guid Id {  
                get;  
                set;  
            }  
            public string ProductName {  
                get;  
                set;  
            }  
        }  
    }  

Step 2
Make the below changes into ProductsController.cs Class
    namespace AspNetCoreVersioning.Controllers {  
            [ApiController]  
            [Route("api/products")]  
            public class ProductsController: ControllerBase {  
                [HttpGet("{productId}")]  
                public IActionResult GetProductV1([FromRoute] Guid productId) {  
                        var product = new ProductResponseV1 {  
                            Id = productId,  
                                Name = "Sanitizer"  
                        };  
                        return Ok(product);  
                    }  
                    [HttpGet("{productId}")]  
                public IActionResult GetProductV2([FromRoute] Guid productId) {  
                    var product = new ProductResponseV2 {  
                        Id = productId,  
                            ProductName = "Sanitizer"  
                    };  
                    return Ok(product);  
                }  
            }  

Now Run the API. You will get the below Exception.
 
An unhandled exception occurred while processing the request.
 
AmbiguousMatchException: The request matched multiple endpoints. Matches,
    AspNetCoreVersioning.Controllers.ProductsController.GetProductV2 (AspNetCoreVersioning)  
    AspNetCoreVersioning.Controllers.ProductsController.GetProductV1 (AspNetCoreVersioning)  
    Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ReportAmbiguity(CandidateState[] candidateState)
 

In order to resolve this error message, we need to make some configuration changes at ProductsController.cs Class as below,
    namespace AspNetCoreVersioning.Controllers {  
        [ApiController]  
        [Route("api/products")]  
        [ApiVersion("1.0")]  
        [ApiVersion("2.0")]  
        public class ProductsController: ControllerBase {  
            [HttpGet("{productId}")]  
            public IActionResult GetProductV1([FromRoute] Guid productId) {  
                    var product = new ProductResponseV1 {  
                        Id = productId,  
                            Name = "Sanitizer"  
                    };  
                    return Ok(product);  
                }  
                [HttpGet("{productId}")]  
                [MapToApiVersion("2.0")]  
            public IActionResult GetProductV2([FromRoute] Guid productId) {  
                var product = new ProductResponseV2 {  
                    Id = productId,  
                        ProductName = "Sanitizer"  
                };  
                return Ok(product);  
            }  
        }  
    }  


Now Run the API as below,
https://localhost:12345/api/products/00000000-0000-0000-0000-000000000000?api-version=1

    Result: {"id":"00000000-0000-0000-0000-000000000000","name":"Sanitizer"}   

https://localhost:12345/api/products/00000000-0000-0000-0000-000000000000?api-version=2

    Result: {"id":"00000000-0000-0000-0000-000000000000","productName":"Sanitizer"}   

I will be covering URL based and Header based versioning in the upcoming sessions. Thanks for reading my article. I appreciate your feedback in the comment section below.



European ASP.NET Core Hosting :: JWT Authentication In ASP.NET Core

clock May 3, 2021 07:00 by author Peter

JWT in ASP.NET Core
JWT (JSON web token) has become more and more popular in web development. It is an open standard which allows transmitting data between parties as a JSON object in a secure and compact way. The data transmitting using JWT between parties are digitally signed so that it can be easily verified and trusted.

In this article, we will learn how to setup JWT with ASP.NET core web application. We can create an application using Visual Studio or using CLI (Command Line Interface).

    dotnet new webapi -n JWTAuthentication   

Above command will create an ASP.NET Web API project with the name "JWTAuthentication" in the current folder.
 
The first step is to configure JWT based authentication in our project. To do this, we need to register a JWT authentication schema by using "AddAuthentication" method and specifying JwtBearerDefaults.AuthenticationScheme. Here, we configure the authentication schema with JWT bearer options.
    public void ConfigureServices(IServiceCollection services)    
    {    
        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)    
        .AddJwtBearer(options =>    
        {    
            options.TokenValidationParameters = new TokenValidationParameters    
            {    
                ValidateIssuer = true,    
                ValidateAudience = true,    
                ValidateLifetime = true,    
                ValidateIssuerSigningKey = true,    
                ValidIssuer = Configuration["Jwt:Issuer"],    
                ValidAudience = Configuration["Jwt:Issuer"],    
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))    
            };    
        });    
        services.AddMvc();    
    }   


In this example, we have specified which parameters must be taken into account to consider JWT as valid. As per our code,  the following items consider a token valid:

    Validate the server (ValidateIssuer = true) that generates the token.
    Validate the recipient of the token is authorized to receive (ValidateAudience = true)
    Check if the token is not expired and the signing key of the issuer is valid (ValidateLifetime = true)
    Validate signature of the token (ValidateIssuerSigningKey = true)
    Additionally, we specify the values for the issuer, audience, signing key. In this example, I have stored these values in appsettings.json file.

AppSetting.Json

    {    
      "Jwt": {    
        "Key": "ThisismySecretKey",    
        "Issuer": "Test.com"    
      }    
    }   

The above-mentioned steps are used to configure a JWT based authentication service. The next step is to make the authentication service is available to the application. To do this, we need to call app.UseAuthentication() method in the Configure method of startup class. The UseAuthentication method is called before UseMvc method.

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)    
    {    
        app.UseAuthentication();    
        app.UseMvc();    
    }

Generate JSON Web Token
I have created a LoginController and Login method within this controller, which is responsible to generate the JWT. I have marked this method with the AllowAnonymous attribute to bypass the authentication. This method expects the Usermodel object for Username and Password.
 
I have created the "AuthenticateUser" method, which is responsible to validate the user credential and returns to the UserModel. For demo purposes, I have returned the hardcode model if the username is "Peter". If the "AuthenticateUser" method returns the user model, API generates the new token by using the "GenerateJSONWebToken" method.
 
Here, I have created a JWT using the JwtSecurityToken class. I have created an object of this class by passing some parameters to the constructor such as issuer, audience, expiration, and signature.
 
Finally, JwtSecurityTokenHandler.WriteToken method is used to generate the JWT. This method expects an object of the JwtSecurityToken class.
    using Microsoft.AspNetCore.Authorization;    
    using Microsoft.AspNetCore.Mvc;    
    using Microsoft.Extensions.Configuration;    
    using Microsoft.IdentityModel.Tokens;    
    using System;    
    using System.IdentityModel.Tokens.Jwt;    
    using System.Security.Claims;    
    using System.Text;    
        
    namespace JWTAuthentication.Controllers    
    {    
        [Route("api/[controller]")]    
        [ApiController]    
        public class LoginController : Controller    
        {    
            private IConfiguration _config;    
        
            public LoginController(IConfiguration config)    
            {    
                _config = config;    
            }    
            [AllowAnonymous]    
            [HttpPost]    
            public IActionResult Login([FromBody]UserModel login)    
            {    
                IActionResult response = Unauthorized();    
                var user = AuthenticateUser(login);    
        
                if (user != null)    
                {    
                    var tokenString = GenerateJSONWebToken(user);    
                    response = Ok(new { token = tokenString });    
                }    
        
                return response;    
            }    
        
            private string GenerateJSONWebToken(UserModel userInfo)    
            {    
                var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));    
                var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);    
        
                var token = new JwtSecurityToken(_config["Jwt:Issuer"],    
                  _config["Jwt:Issuer"],    
                  null,    
                  expires: DateTime.Now.AddMinutes(120),    
                  signingCredentials: credentials);    
        
                return new JwtSecurityTokenHandler().WriteToken(token);    
            }    
        
            private UserModel AuthenticateUser(UserModel login)    
            {    
                UserModel user = null;    
        
                //Validate the User Credentials    
                //Demo Purpose, I have Passed HardCoded User Information    
                if (login.Username == "Peter")    
                {    
                    user = new UserModel { Username = "Peter", EmailAddress = "[email protected]" };    
                }    
                return user;    
            }    
        }    
    }   


Once, we have enabled the JWT based authentication, I have created a simple Web API method that returns a list of value strings when invoked with an HTTP GET request. Here, I have marked this method with the authorize attribute, so that this endpoint will trigger the validation check of the token passed with an HTTP request.
 
If we call this method without a token, we will get 401 (UnAuthorizedAccess) HTTP status code as a response. If we want to bypass the authentication for any method, we can mark that method with the AllowAnonymous attribute.
 
To test the created Web API, I am Using Fiddler. First, I have requested to "API/login" method to generate the token. I have passed the following JSON in the request body.
    {"username": "Peter", "password": "password"}

As a response, we will get the JSON like the following,
    {    
        "token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJKaWduZXNoIFRyaXZlZGkiLCJlbWFpbCI6InRlc3QuYnRlc3RAZ21haWwuY29tIiwiRGF0ZU9mSm9pbmciOiIwMDAxLTAxLTAxIiwianRpIjoiYzJkNTZjNzQtZTc3Yy00ZmUxLTgyYzAtMzlhYjhmNzFmYzUzIiwiZXhwIjoxNTMyMzU2NjY5LCJpc3MiOiJUZXN0LmNvbSIsImF1ZCI6IlRlc3QuY29tIn0.8hwQ3H9V8mdNYrFZSjbCpWSyR1CNyDYHcGf6GqqCGnY"    
    }  

Now, we will try to get the list of values by passing this token into the authentication HTTP header. Following is my Action method definition.
    [HttpGet]    
    [Authorize]    
    public ActionResult<IEnumerable<string>> Get()    
    {    
        return new string[] { "value1", "value2", "value3", "value4", "value5" };    
    }  

    Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJKaWduZXNoIFRyaXZlZGkiLCJlbWFpbCI6InRlc3QuYnRlc3RAZ21haWwuY29tIiwiRGF0ZU9mSm9pbmciOiIwMDAxLTAxLTAxIiwianRpIjoiYzJkNTZjNzQtZTc3Yy00ZmUxLTgyYzAtMzlhYjhmNzFmYzUzIiwiZXhwIjoxNTMyMzU2NjY5LCJpc3MiOiJUZXN0LmNvbSIsImF1ZCI6IlRlc3QuY29tIn0.8hwQ3H9V8mdNYrFZSjbCpWSyR1CNyDYHcGf6GqqCGnY 

Handle Claims with JWT
Claims are data contained by the token. They are information about the user which helps us to authorize access to a resource. They could be Username, email address, role, or any other information. We can add claims information to the JWT so that they are available when checking for authorization.
 
In the above example, if we want to pass the claims to our token then the claim information needs to add GenerateJSONWebToken method of Login controller. In the following example, I have added a username, email address, and date of joining as claimed into the token.

    private string GenerateJSONWebToken(UserModel userInfo)    
    {    
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));    
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);    
        
        var claims = new[] {    
            new Claim(JwtRegisteredClaimNames.Sub, userInfo.Username),    
            new Claim(JwtRegisteredClaimNames.Email, userInfo.EmailAddress),    
            new Claim("DateOfJoing", userInfo.DateOfJoing.ToString("yyyy-MM-dd")),    
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())    
        };    
        
        var token = new JwtSecurityToken(_config["Jwt:Issuer"],    
            _config["Jwt:Issuer"],    
            claims,    
            expires: DateTime.Now.AddMinutes(120),    
            signingCredentials: credentials);    
        
        return new JwtSecurityTokenHandler().WriteToken(token);    
    }   

The claims are an array of key-value pair. The keys may be values of a JwtRegisteredClaimNames structure (it provides names for public standardized claims) or custom name (such as DateOfJoining in above example).
 
This claims can be used to filter the data. In the following example, I have to change the list of values if the user spends more than 5 years with the company.
    [HttpGet]    
    [Authorize]    
    public ActionResult<IEnumerable<string>> Get()    
    {    
        var currentUser = HttpContext.User;    
        int spendingTimeWithCompany = 0;    
        
        if (currentUser.HasClaim(c => c.Type == "DateOfJoing"))    
        {    
            DateTime date = DateTime.Parse(currentUser.Claims.FirstOrDefault(c => c.Type == "DateOfJoing").Value);    
            spendingTimeWithCompany = DateTime.Today.Year - date.Year;    
        }    
        
        if(spendingTimeWithCompany > 5)    
        {    
            return new string[] { "High Time1", "High Time2", "High Time3", "High Time4", "High Time5" };    
        }    
        else    
        {    
            return new string[] { "value1", "value2", "value3", "value4", "value5" };    
        }    
    }   

JWT is very famous in web development. It is an open standard that allows transmitting data between parties as a JSON object in a secure and compact way. In this article, we will learn how to generate and use JWT with ASP.NET core application.



ASP.NET Core 5.0.2 Hosting - HostForLIFE :: InProcess Hosting Model In ASP.NET Core

clock April 26, 2021 08:23 by author Peter

In this article, we will learn about one of the AspNetCoreHostingModel i.e. InProcess Hosting Model. In ASP.NET Core, there are two types of AspNetCoreHostingModel, one is InProcess and another is OutOfProcess hosting model. In InProcess hosting model, the ASP.NET Core application is hosted inside of the IIS Worker Process i.e. w3wp.exe. In OutOfProcess hosting model, Web Requests are forwarded to the ASP.NET Core app running on the Kestrel Server. In this article, we are covering the InProcess hosting model. InProcess hosting model provides better performance over OutOfProcess hosting because the request is not proxied over the loopback adapter in InProcess hosting model.

 
Understanding the general architecture for the InProcess hosting model
As you can see in the above image, A request came from the user to IIS through the internet over HTTP or HTTPS protocol. ASP.NET Core Module receives the request which is passed to IIS HTTP Server. After IIS HTTP Server, the request is sent to the ASP.NET Core application’s middleware pipeline. Middleware handles the request and passes it to the HttpContext instance to the application’s logic. Then application response is passed to IIS through the IIS HTTP Server. Further, IIS sends the response back to the user who initiated the request.
 
Let’s do the hands-on in order to understand the hosting model
 
I am using the same application that we have created in the previous article of this series which was basically an Empty ASP.NET Core Application. Click on Startup.cs class.
 

Change the code in Configure method as highlighted in the below image which is for finding the ProcessName on which application is running.

Now right click on the project in order to view the properties.


Click on Debug tab in order to see the Hosting Model for the selected launch profile. We can change the hosting model from here (Dropdown contains three values that are Default (InProcess), InProcess, OutOfProcess) as well as by editing the .csproj file.

Right-click on the project in order to edit the .csproj file.

In order to configure the application for InProcess hosting, set the value of AspNetCoreHostingModel property to InProcess as shown below.

Now run the application on the IIS Express profile, application is run on the iisexpress worker process. As we are using InProcess hosting model for the application and in InProcess hosting model, the ASP.NET Core application is hosted inside of the IIS Worker Process i.e. w3wp.exe in case the application is hosted on IIS or IISExpress (if the application is launched through the IIS Express).

Go to Developer tools by clicking on Inspect element, then click on the Network tab in order to see the request and response header details. In the response header, it's clearly visible that the server which is sending the response is Microsoft-IIS.


Now let’s run the application through the Profile i.e. FirstCoreApplication (this can be as per your project) which will host the application on the Kestrel server.

Go to Developer tools by clicking on Inspect element, click on the Network tab in order to see the request and response header details. In the response header, it's clearly visible that the server is Kestrel. On running the application through the DotNet CLI, the application does not follow the InProcess hosting model, as Worker Process is dotnet.exe. dotnet.exe is the process which runs and host application with the kestrel server.

I hope this article will help you in understanding InProcess hosting model. In the next article, we will understand the OutOfProcess hosting model.



ASP.NET Core 5.0.2 Hosting - HostForLIFE :: .NET Batch Processing With Directory.EnumerateFiles

clock April 19, 2021 06:50 by author Peter

In case one wants to retrieve files from catalog Directory.GetFiles is a simple answer sufficient for most scenarios. However, when you deal with a large amount of data you might need more advanced techniques.

Example
Let’s assume you have a big data solution and you need to process a directory that contains 200000 files. For each file, you extract some basic info
public record FileProcessingDto  
{  
    public string FullPath { get; set; }  
    public long Size { get; set; }  
    public string FileNameWithoutExtension { get; set; }  
    public string Hash { get; internal set; }  
}  


Note how we conveniently use novel C# 9 record types for our DTO here.

After that, we send extracted info for further processing. Let’s emulate it with the following snippet
public class FileProcessingService  
{  
    public Task Process(IReadOnlyCollection<FileProcessingDto> files, CancellationToken cancellationToken = default)  
    {  
        files.Select(p =>  
        {  
            Console.WriteLine($"Processing {p.FileNameWithoutExtension} located at {p.FullPath} of size {p.Size} bytes");  
            return p;  
        });  
 
        return Task.Delay(TimeSpan.FromMilliseconds(20), cancellationToken);  
    }  
}  


Now the final piece is extracting info and calling the service
public class Worker  
{  
    public const string Path = @"path to 200k files";  
    private readonly FileProcessingService _processingService;  
 
    public Worker()  
    {  
        _processingService = new FileProcessingService();  
    }  
 
    private string CalculateHash(string file)  
    {  
        using (var md5Instance = MD5.Create())  
        {  
            using (var stream = File.OpenRead(file))  
            {  
                var hashResult = md5Instance.ComputeHash(stream);  
                return BitConverter.ToString(hashResult)  
                    .Replace("-", "", StringComparison.OrdinalIgnoreCase)  
                    .ToLowerInvariant();  
            }  
        }  
    }  
 
    private FileProcessingDto MapToDto(string file)  
    {  
        var fileInfo = new FileInfo(file);  
        return new FileProcessingDto()  
        {  
            FullPath = file,  
            Size = fileInfo.Length,  
            FileNameWithoutExtension = fileInfo.Name,  
            Hash = CalculateHash(file)  
        };  
    }  
 
    public Task DoWork()  
    {  
        var files = Directory.GetFiles(Path)  
            .Select(p => MapToDto(p))  
            .ToList();  
 
        return _processingService.Process(files);  
    }  
}  

Note that here we act in a naive fashion and extract all files via Directory.GetFiles(Path) in one take.

However, once you run this code via
await new Worker().DoWork()  

you’ll notice that results are far from satisfying and the application is consuming memory extensively.

Directory.EnumerateFiles to the rescue

The thing with Directory.EnumerateFiles is that it returns IEnumerable<string> thus allowing us to fetch collection items one by one. This in turn prevents us from excessive use of memory while loading huge amounts of data at once.

Still, as you may have noticed FileProcessingService.Process has delay coded in it (sort of I/O operation we emulate with simple delay). In a real-world scenario, this might be a call to an external HTTP-endpoint or work with the storage. This brings us to the conclusion that calling FileProcessingService.Process 200 000 times might be inefficient.

That’s why we’re going to load reasonable batches of data into memory at once.

The reworked code looks as follows
public class WorkerImproved  
{  
    //omitted for brevity  
 
    public async Task DoWork()  
    {  
        const int batchSize = 10000;  
        var files = Directory.EnumerateFiles(Path);  
        var count = 0;  
        var filesToProcess = new List<FileProcessingDto>(batchSize);  
 
        foreach (var file in files)  
        {  
            count++;  
            filesToProcess.Add(MapToDto(file));  
            if (count == batchSize)  
            {  
                await _processingService.Process(filesToProcess);  
                count = 0;  
                filesToProcess.Clear();  
            }  
 
        }  
        if (filesToProcess.Any())  
        {  
            await _processingService.Process(filesToProcess);  
        }  
    }  
}  

Here we enumerate collection with foreach and once we reach the size of the batch we process it and flush the collection. The only interesting moment here is to call service one last time after we exit the loop in order to flush remaining items.

Evaluation
Results produced by Benchmark.NET are pretty convincing

Few words on batch processing
In this article we took a glance at the common pattern in software engineering. Batches of reasonable amount help us to beat both I/O penalty of working in an item-by-item fashion and excessive memory consumption of loading all items in memory at once.
 
As a rule, you should strive for using batch APIs when doing I/O operations for multiple items. And once the number of items becomes high you should think about splitting these items into batches.
 
Few words on return types
Quite often when dealing with codebases I see code similar to the following
    public IEnumerable<int> Numbers => new List<int> { 1, 2, 3 };  

I would argue that this code violates Postel’s principle and the thing that follows from it is that as a consumer of a property I have can’t figure out whether I can enumerate items one by one or if they are just loaded at once in memory.
 
This is a reason I suggest being more specific about return type i.e.
    public IList<int> Numbers => new List<int> { 1, 2, 3 };  

Batching is a nice technique that allows you to handle big amounts of data gracefully. Directory.EnumerateFiles is the API that allows you to organize batch processing for the directory with a large number of files.




About HostForLIFE

HostForLIFE is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2019 Hosting, ASP.NET 5 Hosting, ASP.NET MVC 6 Hosting and SQL 2019 Hosting.


Month List

Tag cloud

Sign in