What is Middleware?

In ASP.NET Core, middleware are components that plug into the application's request processing pipeline. Each middleware component has the opportunity to:

  • Execute logic.
  • Pass the request on to the next middleware in the pipeline.
  • Short-circuit the request, preventing further processing.

This composable approach allows for building sophisticated request handling logic by chaining together simple, focused components.

The Middleware Pipeline

The ASP.NET Core request pipeline is a sequence of middleware components. When a request arrives at the application, it flows through the pipeline from the first middleware to the last. Each middleware can inspect and modify the request and response objects. The pipeline is configured in the Configure method of the Startup class (or the equivalent in minimal APIs).

How It Works

Imagine a chain of command. Each middleware is a link in the chain. It receives the request, does something with it, and then decides whether to pass it to the next link or handle it completely. The very last middleware in the pipeline is typically responsible for executing the application's core logic, like routing to a controller action or serving a static file.

Creating Custom Middleware

You can create your own middleware by implementing one of the following patterns:

1. Using a Class

This is the most common and recommended approach. Create a class with a constructor that accepts an `RequestDelegate` and implement an `InvokeAsync` method that takes an `HttpContext` object.


    public class MyCustomMiddleware
    {
        private readonly RequestDelegate _next;

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

        public async Task InvokeAsync(HttpContext context)
        {
            // Logic before the next middleware
            Console.WriteLine($"Request received for: {context.Request.Path}");

            // Call the next middleware in the pipeline
            await _next(context);

            // Logic after the next middleware (e.g., modifying the response)
            Console.WriteLine($"Request processing finished for: {context.Request.Path}");
        }
    }
                        

2. Using a Factory Function

You can also use a factory function, often with dependency injection.


    // In Startup.cs (or Program.cs for minimal APIs)
    app.Use(async (context, next) =>
    {
        Console.WriteLine("Processing request...");
        await next(context);
        Console.WriteLine("Finished processing request.");
    });
                        

Using Middleware

Once you've created your middleware, you need to register it in the request pipeline. This is typically done in the Configure method of your Startup.cs file or the Program.cs file for minimal APIs.


    // In Startup.cs Configure method:
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // ... other middleware
        app.UseMiddleware<MyCustomMiddleware>();
        // ...
    }

    // Or for minimal APIs (Program.cs):
    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();

    app.UseMiddleware<MyCustomMiddleware>();

    app.Run();
                        

Built-in Middleware

ASP.NET Core provides a rich set of built-in middleware components for common tasks:

  • StaticFilesMiddleware: Serves static files (HTML, CSS, JS, images).
  • AuthenticationMiddleware: Handles authentication.
  • AuthorizationMiddleware: Handles authorization.
  • RoutingMiddleware: Selects the correct endpoint for a request.
  • EndpointRoutingMiddleware: Runs the selected endpoint.
  • ExceptionHandlerMiddleware: Catches exceptions and displays an error page.
  • CorsMiddleware: Implements Cross-Origin Resource Sharing.

You can find more information on these in the official ASP.NET Core documentation.

Order Matters

The order in which you add middleware to the pipeline is crucial. Middleware components are executed in the order they are called. If a middleware component terminates the request (e.g., by sending a response directly), subsequent middleware will not be executed.

For example, if you want to serve static files, you should typically add StaticFilesMiddleware early in the pipeline:


    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseStaticFiles(); // Serve static files first
        app.UseRouting();
        app.UseAuthorization();

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

Conversely, exception handling middleware should usually be placed at the very beginning of the pipeline so that it can catch exceptions from any subsequent middleware.