Middleware - Building Custom Request Processors
Level: Beginner
Middleware is code that runs in the request pipeline. Learn this after the request pipeline and before routing details.
- Middleware definition: Reusable code that runs on every request in the pipeline
- Why middleware exists: Avoid repeating logging, auth, CORS, error handling in every route
app.Use(): Pass request to next middleware/endpoint (used for logging, timing, custom checks)app.Run(): Terminal middleware that ends pipeline (last middleware, returns response)app.Map(): Branch pipeline by path (e.g.,/adminroutes to admin middleware)nextparameter: Function that continues pipeline to next middleware- Before/after pattern: Code before
await next()runs on request, code after runs on response - Middleware can stop request: Return without calling
next()(e.g., authentication failure blocks request) - Built-in middleware examples: UseExceptionHandler, UseHttpsRedirection, UseStaticFiles, UseCors, UseAuthentication, UseAuthorization
- Custom middleware class: Implements InvokeAsync(HttpContext context) method
- School Management examples: Request logging middleware (logs GET /students), maintenance mode middleware, tenant detection middleware
- Common mistakes: Forgetting
await next(), callingnext()twice, wrong middleware order - Performance consideration: Expensive middleware (database queries) should run after static files
What is Middleware?
Middleware is code that runs while a request is travelling through the ASP.NET Core pipeline.
It can:
- Read request data
- Log request details
- Check authentication
- Reject invalid requests
- Call the next middleware
- Modify the response
- Catch errors
Simple mental model:
Request -> Middleware -> Middleware -> Endpoint -> Response
First Middleware Example
app.Use(async (context, next) =>
{
Console.WriteLine("Before endpoint");
await next();
Console.WriteLine("After endpoint");
});
context means current request and response.
next means "continue to the next middleware or endpoint".
Before and After Behavior
app.Use(async (context, next) =>
{
Console.WriteLine("1. Request entering logging middleware");
await next();
Console.WriteLine("4. Response leaving logging middleware");
});
app.MapGet("/", () =>
{
Console.WriteLine("2. Endpoint running");
return "Hello";
});
Output:
1. Request entering logging middleware
2. Endpoint running
4. Response leaving logging middleware
This is why middleware can do work both before and after your endpoint.
School Example: Request Logging
app.Use(async (context, next) =>
{
string method = context.Request.Method;
string path = context.Request.Path;
Console.WriteLine($"SchoolPortal Request: {method} {path}");
await next();
Console.WriteLine($"SchoolPortal Response: {context.Response.StatusCode}");
});
For:
GET /students/101
log:
SchoolPortal Request: GET /students/101
SchoolPortal Response: 200
Middleware Can Stop the Request
app.Use(async (context, next) =>
{
bool maintenanceMode = false;
if (maintenanceMode)
{
context.Response.StatusCode = 503;
await context.Response.WriteAsync("SchoolPortal is under maintenance");
return;
}
await next();
});
If middleware returns without calling next, the pipeline stops.
app.Use vs app.Run vs app.Map
| Method | Meaning | Calls Next? | Use Case |
|---|---|---|---|
app.Use | Add middleware | Usually yes | Logging, auth, timing |
app.Run | Terminal middleware | No | End the pipeline |
app.Map | Branch pipeline by path | Depends | Special path handling |
app.Use
app.Use(async (context, next) =>
{
await next();
});
Use when request should usually continue.
app.Run
app.Run(async context =>
{
await context.Response.WriteAsync("This ends the pipeline");
});
Use carefully. It stops the pipeline.
app.Map
app.Map("/admin", adminApp =>
{
adminApp.Run(async context =>
{
await context.Response.WriteAsync("Admin area");
});
});
Use when a path needs a separate branch.
Built-In Middleware Examples
| Middleware | Purpose |
|---|---|
UseExceptionHandler | Handles errors safely |
UseHttpsRedirection | Redirects HTTP to HTTPS |
UseStaticFiles | Serves CSS, JS, images |
UseRouting | Enables routing |
UseCors | Allows frontend apps from other origins |
UseAuthentication | Identifies the user |
UseAuthorization | Checks permissions |
Common Middleware Order
app.UseExceptionHandler("/error");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
Order matters because each middleware depends on what happened before it.
Custom Middleware Class
For bigger middleware, use a class.
public class RequestTimingMiddleware
{
private readonly RequestDelegate _next;
public RequestTimingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var watch = System.Diagnostics.Stopwatch.StartNew();
await _next(context);
watch.Stop();
Console.WriteLine($"Request took {watch.ElapsedMilliseconds} ms");
}
}
Register it:
app.UseMiddleware<RequestTimingMiddleware>();
Real School Middleware Ideas
| Middleware | What It Does |
|---|---|
| Request logging | Log every API request |
| Request timing | Detect slow attendance/report endpoints |
| Maintenance mode | Temporarily block users during upgrades |
| Security headers | Add safe browser headers |
| Tenant detection | Identify school/branch from request |
Common Beginner Mistakes
| Mistake | Problem |
|---|---|
Forgetting await next() | Request stops early |
Calling next() twice | Pipeline may behave incorrectly |
| Writing business logic in middleware | Middleware should handle cross-cutting concerns |
| Wrong order | Auth, CORS, static files, and errors may break |
Using app.Run too early | Routes after it may never execute |
What to Remember
Middleware = reusable request/response processing step
next = continue pipeline
context = current request and response
order = very important
Q: What is middleware in ASP.NET Core?
Good Answer: "Middleware is a component in the ASP.NET Core request pipeline. It receives the current HttpContext, can inspect or modify the request, can stop the request, or can call next() to pass control to the next middleware. Middleware can also run code after the next component returns, so it can process both request and response. Common examples are logging, exception handling, static files, CORS, authentication, and authorization."
Practice Before Next Article
- Write middleware that logs request path.
- Write middleware that blocks
/blocked. - Explain
app.Usevsapp.Run. - Explain why authentication comes before authorization.
- List three built-in middleware components.
Use ChatGPT, Claude, or Copilot to go deeper on ASP.NET Core middleware. Try these prompts:
"Explain app.Use vs app.Run with examples""Give me a custom middleware for request timing""Why does middleware order matter?""What are common middleware interview questions?"
💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.