跳到主要内容

Middleware

1. What Is Middleware (Core Concept)

In ASP.NET Core, middleware is a series of components that form a request–response pipeline. Every HTTP request must pass through this pipeline, and every response flows back through it in reverse order.

Each middleware component can:

  • Read the incoming request
  • Modify the request or response
  • Call the next middleware
  • Stop the pipeline early and return a response

Mental model: Middleware is like checkpoints a request passes before reaching your app logic.


2. Middleware Pipeline Execution Model

Request Flow

Client → Middleware 1 → Middleware 2 → Middleware 3 → App

Response Flow

App → Middleware 3 → Middleware 2 → Middleware 1 → Client
  • Middleware executes top → bottom on the request
  • Then bottom → top on the response
  • Code before next() runs on the way in
  • Code after next() runs on the way out

This is why logging, timing, and error handling work so well in middleware.


3. app.Use vs app.Run (Critical Difference)

app.Use(...)

  • Non-terminal middleware
  • Receives next
  • Must call await next() to continue the pipeline
  • Can run logic before and after the next middleware

Typical use cases

  • Authentication
  • Logging
  • Adding headers
  • Metrics
app.Use(async (context, next) => {
// before
await next();
// after
});

app.Run(...)

  • Terminal middleware
  • Does NOT call next
  • Ends the pipeline
  • Generates the final response

Typical use cases

  • Simple endpoints
  • Fallback responses
app.Run(async (context) => {
await context.Response.WriteAsync("Done");
});

Important Rule

  • Only the first app.Run will execute
  • Anything after it is unreachable

4. Chaining Middleware (Execution Example)

app.Use(async (ctx, next) => {
await ctx.Response.WriteAsync("A ");
await next();
});

app.Use(async (ctx, next) => {
await ctx.Response.WriteAsync("B ");
await next();
});

app.Run(async (ctx) => {
await ctx.Response.WriteAsync("C ");
});

Output

A B C

Why?

  • A runs → calls next
  • B runs → calls next
  • C runs → pipeline ends
  • No middleware runs after Run

5. Short-Circuiting the Pipeline

A middleware can stop the pipeline by not calling next().

Common scenarios:

  • Authentication failure
  • Authorization failure
  • Validation errors
  • Caching
if (!isAuthorized) {
context.Response.StatusCode = 401;
return; // pipeline stops
}

This is how security middleware works.


6. Custom Middleware — Why & When

Why create custom middleware?

  • Reusable logic
  • Centralized behavior
  • Cleaner controllers
  • Cross-cutting concerns

Examples:

  • Request logging
  • Feature flags
  • Custom headers
  • Rate limiting

7. Two Ways to Write Custom Middleware

Conventional Middleware (Most Common)

Key features

  • Class-based
  • Constructor injects RequestDelegate
  • Uses Invoke method
public class HelloMiddleware
{
private readonly RequestDelegate _next;

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

public async Task Invoke(HttpContext context)
{
await context.Response.WriteAsync("Before\n");
await _next(context);
await context.Response.WriteAsync("After\n");
}
}

Best when:

  • You want simplicity
  • You don't need DI lifetime control

IMiddleware-Based Middleware

Key features

  • Implements IMiddleware
  • Registered via DI
  • Uses InvokeAsync
public class MyMiddleware : IMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
await next(context);
}
}

Best when:

  • You want DI-managed lifetimes
  • You need scoped services

8. Extension Methods (Clean Registration)

Always wrap middleware registration in extension methods:

public static class MiddlewareExtensions
{
public static IApplicationBuilder UseHelloMiddleware(this IApplicationBuilder app)
{
return app.UseMiddleware<HelloMiddleware>();
}
}

This keeps Program.cs clean and readable.


Typical Order

  1. Exception handling
  2. HTTPS redirection
  3. Static files
  4. Routing
  5. CORS
  6. Authentication
  7. Authorization
  8. Endpoints (controllers / minimal APIs)

Why this order?

  • Errors should be caught early
  • Security before business logic
  • Static files should bypass auth
  • Authorization needs routing info

Order ≠ random. Each layer depends on the previous one.


10. UseWhen() — Conditional Middleware

UseWhen() allows branching pipelines based on a condition.

app.UseWhen(
(ctx) => ctx.Request.Query.ContainsKey("debug"),
(branch) => {
branch.Use(async (ctx, next) => {
await ctx.Response.WriteAsync("Debug Mode\n");
await next();
});
},
);
  • Condition is checked per request
  • Branch runs only when condition is true
  • After branch, request rejoins main pipeline

Use cases

  • Debug-only logic
  • Feature toggles
  • A/B testing
  • Conditional logging

11. Interview-Ready Summary (Say This)

"Middleware in ASP.NET Core is a request–response pipeline. Each middleware can inspect, modify, or short-circuit requests. Use continues the pipeline, Run terminates it. Order matters, and custom middleware helps encapsulate cross-cutting concerns like logging, authentication, and error handling."