One HTTP request
“What happens in ASP.NET Core between a request arriving and the response going back?”
The path one web request takes from the browser, through your app, and back.
See it
GET /api/loans/{id}: just text, with a method, a path and some headers.In plain words
A web request is a short text message. Kestrel, the web server inside your app, receives it. Then it walks down a line of small steps called middleware, in the order Program.cs lists them. Each step can do something on the way in and again on the way out. At the end, your controller runs, asks the service, and the service asks the repository. The answer travels back up the same line, in reverse, and leaves as JSON.
- Kestrel accepts the connection and creates an .
- The request then runs through the middleware pipeline: a chain of steps, each running code before and after
await next(context), in registration order on the way in and reverse order on the way out. - Routing matches an endpoint; the endpoint middleware runs the controller action, which uses model binding, calls services injected by DI and returns an serialized to JSON.
- Errors become ProblemDetails, via placed first.
In the lending API
Five files from the lending API, in the order a request meets them. Press Next line to walk through them.
Lending.sln · where these 5 files sit
- src/
- Api/
- Middleware/
- Controllers/Servicing/
- Errors/
- Middleware/
- Application/Servicing/Loans/
- Api/
Guid id. The controller calls the service.[ApiController] rules and JSON output.What they ask next
Interviewers check that you can explain every line. Tap a question to open it.
Explain this lineWhy is the first line?
It only catches exceptions thrown by steps after it. Put it last and an error in the logging middleware escapes it: in production the client gets a bare 500 with no body and no
traceId.So does order matter for every middleware?
Yes. Authentication must come before authorization, CORS before the endpoint, and so on. The middleware card covers the usual order.
Why not…Why didn't the 500 request leave a log line from ?
await next(context)threw, so the lines after it never ran. The exception handler logged the error instead. To log every request anyway, wrapnextintry/catch, log status 500, and rethrow.What happens if a middleware never calls
next?The request stops there. Nothing after it runs: no controller. This is called short-circuiting. It is useful on purpose, for example a health check or a rate limiter answering early.
Why not…, and : what is the difference?
adds a step that can call
next.Run(handler)adds a last step that never does. branches the line by path. The bareapp.Run()at the end of Program.cs is different: it just starts the server.Can middleware take a scoped service in its constructor?
No. A middleware class is built once for the whole app, so a scoped service there would be shared by every request. Take it as an extra parameter instead. See .
Where is ?
adds it for you at the start of the line, and runs the endpoint at the end. That is why every middleware can already see which action will run. Call yourself only when a step must run before matching, like URL rewriting.
Why not…Why return instead of throwing a ?
A missing loan is a normal answer, not a crash. Exceptions are slow, fill error logs with noise and set off alerts. Many teams still throw domain exceptions and map them in the handler. Say which you pick and why.
Why does
/api/loans/abcreturn an empty 404?{id:guid}makesabcmatch no route, so no controller runs and nothing writes a body. Addapp.UseStatusCodePages()to give such responses a ProblemDetails body too.Middleware or filter: what is the difference?
Middleware sees every request and knows nothing about controllers. A filter runs inside MVC, around one action, and can see its controller and arguments. Logging every request: middleware. Checking one action's input: filter.
Does each request get its own thread?
No. Many requests share a small pool of threads, and frees the thread while waiting. See .
Who creates and gives it ?
The DI container, once per request, inside that request's . See .
Traps
- Don't say
The controller is the first code that sees the request.SayKestrel, routing and every middleware see it first. The controller runs last, at the endpoint. - Don't say
Middleware order doesn't matter, it all runs anyway.SayIt runs in Program.cs order on the way in and in reverse on the way out. A step only affects what comes after it. - Don't say
Send the exception message back so the client knows what went wrong.SaySend ProblemDetails with atraceId. The stack trace stays in the logs, found by that id.
Interviews now include reviewing AI-written code. In a generated Program.cs, read the lines as a running order. Any order compiles, but an exception handler placed late misses every error thrown before it.