Middlewares in .NET

In modern web application development, efficiently managing HTTP requests is crucial for performance, security, and maintenance. In .NET, middlewares are essential for configuring the request processing pipeline, allowing developers to handle requests and responses in a modular and extendable way. This article explores what middlewares are, how to implement them in .NET, and best practices for their effective use.

What is a Middleware?

Think of your web application like a restaurant kitchen. When a customer places an order (HTTP request), the order goes through various stations (middlewares) in the kitchen, each with its specific task, before reaching the customer's table (HTTP response).

A middleware acts as one of these stations. Each middleware can:

  1. Process incoming HTTP requests.
  2. Execute logic before and/or after passing the request to the next middleware.
  3. Terminate the request or pass it through the chain of middlewares.

This design allows developers to add cross-cutting features like authentication, logging, error handling, and more in a modular way.

Types of Middlewares

In ASP.NET Core, middlewares can be categorized into three main types:

  1. Third-Party Middlewares: Provided by external libraries, such as OAuth authentication.
  2. Built-in Middlewares: Included in ASP.NET Core, such as error handling, authentication, and authorisation.
  3. Custom Middlewares: Created by users for specific requirements.

Project Structure in .NET

In .NET, the entry point of an application is in the Program class, where the middleware pipeline and necessary services are configured.

Example Project

Start by creating a new ASP.NET Core project:

dotnet new web -n MiddlewareExample

The basic project structure will include a Program.cs class.

Program.cs Class

In the Program class, you configure middlewares and services. Here’s a complete example:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Threading.Tasks;

var builder = WebApplication.CreateBuilder(args);

// Configure services
builder.Services.AddControllers();

var app = builder.Build();

// Configure the middleware pipeline
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

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

// Custom middleware
app.UseMiddleware<LoggingMiddleware>();

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

app.Run();

// Custom middleware
public class LoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<LoggingMiddleware> _logger;

    public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        _logger.LogInformation($"Incoming request: {context.Request.Method} {context.Request.Path}");

        await _next(context);

        _logger.LogInformation($"Outgoing response: {context.Response.StatusCode}");
    }
}

Built-in Middlewares

ASP.NET Core comes with several built-in middlewares commonly used in applications.

Error Handling

The error handling middleware captures unhandled exceptions and provides a user-friendly error response:

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
else
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

Authentication and Authorisation

To add authentication and authorisation:

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

Response Compression Middleware

To add response compression:

builder.Services.AddResponseCompression();

app.UseResponseCompression();

Creating a Custom Middleware

Custom middleware follows a specific structure. Here’s an example of a middleware that measures the processing time of a request:

Request Timing Middleware

public class RequestTimingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestTimingMiddleware> _logger;

    public RequestTimingMiddleware(RequestDelegate next, ILogger<RequestTimingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var startTime = DateTime.UtcNow;

        context.Response.OnStarting(() =>
        {
            var duration = DateTime.UtcNow - startTime;
            context.Response.Headers["X-Processing-Time-Milliseconds"] = duration.TotalMilliseconds.ToString();
            return Task.CompletedTask;
        });

        await _next(context);
    }
}

To register this middleware in the pipeline:

app.UseMiddleware<RequestTimingMiddleware>();

Middleware Diagram

Best Practices

  1. Order of Middlewares: The sequence in which middlewares are registered is crucial. Ensure authentication, authorisation, and error handling middlewares are positioned correctly.

  2. Avoid Heavy Logic: Middlewares should be lightweight and fast. Avoid performing expensive operations within them.

  3. Reuse: Utilize built-in and third-party middlewares whenever possible to avoid reinventing the wheel.

  4. Exception Handling: Handle exceptions within middlewares to prevent information leaks and maintain consistent responses.

  5. Proper Logging: Implement effective logging to monitor middleware behaviour and aid debugging.

Conclusion

Middlewares are vital components in ASP.NET Core that enable modular handling and processing of HTTP requests. With .NET, configuring and using middlewares has been streamlined and centralised in the Program class.

Sources of Information

  1. Official Microsoft Documentation on Middlewares in ASP.NET Core
  2. Examples and Best Practices on GitHub
Web DevelopmentBackendCsharpDotnetMiddleware
Avatar for Adrián Bailador

Written by Adrián Bailador

🚀 Full-Stack Dev 👨🏻‍💻 .NET Engineer 👾 Geek & Friki 💡 Talks about #dotnet, #csharp, #azure, #visualstudio and a little bit of #nextjs.

Loading

Fetching comments

Hey! 👋

Got something to say?

or to leave a comment.