How to write a custom middleware in .NET Core

There are many scenarios where you need a custom middleware, such as: logging, adding a RequestId header, debugging integrations with 3rd parties(to inspect incoming requests), etc.

There are also several ways to achieve this. Let's investigate together some of the options.

  1. In the body of the Configure method in Startup.cs: Below is an example of a middleware that reads the "Accept-Language" header, and set some kind of greeting based on that value. For simplicity I've omitted the return part of the greeting.

    public void Configure(IApplicationBuilder app)
    {
    // code omitted for readability
    app.Use(async (context, next) =>
    {
        var acceptedLanguage = context.Request.Query["Accept-Language"];
        if (!string.IsNullOrWhiteSpace(acceptedLanguage))
        {
            acceptedLanguage switch
            {
                "en" => "Hello user",
                "fr" => "Bonjour l'utilisateur",
                "de" => "Hallo Benutzer",
                _              => throw new ArgumentException(message: "invalid language value", paramName: nameof(acceptedLanguage)),
            };
        }
    
        // Call the next delegate/middleware in the pipeline
        await next();
    });
    // code omitted for readability
    }
    
  2. The second way we can achieve this is by moving all this logic into a separate class: In this next example we are setting a custom header "RequestId" with a guid that is returned to the client.
public class TraceMiddleware
{
    private readonly RequestDelegate _next;

    public TraceMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        string traceId = Guid.NewGuid().ToString();

        httpContext.Response.OnStarting((state) =>
        {
            httpContext.Response.Headers.Add("X-RequestId", traceId);
            return Task.FromResult(0);
        }, httpContext);

        await _next(httpContext);
    }
}

By moving the logic into a separate class we are complying to the SOLID principles, specifically the separation of concern. But we having finished yet, we still need to specify our app to use our newly built middleware. This is done in the Configure method, of the the Startup.cs:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // code omitted for readability

            app.UseMiddleware<TraceMiddleware>();

           // code omitted for readability
        }

It's that easy! I hope this helped you.

PS: Microsoft's documentation is really useful, read more here