TL;DR
  • Use adds middleware that can process and pass requests along the pipeline.
  • Run is terminal middleware, once hit, the pipeline ends and no further middleware executes.
  • Map branches the pipeline by path, creating sub-pipelines for specific routes.
  • Always call next() in Use unless you want to short-circuit the pipeline.
  • Middleware order and correct usage of Use, Run, and Map are key to building robust ASP.NET Core APIs.

Hey everyone! If you’ve ever worked with ASP.NET Core, you know middleware is the backbone of how requests are handled. But getting the pipeline flow right can be tricky. Getting Use, Run, and Map mixed up can lead to middleware that doesn’t fire or, worse, short-circuits your entire request.

Let’s quickly break down the difference.

Think of Use as a link in a chain or a toll booth on a highway. Its job is to perform an action and then pass the request to the next component in the pipeline. If you don’t call next(), the chain breaks, and the request stops.

You’ll use this for tasks that need to inspect or modify a request and then let it continue, like logging, authentication, or adding response headers.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// This middleware logs the request path and passes it on.
app.Use(async (context, next) =>
{
    Console.WriteLine($"[Use] Handling request for: {context.Request.Path}");
    // Calls the next middleware in the pipeline.
    await next(context);
});

app.MapGet("/", () => "Hello from the main pipeline!");

app.Run();

Notice how await next(context) is the key. It’s what keeps the request moving down the line.

Run: The End of the Line

Run is a terminal middleware. It’s a dead-end road. Once a request hits a Run delegate, the pipeline stops. There’s no next parameter because it’s not meant to call anything after it.

Run is perfect for handling a request that should be fully handled by one piece of middleware, like a simple health check endpoint or a fallback “not found” response.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// This middleware will ALWAYS run and terminate the request.
// Anything registered after this won't be called.
app.Run(async context =>
{
    Console.WriteLine("[Run] Pipeline ends here.");
    await context.Response.WriteAsync("This is the end of the pipeline.");
});

// This will never be reached.
app.MapGet("/", () => "You will never see this!");

app.Run();

Map: The Fork in the Road

Map is how you create a branch in your pipeline. Think of it as a fork in the road that directs traffic based on the request path. If a request path starts with the value you specify, it gets diverted into a separate, isolated sub-pipeline.

This is incredibly useful for creating path-specific middleware chains, like one for /api and another for /admin.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// This branch is only taken if the path starts with /hello
app.Map("/hello", branchApp =>
{
    // This sub-pipeline has its own terminal middleware.
    branchApp.Run(async context =>
    {
        await context.Response.WriteAsync("Hello from the /hello branch!");
    });
});

app.MapGet("/", () => "This is the main branch.");

app.Run();

Here, a request to /hello goes down a different path than a request to /.

Quick Takeaway

Here’s a simple way to remember them:

MethodPurposeAnalogyCalls next()?
UseProcess and pass along the request.A toll boothYes
RunProcess and end the request.A dead-end roadNo
MapBranch the pipeline by path.A fork in the roadN/A (creates a new pipeline)

Understanding this flow control is key to building clean and efficient ASP.NET Core applications. Happy coding!

FAQ

What is the purpose of Use in ASP.NET Core middleware?

Use adds middleware to the pipeline that can process requests and optionally call the next middleware using next(). It’s ideal for tasks like logging, authentication, or modifying requests and responses before passing them along.

How does Run differ from Use in middleware?

Run is a terminal middleware that ends the pipeline. Once a request hits a Run delegate, no further middleware is executed. Use it for scenarios where you want to fully handle and terminate the request.

What does Map do in ASP.NET Core middleware?

Map creates a branch in the middleware pipeline based on the request path. It allows you to define separate sub-pipelines for different URL segments, making it easy to organize logic for APIs, admin panels, or feature-specific routes.

Can you use multiple Use and Run methods in the same pipeline?

Yes, you can chain multiple Use methods, but only one Run should be used as it terminates the pipeline. Middleware after a Run will never be executed.

What happens if you forget to call next() in a Use middleware?

If you don’t call next(), the pipeline stops at that middleware. This can be intentional for short-circuiting, but often it’s a source of bugs where downstream middleware doesn’t execute.

When should you use Map instead of conditional logic inside middleware?

Use Map for cleanly separating logic by path, especially for large applications or APIs with distinct areas. It keeps your pipeline organized and avoids complex conditional statements inside middleware.

How does middleware order affect request processing?

Middleware executes in the order it’s registered. Placing middleware in the wrong order can cause issues with authentication, CORS, routing, or error handling.

Is it possible to nest Map branches?

Yes, you can nest Map calls to create deeply branched pipelines for complex routing scenarios. Each branch can have its own middleware chain and terminal handlers.

What are common mistakes with middleware registration?

Common mistakes include registering Run too early, forgetting to call next() in Use, or misplacing authentication and CORS middleware, leading to security or routing issues.

How can you debug middleware pipeline issues?

Use logging inside each middleware, tools like MiddlewareAnalysis, or breakpoints in the pipeline to trace request flow and identify where the pipeline stops or misbehaves.
See other aspnet-core posts