Back to Blog
GuidesAugust 10, 20265 min read

ASP.NET Core Middleware: What It Is, How It Works, and What Interviewers Actually Ask

Blog covers ASP.NET Core middleware from interview perspective — starts with the pipeline mental model, explains Use/Run/Map differences, covers custom middleware implementation (class-based), addresses the scoped services in middleware trap (constructor vs InvokeAsync injection), and ends with the actual follow-up questions interviewers ask (short-circuiting, exception handler order, middleware vs filters, correlation ID exercise). Positions Kiriti AI as the tool that pushes past first answers into follow-ups.

Share this article:

ASP.NET Core Middleware: What It Is, How It Works, and What Interviewers Actually Ask

Middleware is one of those ASP.NET Core topics that looks straightforward until an interviewer starts asking follow-up questions. Most developers can give a definition. Far fewer can explain the request pipeline clearly, write custom middleware correctly, or explain why order matters — which is exactly where most interviews go after the first answer.

Here's what you actually need to know, and what you should expect to be asked.

What Middleware Actually Is

Middleware is software that's assembled into the request pipeline to handle requests and responses. Every request that comes into an ASP.NET Core application passes through a series of middleware components in order — each one can either handle the request, pass it to the next component, or do both (handle something on the way in, then handle something else on the way back out).

The simplest mental model: middleware is a chain. A request enters at the top, passes through each link, hits the endpoint, and then the response travels back through the same chain in reverse order.

app.Use(async (context, next) =>
{
    // Do something before the next middleware
    await next.Invoke();
    // Do something after the next middleware has run
});

The next delegate is what passes control to the next middleware in the pipeline. If you don't call it, the pipeline short-circuits — nothing after that middleware runs. This is how authentication middleware works: if the request isn't authenticated, it returns a 401 and never calls next, so the actual endpoint never executes.

How the Request Pipeline Is Built

In Program.cs (or Startup.cs in older versions), middleware is registered in order using app.Use(), app.Run(), and app.Map(). The order you register them is the order they execute.

app.UseExceptionHandler("/error");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();

This is one of the most common interview questions, and the expected answer isn't just "you add middleware in Program.cs" — it's understanding why order matters.

UseAuthentication must come before UseAuthorization because you can't authorize a user before you've identified who they are. UseRouting must come before UseAuthorization because authorization decisions depend on knowing which endpoint was matched. UseExceptionHandler goes first because it needs to catch exceptions thrown by everything that comes after it.

Interviewers ask about order specifically to find out if you've thought about this, or if you've just copied a template.

The Difference Between Use, Run, and Map

This is almost always asked once the basics are covered:

app.Use() — adds middleware that can call the next component. This is the standard way to add middleware.

app.Run() — adds terminal middleware. It never calls next — the pipeline ends here. Using app.Run() in the middle of your pipeline means everything registered after it will never execute.

app.Map() — branches the pipeline based on the request path. Useful for creating separate middleware pipelines for different routes.

app.Map("/admin", adminApp =>
{
    adminApp.UseAdminAuthentication();
    adminApp.Run(async context => {
        await context.Response.WriteAsync("Admin area");
    });
});

Writing Custom Middleware

There are two ways to write custom middleware. Interviewers usually want to see the class-based approach because it's cleaner for anything non-trivial:

public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestLoggingMiddleware> _logger;

    public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        _logger.LogInformation($"Request: {context.Request.Method} {context.Request.Path}");
        
        await _next(context);
        
        _logger.LogInformation($"Response: {context.Response.StatusCode}");
    }
}

// Register it
app.UseMiddleware<RequestLoggingMiddleware>();

The pattern is always the same: constructor takes RequestDelegate next (and any other dependencies), InvokeAsync takes HttpContext, you call await _next(context) where you want to pass control forward.

A common follow-up: can you inject scoped services into middleware? The answer is nuanced. Middleware is registered as a singleton — it's created once. If you inject a scoped service through the constructor, you'll get a captive dependency problem (the scoped service lives as long as the singleton, which is wrong). The correct approach is to inject scoped services through the InvokeAsync method parameters instead, since ASP.NET Core resolves those per request.

public async Task InvokeAsync(HttpContext context, IMyService myService)
{
    // myService is resolved per request here, correctly
    await _next(context);
}

This specific point — scoped services in middleware — is one of the more advanced questions that separates candidates who've actually written middleware in production from those who've only read about it.

What Interviewers Are Actually Testing

The definition question ("what is middleware?") is just the entry point. The real questions come after:

  • "What happens if you forget to call next()?" — The pipeline short-circuits. Legitimate use cases (authentication, caching) but a common accidental bug.
  • "Why does exception handling middleware go first?" — Because it needs to catch exceptions thrown by everything downstream.
  • "How is middleware different from filters?" — Middleware operates at the HTTP pipeline level and handles all requests. Filters operate within the MVC framework and only apply to controller actions. Middleware can't access MVC-specific context (route data, action parameters). Filters can.
  • "How would you add a request correlation ID to every request and response?" — A classic custom middleware exercise: generate a GUID, add it to request headers, log it, and add it to the response headers. Tests whether you can actually implement what you just explained.

The Pattern Behind All of This

Middleware questions in ASP.NET Core interviews follow a consistent structure: they start with the definition, move to order/pipeline, then to custom implementation, then to a specific edge case (scoped services, short-circuiting, filters vs middleware). If you understand the request pipeline as a genuine mental model rather than a set of memorized facts, all of the follow-ups become answerable.

That shift — from memorized definition to actual mental model — is the same shift that separates candidates who pass from candidates who don't, across almost every technical interview topic.

Kiriti AI's adaptive mock interviews are built to push past the first answer and into the follow-ups — the exact questions that catch people off guard when they've studied the definition but never had to explain the reasoning out loud under pressure.

Try a free mock interview at kiritiai.com and find out how your ASP.NET Core explanations actually hold up when someone's pushing back.

Subscribe to our newsletter

Get notified when we publish new engineering resources, guides, and platform updates.