If you've struggled to understand when you should Use, Map, or Run general middleware in ASP.NET Core — you're not the only one.
Each of these methods helps you build the HTTP request pipeline, and understanding how and when to Use, Map, and Run can give you more control over, modularity, and understanding of the architecture in your application.
Let's take a look.
What Is Middleware?
In ASP.NET Core, middleware is a way to think about a collection of components that process incoming HTTP requests and outgoing HTTP responses.
Each middleware component can:
- Inspect or modify the request/response
- Do logic before or after the next middleware component
- Short-circuit the pipeline by not calling the next middleware
This is what enables ASP.NET Core apps to work on requests — it's a linear pipeline, and it matters what order they go in.
What Are Request Delegates?
Request delegates are the actual logic that handles HTTP requests. In ASP.NET Core, you establish your middleware pipeline with these three extension methods on IApplicationBuilder:
Use(...)Map(...)Run(...)
Let's dive deeper into the differences.
Use – Chain Middleware
Purpose: To add middleware to the pipeline.
How it works:
Use takes a next parameter, which represents the next delegate in the pipeline. This allows it to run the current middleware before and/or after the next middleware.
Example:
app.Use(async (context, next) =>
{
// Pre-processing logic
Console.WriteLine("Before next");
await next();
// Post-processing logic
Console.WriteLine("After next");
});Key Behavior:
- If
next()is not called the pipeline is short-circuited (no other middleware will run). - Useful for logging, authentication, exception handling, etc.
🌳 Map – Branch Middleware
Purpose: To branch the middleware pipeline based on the URL path.
How it works:
Map will check if the request path begins with a specific segment, and if so, branch off and run a separate middleware pipeline for that path.
Example:
app.Map("/admin", adminApp =>
{
adminApp.Run(async context =>
{
await context.Response.WriteAsync("Welcome to the Admin area.");
});
});Key Behavior:
- Will only run the branch if the path matches.
- Great for modularizing logic by feature or section (e.g.,
/api,/admin). - You can still use
UseandRuninside a branch.
⛔ Run – Terminate the Pipeline
Purpose: To add terminal middleware that simulates the end of the request pipeline.
How it works:
Run does not call next(), and therefore, there is no other middleware that will execute.
Example:
app.Run(async context =>
{
await context.Response.WriteAsync("Hello, World!");
});Key Behavior:
- Used when the pipeline should stop and respond immediately.
- Typically found at the end of the pipeline or when inside a
Mapbranch.
Which One Should You Use?
- Use
Usewhen you want to wrap logic around requests (e.g., logging, auth). - Use
MapIf you want to handle route-based branching cleanly and modularly. - Use
RunYou want to terminate the pipeline, either at the end or in a mapped branch.
Final Thoughts
Middleware in ASP.NET Core allows you to have explicit control over the behavior and construction of your application. By understanding how to use Use, Map, and Run properly, you can create cleaner, more modular, and more maintainable pipelines to handle HTTP traffic.
Again, this understanding of these three core building blocks isn't purely academic; it is the foundation for allowing you to construct scalable and testable ASP.NET Core applications.
What about you? What is your favorite use case for middleware, or one you've created from scratch? I'd love to discuss it in the comments.