What is middleware in ASP.NET Core?
Quick Answer
Middleware is a component that sits in the request pipeline. Each piece of middleware receives the HttpContext, can do work before and after the rest of the pipeline, and then either calls next() to pass the request on or short-circuits by returning a response. Middleware is chained in the order you register it in Program.cs.
Middleware is how ASP.NET Core processes a request. Instead of HTTP modules and handlers, you have a pipeline of small components, and each one gets the HttpContext, does something before and after calling the next component, and can stop the pipeline early if it needs to. You wire them up in Program.cs with app.Use, and the order you add them is the order they run — so, for example, UseAuthentication has to come before UseAuthorization, and exception handling goes near the top so it can catch everything below it. Anything cross-cutting, like logging or adding a security header, is a good fit for custom middleware.
Conceptually the pipeline is a set of nested function calls. When a request arrives, it flows through each middleware in registration order; when a response comes back, it flows through them in reverse. That “before and after” shape is why order matters so much.
Middleware is registered with three extension methods:
app.Use(...)— adds middleware that can callnextto continue, or not.app.Run(...)— a terminal middleware that never callsnext; it ends the pipeline.app.Map(...)/app.UseWhen(...)— branch the pipeline based on the path or a predicate.
Common built-in middleware: exception handling, HTTPS redirection, static files, routing, CORS, authentication, authorization, and the endpoint middleware that actually runs your controller or minimal API. Cross-cutting concerns — logging, correlation IDs, request timing, custom headers — are natural middleware.
A middleware can short-circuit: for example, the authentication/authorization middleware returns 401 or 403 without ever calling the endpoint.
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 sw = Stopwatch.StartNew();
await _next(context); // call the rest of the pipeline
sw.Stop();
_logger.LogInformation("{Method} {Path} -> {Status} in {Ms}ms",
context.Request.Method, context.Request.Path,
context.Response.StatusCode, sw.ElapsedMilliseconds);
}
}
// Program.cs
app.UseMiddleware<RequestTimingMiddleware>();
- One clear place for cross-cutting concerns that apply to every request
- Explicit, ordered composition — you can read the whole pipeline in Program.cs
- Short-circuiting: auth, caching or rate limiting can respond without hitting your code
- Reusable and testable in isolation
- Branching with Map / UseWhen for path- or condition-specific behaviour