Transparent image over two controls with different back colors

I am trying to place a transparent image over two adjacent controls that have different background colors.

I am trying to place a transparent image over two adjacent controls that have different background colors.

I want the image to remain transparent, meaning the Image needs to show the backcolor of each control.

The controls are two Panels set to different background colors and the Image (PictureBox or otherwise) is placed between the two panel controls.

Public Class frmMain 
    Private Img1 As Image = Image.FromFile("C:\xxxx.png") 
Private Sub frmMain_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint 
    e.Graphics.DrawImage(Img1, 5, 5) 
End Sub 

End Class


Learn Visual Basic - Visual Basic Fundamentals for Absolute Beginners

Learn Visual Basic - Visual Basic Fundamentals for Absolute Beginners

Want to learn a different language? In this video, you'll learn the fundamentals of Visual Basic programming, how to get the tools, write code, and debug features, customizations and much more.

How to make a Windows Service from .Net Core 3.0

How to make a Windows Service from .Net Core 3.0

NET Core 3.0, it's a lot easier to create Windows Services: just a single line of code ... If the application runs on a Windows system, the method ..

In this blog post, we will create a demo Windows Service application which includes a set of features such as reading configurations, logging to files, dependency injection, file system watcher, and so on. The application will be written in .NET Core 3.0, which introduces new concepts like generic host, worker service, background service, and so on. We will go over the installation process of our Windows Service application as well.

The complete solution can be found in this GitHub repository. This application can be used as a bare-bones template for Windows Service applications or Console applications.

Why do we build Windows Service applications?

Microsoft Windows services allow us to create long-running executable applications that run in their own Windows sessions. Windows services don’t have any user interface, can be automatically started when the computer reboots, and can be paused and restarted.

Windows services are ideal for long-running functionality that does not interfere with other users who are working on the same computer. For example, in a Windows Service application, we can use a FileSystemWatcher to listen to the file system change notifications and raise events when a directory, or a file in a directory, changes. The beauty is that the Windows Service application handles all the events in background.

Practically, we usually run services in the security context of a specific user account that is different from the logged-on user or the default computer account. So a hacker cannot easily mess up the file system or the service related database through a compromised computer.

If you have created a Windows Service application in .NET framework, then you must remember the pain of debugging the Windows Service application. During those old days, the tool TopShelf helped us a little bit, but not much. Now, with the .NET Core 3.0, the experience of developing a Windows Service application is much more pleasant. In my opinion, the concept of a Windows Service is clearer as well.

In order to follow along, you need to have .NET Core 3.0 SDK installed. Also, you need to have Admin privilege in your computer or the hosting server, so that you can install the Windows Service and/or remove it.

Let’s first create a basic ASP.NET Core application and configure it to be able to be hosted in a Windows Service.

Create a bare-bones Windows Service application

We will use the worker service template from .NET Core as a starting point. If you are using Visual Studio, then you can follow the steps below:
(1) Create a new project.
(2) Select **Worker Service**. Select **Next**.
(3) Set the project name as “Demo”. Then select **Create**.
(4) In the **Create a new Worker service** dialog, select **Create**.

If you are using .NET CLI, then you can use the following command to create a solution which contains a Worker Service project.

dotnet new sln -o WindowsServiceDemo -n Demo
cd .\WindowsServiceDemo\
dotnet new worker -o Demo
dotnet sln add .\Demo\

In order to enable the worker service app to run as a Windows Service, we need to update the project a little bit by doing the following steps:

  1. Add a NuGet package [Microsoft.Extensions.Hosting.WindowsServices](https://www.nuget.org/packages/Microsoft.Extensions.Hosting.WindowsServices).
  2. Update the Program.cs by adding the IHostBuilder.UseWindowsService() extension method to the CreateHostBuilder process. The code snippet below shows an example.

Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseWindowsService()
            .ConfigureAppConfiguration((context, config) =>
            {
                // configure the app here.
            })
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<Worker>();
            });
}

Add line 10 to make the worker service app to be able to host in a Windows Service

In the code above, line 10 is the key to creating a Windows Service app. When the application is hosted in a Windows Service, the extension method IHostBuilder.UseWindowsService() will set the ContentRoot, configure logging, set the host lifetime to WindowsServiceLifetime, and so on.

Voila~ Now we can build the app.

Through these simple steps, we have created an ASP.NET Core app that can be hosted in a Windows Service. Moreover, this app is automatically a Console application that we can run it directly via executing the Demo.exe file in the output folder. This setup allows us to debug the application as a Console app, and allows us to host the app in a Windows Service with minimum configurations.

Bonus: we can check if the app is running as a Windows Service or not using the bool WindowsServiceHelpers.IsWindowsService() method, which returns true if the current process is hosted as a Windows Service, otherwise false.

Add Serilog as a logging provider

Logging is essential for monitoring the status of our application. We definitely need logging for Windows Service applications, because they don’t have any interface and they are totally in the background. In this application, we will use Serilog to log messages to both Console output and physical files.

We will need to install the following NuGet packages that are related to Serilog: Serilog.Enrichers.Thread, Serilog.Extensions.Hosting, Serilog.Sinks.Console, and Serilog.Sinks.File. All of these NuGet packages should use their latest versions.

Then we update the Main method in the Program.cs file to be the code snippet below.

Program.cs

public static void Main(string[] args)
{
    const string loggerTemplate = @"{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u4}]<{ThreadId}> [{SourceContext:l}] {Message:lj}{NewLine}{Exception}";
    var baseDir = AppDomain.CurrentDomain.BaseDirectory;
    var logfile = Path.Combine(baseDir, "App_Data", "logs", "log.txt");
    Log.Logger = new LoggerConfiguration()
        .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
        .Enrich.With(new ThreadIdEnricher())
        .Enrich.FromLogContext()
        .WriteTo.Console(LogEventLevel.Information, loggerTemplate, theme: AnsiConsoleTheme.Literate)
        .WriteTo.File(logfile, LogEventLevel.Information, loggerTemplate,
            rollingInterval: RollingInterval.Day, retainedFileCountLimit: 90)
        .CreateLogger();

    try
    {
        Log.Information("====================================================================");
        Log.Information($"Application Starts. Version: {System.Reflection.Assembly.GetEntryAssembly()?.GetName().Version}");
        Log.Information($"Application Directory: {AppDomain.CurrentDomain.BaseDirectory}");
        CreateHostBuilder(args).Build().Run();
    }
    catch (Exception e)
    {
        Log.Fatal(e, "Application terminated unexpectedly");
    }
    finally
    {
        Log.Information("====================================================================\r\n");
        Log.CloseAndFlush();
    }
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseWindowsService()
        .ConfigureAppConfiguration((context, config) =>
        {
            // Configure the app here.
        })
        .ConfigureServices((hostContext, services) =>
        {
            services.AddHostedService<Worker>();
        })
        .UseSerilog();

add Serilog

In the code snippet above, we added two logging sinks: (1) Console (line 10) with color theme, and (2) plain text files (line 11) that are rolling every day. The logging message are enriched with ThreadID and LogContext, which are two common fields that can help us diagnosing issues if any.

In the end, we add the line 44, .UseSerilog(), to the HostBuilder, so that the host will use Serilog as a logging provider.

Caveat:
By default, the log file path (in line 11) should be able to use a relative path with respect to the assembly entry file. However, when an application is hosted in a Windows Service, the current working directory is set to the “ _C:\WINDOWS\system32_” folder, which is not a good place for log files. So I used an absolute path, with respect to AppDomain.CurrentDomain.BaseDirectory, to make sure the log files are written and saved into the proper location.

Now, if we run the application, we should be able to see both Console outputs and a log file with all logging messages.

We can implement the Windows Service app to run scheduled background tasks, execute long running jobs, and so on. In this blog post, we will utilized a FileSystemWatcher to run background tasks when some specific file system events raise. This process is useful to monitor shared network folders or SFTP folders, in which users drop files.

Add FileSystemWatcher

For those who are new to FileSystemWatcher, you can read more from this article in Microsoft Docs. We are going to add a FileSystemWatcher to listen to the file system change notifications when a new txt file is created in a directory, C:\temp.

The FileSystemWatcher is better to live in the Worker service, because they will have the same lifetime. We initialize the FileSystemWatcher when the Worker service starts, and we dispose the FileSystemWatcher when the Worker service disposes.

The code snippet below shows an example Worker.cs file.

Worker.cs

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    private FileSystemWatcher _folderWatcher;
    private readonly string _inputFolder;

    public Worker(ILogger<Worker> logger)
    {
        _logger = logger;
        _inputFolder = @"C:\temp";
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await Task.CompletedTask;
    }

    public override Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Service Starting");
        if (!Directory.Exists(_inputFolder))
        {
            _logger.LogWarning($"Please make sure the InputFolder [{_inputFolder}] exists, then restart the service.");
            return Task.CompletedTask;
        }

        _logger.LogInformation($"Binding Events from Input Folder: {_inputFolder}");
        _folderWatcher = new FileSystemWatcher(_inputFolder, "*.TXT")
        {
            NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.FileName |
                              NotifyFilters.DirectoryName
        };
        _folderWatcher.Created += Input_OnChanged;
        _folderWatcher.EnableRaisingEvents = true;

        return base.StartAsync(cancellationToken);
    }

    protected void Input_OnChanged(object source, FileSystemEventArgs e)
    {
        if (e.ChangeType == WatcherChangeTypes.Created)
        {
            _logger.LogInformation($"InBound Change Event Triggered by [{e.FullPath}]");

            // do some work

            _logger.LogInformation("Done with Inbound Change Event");
        }
    }

    public override async Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Stopping Service");
        _folderWatcher.EnableRaisingEvents = false;
        await base.StopAsync(cancellationToken);
    }

    public override void Dispose()
    {
        _logger.LogInformation("Disposing Service");
        _folderWatcher.Dispose();
        base.Dispose();
    }
}

A background service with a file system watcher

In line 28, we set the filter to be “*.TXT”, which tells the FileSystemWatcher to watch for specify the type of files in the input folder, which is the txt file type in this project. The FileSystemWatcher accepts event handlers for Changed, Created, Deleted, and Renamed events. For demo purposes, we only handle the new txt file Created events in this project.

If we run the application, then we are able to observe the effects of the Worker service and the FileSystemWatcher. Once a new txt file is created in the C:\temp folder, the application will get notified and the Input_OnChanged event delegate will be called.

Awesome. Now we have a background process to watch the file changes.

Note: In order to work on files or folders in the file system, it’s important to make sure that the current user (for debugging purposes) and the Log On As account have proper permission to the intended working directory.

Bonus: We can use WindowsIndentity.GetCurrent().Name to verify the current user name. I usually write the user name to the application logs as a tracking measure.

Add configuration files

You might have noticed that the input folder path is a magic string, C:\temp, in the code above. We can improve the code by loading a configuration file to get the input folder path.

We add a JSON object “AppSettings” to the appsettings.json file. For demo purposes, we only add one property, InputFolder, in the AppSettings object. The settings can be extended as needed.

appsettings.json

"AppSettings": {
    "InputFolder": "C:\\temp"
}

In order to bind the AppSettings JSON value, we create a C# class file, AppSettings.cs, as follows.

AppSettings.cs

public class AppSettings
{
    public string InputFolder { get; set; }
}

Then, we register the configuration, AppSettings, in the Dependency Injection (DI) container by adding a line in the Program.cs file as follows.

Program.cs

.ConfigureServices((hostContext, services) =>
{
    services.AddHostedService<Worker>();
    services.Configure<AppSettings>(hostContext.Configuration.GetSection("AppSettings"));
})

In the end, we can inject the settings to the Worker service like the following code snippet.

Worker.cs

public Worker(ILogger<Worker> logger, IOptions<AppSettings> settings)
{
    _logger = logger;
    _inputFolder = settings.Value.InputFolder;
}

In this way, we eliminated the magic string, and we have a more extendable code base.

Caveat: The Hosting Environment
It is known that ASP.NET Core web app uses the environment variable ASPNETCORE_ENVIRONMENT to determine the web host environment. However, in the case of Generic Host (link), the host environment is determined by the environment variable DOTNET_ENVIRONMEN by default, which is different from the one for Web applications. Well, you can always overwrite the HostEnvironment using a key-value pair with the key “environment” and its value.

We don’t need to explicitly add JSON file providers for the appsettings.json and appsettings.{environment}.json files, because they are automatically configured inside the Host.CreateDefaultBuilder(args) method. So you might want to make sure the environment name is correctly set, if you want to load correct settings in the appsettings.{environment}.json file.

Add a Scoped Service

In many cases, our applications depend on some short-lived services, for example, database connections, HTTP Client. We don’t want the application holds unnecessary stale resources, so we register those short-lived services as Scoped or Transient dependencies in the DI container. All these should be straightforward in Web applications. However, in Windows Service Applications, there’s some extra work to do.

For demo purposes, we will keep this application simple, and we add two contrived services in the Demo project. The following two code snippets show the SeriveA and ServiceB classes.

ServiceA.cs

public interface IServiceA
{
    void Run();
}

public class ServiceA : IServiceA
{
    private readonly ILogger<ServiceA> _logger;
    private readonly IServiceB _serviceB;

    public ServiceA(ILogger<ServiceA> logger, IServiceB serviceB)
    {
        _logger = logger;
        _serviceB = serviceB;
    }

    public void Run()
    {
        _logger.LogInformation("In Service A");
        _serviceB.Run();
    }
}

ServiceB.cs

public interface IServiceB
{
    void Run();
}

public class ServiceB : IServiceB
{
    private readonly ILogger<ServiceB> _logger;

    public ServiceB(ILogger<ServiceB> logger)
    {
        _logger = logger;
    }

    public void Run()
    {
        _logger.LogInformation("In Service B");
    }
}

We inject the ServiceB into the ServiceA, and we will use ServiceA as an entry point to run the process when a new file is created and detected by the FileSystemWatcher in the Worker service.

We register the ServiceA and the ServiceB in the Program.cs file as follows.
Program.cs

.ConfigureServices((hostContext, services) =>
{
    services.AddHostedService<Worker>();
    services.Configure<AppSettings>(hostContext.Configuration.GetSection("AppSettings"));
    services.AddScoped<IServiceA, ServiceA>();
    services.AddScoped<IServiceB, ServiceB>();
})

Then we inject the IServiceA to the Worker service and call serviceA.Run() as follows.

Worker.cs

public class Worker : BackgroundService
{
    // ...
    private readonly IServiceA _serviceA;

    public Worker(ILogger<Worker> logger, IOptions<AppSettings> settings, IServiceA serviceA)
    {
        // ...
        _serviceA = serviceA;
    }
    // ...
    protected void Input_OnChanged(object source, FileSystemEventArgs e)
    {
        if (e.ChangeType == WatcherChangeTypes.Created)
        {
            // ...
            _serviceA.Run();
            // ...
        }
    }
    //...
}

All done. Everything is hooked up. However, when we run the program, we will get an error immediately. The error message is similar to the following.

System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: Demo.Worker': Cannot consume scoped service 'Demo.Services.IServiceA' from singleton 'Microsoft.Extensions.Hosting.IHostedService'.)

The key part of the error message is “cannot consume scoped service from singleton”. This error is due to the Worker service has a singleton lifetime, while the ServiceA has a scoped lifetime, which would be garbage collected before the Worker service. Thus they cause a violation in DI container.

In order to make it work, we can borrow the IServiceProvider to create a scope and resolve the scoped service. The following code snippet shows an example.

Worker.cs

public class Worker : BackgroundService
{
    // ...
    private readonly IServiceProvider _services;

    public Worker(ILogger<Worker> logger, IOptions<AppSettings> settings, IServiceProvider services)
    {
        // ...
        _services = services;
    }
    // ...
    protected void Input_OnChanged(object source, FileSystemEventArgs e)
    {
        if (e.ChangeType == WatcherChangeTypes.Created)
        {
            // ...
            using (var scope = _services.CreateScope())
            {
                var serviceA = scope.ServiceProvider.GetRequiredService<IServiceA>();
                serviceA.Run();
            }
            // ...
        }
    }
    //...
}

In this way, the DI container is able to resolve all dependencies. Problem solved!

Now, we are ready to deploy our application to a Windows Service.

Windows Service management

To achieve best performance, we first need to build our application in the Release mode.

If you have PowerShell 6.2+, then you can use a series of commands to install, start/stop, remove Windows Services. These commands include New-Service, Start-Service, Get-Service, Stop-Service, and Remove-Service.

BTW: Make sure you have Admin privilege to do these operations.

Here, we will take the old fashion approach to manage Windows Services using sc.exe in CMD. Note: in CMD environment only. The sc commands might not work in PowerShell environment.

The commands to create, start/stop, and delete a Windows Service are shown in the following code snippet.

cmd.bat

:: Create a Windows Service
sc create DemoService DisplayName="Demo Service" binPath="C:\full\path\to\Demo.exe"

:: Start a Windows Service
sc start DemoService

:: Stop a Windows Service
sc stop DemoService

:: Delete a Windows Service
sc delete DemoService

It is worth mentioning that the binPath needs to be the full path of the exe file of the application. All the commands should be easy to use.

Looks like we have covered all the stuff as planed. Hope you have learned something new, and I bet you are able to get started to create a Windows Service app in .NET Core 3 now.

If you need reference, you can find the full project in this GitHub repository.

Thanks for reading.

Basic Authentication Tutorial With .NET Core 3.0

Basic Authentication Tutorial  With .NET Core 3.0

Basic Authentication Tutorial With .NET Core 3.0.In this tutorial we'll go through a simple example of how to implement **Basic HTTP authentication** in an ASP.NET Core 3.0 API with C#.

In this tutorial we'll go through a simple example of how to implement Basic HTTP authentication in an ASP.NET Core 3.0 API with C#.

The example API has just two endpoints/routes to demonstrate authenticating with basic http authentication and accessing a restricted route:

  • /users/authenticate - public route that accepts HTTP POST requests containing the username and password in the body. If the username and password are correct then the user details are returned.
  • /users - secure route that accepts HTTP GET requests and returns a list of all the users in the application if the HTTP Authorization header contains valid basic authentication credentials. If there are no basic auth credentials or the credentials are invalid then a 401 Unauthorized response is returned.

The tutorial project is available on GitHub at https://github.com/cornflourblue/aspnet-core-3-basic-authentication-api.

Tools required to run the ASP.NET Core 3.0 Tutorial Example Locally

To develop and run ASP.NET Core applications locally, download and install the following:

  • .NET Core SDK - includes the .NET Core runtime and command line tools
  • Visual Studio Code - code editor that runs on Windows, Mac and Linux
  • C# extension for Visual Studio Code - adds support to VS Code for developing .NET Core applications
Running the ASP.NET Core Basic Authentication API Locally
  1. Download or clone the tutorial project code from https://github.com/cornflourblue/aspnet-core-3-basic-authentication-api
  2. Start the api by running dotnet run from the command line in the project root folder (where the WebApi.csproj file is located), you should see the message Now listening on: http://localhost:4000. Follow the instructions below to test with Postman or hook up with one of the example single page applications available (Angular, React or Vue).

NOTE: You can also start the application in debug mode in VS Code by opening the project root folder in VS Code and pressing F5 or by selecting Debug -> Start Debugging from the top menu. Running in debug mode allows you to attach breakpoints to pause execution and step through the application code.

Testing the ASP.NET Core Basic Authentication API with Postman

Postman is a great tool for testing APIs, you can download it at https://www.getpostman.com/.

Below are instructions on how to use Postman to authenticate a user with the api, and then make an authenticated request with basic authentication credentials to retrieve a list of users from the api.

How to authenticate a user with Postman

To authenticate a user with the basic authentication api and follow these steps:

  1. Open a new request tab by clicking the plus (+) button at the end of the tabs.

  2. Change the http request method to "POST" with the dropdown selector on the left of the URL input field.

  3. In the URL field enter the address to the authenticate route of your local API - http://localhost:4000/users/authenticate.

  4. Select the "Body" tab below the URL field, change the body type radio button to "raw", and change the format dropdown selector to "JSON (application/json)".

  5. Enter a JSON object containing the test username and password in the "Body" textarea:

    {
        "username": "test",
        "password": "test"
    }
    
  6. Click the "Send" button, you should receive a "200 OK" response containing the user details in the response body, this indicates that the username and password are correct.

Here's a screenshot of Postman after the request is sent and the user has been authenticated:

How to make an authenticated request to retrieve all users

To make an authenticated request using basic authentication credentials, follow these steps:

  1. Open a new request tab by clicking the plus (+) button at the end of the tabs.
  2. Change the http request method to "GET" with the dropdown selector on the left of the URL input field.
  3. In the URL field enter the address to the users route of your local API - http://localhost:4000/users.
  4. Select the "Authorization" tab below the URL field, change the type to "Basic Auth" in the type dropdown selector, enter test into the "Username" field and test into the "Password" field.
  5. Click the "Send" button, you should receive a "200 OK" response containing a JSON array with all the user records in the system (just the one test user in the example).

Here's a screenshot of Postman after making an authenticated request to get all users:

Running an Angular 8 client app with the ASP.NET Core Basic Auth API

For full details about the example Angular 8 application see the tutorial Angular 8 - Basic HTTP Authentication Tutorial & Example. But to get up and running quickly just follow the below steps.

  1. Download or clone the Angular 8 tutorial code from https://github.com/cornflourblue/angular-6-basic-authentication-example
  2. Install all required npm packages by running npm install from the command line in the project root folder (where the package.json is located).
  3. Remove or comment out the line below the comment // provider used to create fake backend located in the /src/app/app.module.ts file.
  4. Start the application by running npm start from the command line in the project root folder, this will launch a browser displaying the Angular example application and it should be hooked up with the ASP.NET Core Basic Authentication API that you already have running.
Running a React client app with the ASP.NET Core Basic Auth API

For full details about the example React application see the post React - Basic HTTP Authentication Tutorial & Example. But to get up and running quickly just follow the below steps.

  1. Download or clone the React tutorial code from https://github.com/cornflourblue/react-basic-authentication-example
  2. Install all required npm packages by running npm install from the command line in the project root folder (where the package.json is located).
  3. Remove or comment out the 2 lines below the comment // setup fake backend located in the /src/index.jsx file.
  4. Start the application by running npm start from the command line in the project root folder, this will launch a browser displaying the React example application and it should be hooked up with the ASP.NET Core Basic Authentication API that you already have running.
Running a Vue.js client app with the ASP.NET Core Basic Auth API

For full details about the example Vue.js application see the post Vue.js - Basic HTTP Authentication Tutorial & Example. But to get up and running quickly just follow the below steps.

  1. Download or clone the VueJS tutorial code from https://github.com/cornflourblue/vue-basic-authentication-example
  2. Install all required npm packages by running npm install from the command line in the project root folder (where the package.json is located).
  3. Remove or comment out the 2 lines below the comment // setup fake backend located in the /src/index.js file.
  4. Start the application by running npm start from the command line in the project root folder, this will launch a browser displaying the VueJS example application and it should be hooked up with the ASP.NET Core Basic Authentication API that you already have running.
ASP.NET Core Basic Authentication Project Structure

The tutorial project is organised into the following folders:
Controllers - define the end points / routes for the web api, controllers are the entry point into the web api from client applications via http requests.
Models - represent request and response models for controller methods, request models define the parameters for incoming requests, and response models can be used to define what data is returned.
Services - contain business logic, validation and data access code.
Entities - represent the application data.
Helpers - anything that doesn't fit into the above folders.

Click any of the below links to jump down to a description of each file along with its code:

ASP.NET Core Users Controller

Path: /Controllers/UsersController.cs

The ASP.NET Core users controller defines and handles all routes / endpoints for the api that relate to users, this includes authentication and standard CRUD operations. Within each route the controller calls the user service to perform the action required, this enables the controller to stay 'lean' and completely separated from the business logic and data access code.

The controller actions are secured with basic authentication using the [Authorize] attribute, with the exception of the Authenticate method which allows public access by overriding the [Authorize] attribute on the controller with the [AllowAnonymous] attribute on the action method. I chose this approach so any new action methods added to the controller will be secure by default unless explicitly made public.

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using WebApi.Services;
using System.Threading.Tasks;
using WebApi.Models;

namespace WebApi.Controllers
{
    [Authorize]
    [ApiController]
    [Route("[controller]")]
    public class UsersController : ControllerBase
    {
        private IUserService _userService;

        public UsersController(IUserService userService)
        {
            _userService = userService;
        }

        [AllowAnonymous]
        [HttpPost("authenticate")]
        public async Task<IActionResult> Authenticate([FromBody]AuthenticateModel model)
        {
            var user = await _userService.Authenticate(model.Username, model.Password);

            if (user == null)
                return BadRequest(new { message = "Username or password is incorrect" });

            return Ok(user);
        }

        [HttpGet]
        public async Task<IActionResult> GetAll()
        {
            var users = await _userService.GetAll();
            return Ok(users);
        }
    }
}
ASP.NET Core User Entity

Path: /Entities/User.cs

The user entity class represents the data for a user in the application. Entity classes are used to pass data between different parts of the application (e.g. between services and controllers) and can be used to return http response data from controller action methods.

namespace WebApi.Entities
{
    public class User
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
    }
}
ASP.NET Core Basic Authentication Handler

Path: /Helpers/BasicAuthenticationHandler.cs

The basic authentication handler is asp.net core middleware that handles request authentication by inheriting from the asp.net core AuthenticationHandler base class and overriding the HandleAuthenticateAsync() method.

Basic authentication logic is implemented in the HandleAuthenticateAsync() method by verifying the username and password received in the HTTP Authorization header, verification is done by calling _userService.Authenticate(username, password). On successful authentication the method returns AuthenticateResult.Success(ticket) which makes the request authenticated and sets the HttpContext.User to the currently logged in user.

The basic authentication middleware is configured in the application inside the ConfigureServices(IServiceCollection services) method in the application Startup file below.

using System;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using WebApi.Entities;
using WebApi.Services;

namespace WebApi.Helpers
{
    public class BasicAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
    {
        private readonly IUserService _userService;

        public BasicAuthenticationHandler(
            IOptionsMonitor<AuthenticationSchemeOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock,
            IUserService userService)
            : base(options, logger, encoder, clock)
        {
            _userService = userService;
        }

        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            if (!Request.Headers.ContainsKey("Authorization"))
                return AuthenticateResult.Fail("Missing Authorization Header");

            User user = null;
            try
            {
                var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
                var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
                var credentials = Encoding.UTF8.GetString(credentialBytes).Split(new[] { ':' }, 2);
                var username = credentials[0];
                var password = credentials[1];
                user = await _userService.Authenticate(username, password);
            }
            catch
            {
                return AuthenticateResult.Fail("Invalid Authorization Header");
            }

            if (user == null)
                return AuthenticateResult.Fail("Invalid Username or Password");

            var claims = new[] {
                new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                new Claim(ClaimTypes.Name, user.Username),
            };
            var identity = new ClaimsIdentity(claims, Scheme.Name);
            var principal = new ClaimsPrincipal(identity);
            var ticket = new AuthenticationTicket(principal, Scheme.Name);

            return AuthenticateResult.Success(ticket);
        }
    }
}
ASP.NET Core Extension Methods

Path: /Helpers/ExtensionMethods.cs

Extension methods are used to add convenience methods and extra functionality to existing types in C#.

The extension methods class adds a couple of simple convenience methods for removing passwords from User instances and IEnumerable<User> collections. These methods are called by the Authenticate and GetAll methods in the UserService to ensure the user objects returned don't include passwords.

using System.Collections.Generic;
using System.Linq;
using WebApi.Entities;

namespace WebApi.Helpers
{
    public static class ExtensionMethods
    {
        public static IEnumerable<User> WithoutPasswords(this IEnumerable<User> users) {
            return users.Select(x => x.WithoutPassword());
        }

        public static User WithoutPassword(this User user) {
            user.Password = null;
            return user;
        }
    }
}
ASP.NET Core Authenticate Model

Path: /Models/AuthenticateModel.cs

The authenticate model defines the parameters for incoming requests to the /users/authenticate route of the api, because it is set as the parameter to the Authenticate method of the UsersController. When an HTTP POST request is received to the route, the data from the body is bound to an instance of the AuthenticateModel, validated and passed to the method.

ASP.NET Core Data Annotations are used to automatically handle model validation, the [Required] attribute sets both the username and password as required fields so if either are missing a validation error message is returned from the api.

using System.ComponentModel.DataAnnotations;

namespace WebApi.Models
{
    public class AuthenticateModel
    {
        [Required]
        public string Username { get; set; }

        [Required]
        public string Password { get; set; }
    }
}
ASP.NET Core Basic Auth User Service

Path: /Services/UserService.cs

The user service contains a method for authenticating user credentials, and a method for getting all users in the application.

I hardcoded the array of users in the example to keep it focused on basic http authentication, in a production application it is recommended to store user records in a database with hashed passwords. For an extended example that includes support for user registration and stores data with Entity Framework Core check out ASP.NET Core 3.0 - Simple API for Authentication, Registration and User Management.

The top of the file contains an interface that defines the user service, below that is the concrete user service class that implements the interface.

On successful authentication the Authenticate method returns the user details, the client application should then include the base64 encoded user credentials in the HTTP Authorization header of subsequent api requests to access secure endpoints.

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WebApi.Entities;
using WebApi.Helpers;

namespace WebApi.Services
{
    public interface IUserService
    {
        Task<User> Authenticate(string username, string password);
        Task<IEnumerable<User>> GetAll();
    }

    public class UserService : IUserService
    {
        // users hardcoded for simplicity, store in a db with hashed passwords in production applications
        private List<User> _users = new List<User>
        {
            new User { Id = 1, FirstName = "Test", LastName = "User", Username = "test", Password = "test" }
        };

        public async Task<User> Authenticate(string username, string password)
        {
            var user = await Task.Run(() => _users.SingleOrDefault(x => x.Username == username && x.Password == password));

            // return null if user not found
            if (user == null)
                return null;

            // authentication successful so return user details without password
            return user.WithoutPassword();
        }

        public async Task<IEnumerable<User>> GetAll()
        {
            return await Task.Run(() => _users.WithoutPasswords());
        }
    }
}
ASP.NET Core App Settings (Development)

Path: /appsettings.Development.json

Configuration file with application settings that are specific to the development environment.

{
  "Logging": {
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}
ASP.NET Core App Settings

Path: /appsettings.json

Root configuration file containing application settings for all environments.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}
ASP.NET Core Program

Path: /Program.cs

The program class is a console app that is the main entry point to start the application, it configures and launches the web api host and web server using an instance of IHostBuilder. ASP.NET Core applications require a host in which to execute.

Kestrel is the web server used in the example, it's a new cross-platform web server for ASP.NET Core that's included in new project templates by default. Kestrel is fine to use on it's own for internal applications and development, but for public facing websites and applications it should sit behind a more mature reverse proxy server (IIS, Apache, Nginx etc) that will receive HTTP requests from the internet and forward them to Kestrel after initial handling and security checks.

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace WebApi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>()
                        .UseUrls("http://localhost:4000");
                });
    }
}
ASP.NET Core Startup

Path: /Startup.cs

The startup class configures the request pipeline of the application and how all requests are handled.

The basic authentication handler is configured for the application in the ConfigureServices(IServiceCollection services) method.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using WebApi.Helpers;
using WebApi.Services;
using Microsoft.AspNetCore.Authentication;

namespace WebApi
{
    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.AddCors();
            services.AddControllers();

            // configure basic authentication 
            services.AddAuthentication("BasicAuthentication")
                .AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);

            // configure DI for application services
            services.AddScoped<IUserService, UserService>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseRouting();

            // global cors policy
            app.UseCors(x => x
                .AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader());

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints => endpoints.MapControllers());
        }
    }
}
ASP.NET Core Basic Authentication Web Api csproj

Path: /WebApi.csproj

The csproj (C# project) is an MSBuild based file that contains target framework and NuGet package dependency information for the application.

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.0</TargetFramework>
  </PropertyGroup>
</Project>