Previously, in Part 4, you sharpened your C# with collections and LINQ. Now we climb one layer up the stack, to where most .NET interviews spend most of their time: ASP.NET Core and Web APIs. These four questions show up in almost every loop, and each one has a version of the answer that makes an interviewer sit up straight.

The four questions: the middleware pipeline (an ordered chain a request passes through on the way in and back out, where order matters and any middleware can short-circuit), DI lifetimes (singleton for the app, scoped per request, transient per resolution — plus the captive dependency trap), REST design (nouns in URLs, HTTP methods as verbs, honest status codes), and how JWT authentication works (a signed header.payload.signature token that is encoded, not encrypted, so the server can verify it without storing a session).

"Walk me through the middleware pipeline."

Why they ask it: Anyone can scaffold a Web API. This question checks whether you know what actually happens between the request arriving and your controller running. If you understand the pipeline, you can debug the weird stuff: mysterious 401s, CORS errors, exception handlers that never fire.

A strong answer: The pipeline is an ordered chain of components. A request enters at the top, passes through each middleware on the way in, hits a terminal endpoint, and then the response travels back out through the same chain in reverse — like layers of an onion. Each middleware can do work before calling the next one, do work after it returns, or short-circuit entirely and never call the rest. That short-circuiting is exactly how authentication returns a 401 without ever reaching your endpoint.

var app = builder.Build();

// Runs on the way IN and again on the way OUT
app.Use(async (context, next) =>
{
    Console.WriteLine($"In:  {context.Request.Path}");
    await next();
    Console.WriteLine($"Out: {context.Response.StatusCode}");
});

// Terminal middleware — nothing registered after this will run
app.Run(async context =>
{
    await context.Response.WriteAsync("Hello from the end of the pipeline!");
});

Then land the key point: order matters. Authentication must run before authorization (you have to know who someone is before checking what they may do), and both must run before your endpoints. Exception-handling middleware goes first, so it wraps everything below it.

Try it: Create a minimal API, add two app.Use middlewares that each log a line before and after await next(), and watch the console. The nesting order you see is the onion in action — you will never forget it after seeing it once.

Follow-ups to expect:

  • "What happens if a middleware never calls next()?" — It short-circuits; everything below it is skipped.
  • "Why must UseAuthentication come before UseAuthorization?" — Identity must be established before policies can be checked.
  • "Where do you put exception-handling middleware, and why?" — First in the chain, so it catches exceptions from everything after it.

Red flag: Reciting a memorized list of middleware names in order without being able to say why the order matters. If you can explain one concrete failure caused by wrong ordering, you are ahead of most candidates.

"Explain DI lifetimes: singleton, scoped, transient."

Why they ask it: This is the classic ASP.NET Core question, because getting a lifetime wrong produces bugs that only appear under load — shared state leaking between users, or a DbContext being hammered by multiple threads. Interviewers want to know you won't plant that landmine.

A strong answer: Keep it in plain words first, then add the trap.

DI lifetimes in plain words
LifetimeIn plain wordsTypical use
SingletonOne instance for the whole application, foreverCaches, configuration readers
ScopedOne instance per HTTP requestDbContext, unit-of-work services
TransientA brand-new instance every single time it is requestedLightweight, stateless helpers

Then mention that EF Core's DbContext is registered as scoped for a reason: it is not thread-safe, and one-per-request keeps each request's changes isolated.

Try it: Write a tiny service whose constructor sets a Guid property. Register it three times under three interfaces — one per lifetime — inject all three into an endpoint twice, and compare the Guids across two requests. Ten minutes, and lifetimes click permanently.

Follow-ups to expect:

  • "What lifetime is DbContext and why?" — Scoped; it is stateful and not thread-safe.
  • "What happens if a singleton depends on a scoped service?" — See the red flag below; in development, scope validation throws to save you.
  • "When is transient the wrong choice?" — When the service is expensive to construct or holds resources you want reused.

Red flag: Not knowing the captive dependency trap. If a singleton takes a scoped service in its constructor, that "scoped" instance is captured and lives as long as the singleton — one DbContext quietly shared by every request in the app. Name this trap unprompted and you sound like someone who has debugged it at 2 a.m.

"How would you design a clean REST API for appointments?"

Why they ask it: This tests taste, not trivia. Interviewers have inherited APIs full of /getAppointments and /doCancel endpoints, and they are checking whether you will add to that pile. (If REST is still fuzzy, our pizza shop explainer builds it from scratch.)

A strong answer: URLs are nouns, HTTP methods are the verbs, and status codes tell the story. For a clinic's appointments:

A clean appointment API surface
Method and URLMeaningTypical responses
GET /api/v1/appointmentsList (with paging query params)200
GET /api/v1/appointments/42Fetch one200, 404
POST /api/v1/appointmentsBook a new appointment201 + Location header, 400, 409
PUT /api/v1/appointments/42Update it204, 404
DELETE /api/v1/appointments/42Remove it204, 404

The 409 Conflict on POST is the detail that impresses: if the slot was just taken, say so with the right code instead of a vague 400. For versioning, one sentence is enough: "I'd put v1 in the URL because it's the most discoverable option, and plan versions from day one because retrofitting them hurts."

Try it: Sketch the endpoint for canceling an appointment. Is it a DELETE, or is a cancellation really a status change you might report on later? Write down your reasoning — this exact debate comes up in interviews, and either answer works if you can defend it.

Follow-ups to expect:

  • "Cancel: DELETE or state change?" — Usually a state change (PATCH or a POST to /cancel) because the business cares about canceled appointments.
  • "What do you return when the slot is already booked?" — 409 Conflict with a helpful body.
  • "How do you page the list endpoint?" — Query parameters with sensible defaults and a maximum page size.

Red flag: Verbs in every URL and 200 for every outcome, including errors with a "success": false body. That tells the interviewer you have used APIs but never had to live with one.

"How does JWT authentication actually work?"

Why they ask it: Almost every modern .NET API uses token auth, and almost every candidate says "the token proves who you are" and stops. The question separates people who have configured JWT from people who understand it.

A strong answer: A JWT is three Base64Url-encoded parts joined by dots: header.payload.signature. The header names the signing algorithm, the payload carries claims (user id, roles, expiry), and the signature is computed by the server over the first two parts using a secret key. When a request arrives, the server recomputes the signature and compares — if anything in the payload was tampered with, the signatures won't match. Crucially, the payload is encoded, not encrypted: anyone can read it, they just can't forge it. Because the token itself carries the proof, the server stores no session — that's what makes JWT auth stateless and easy to scale. Since tokens can't easily be revoked, keep them short-lived and use a refresh token to mint new ones.

Finish with storage, because it's the follow-up anyway: in the browser, avoid localStorage — any XSS payload can read it. Prefer an httpOnly, secure cookie, which script cannot touch.

Try it: Generate a token in a sample app and decode its first two parts (any Base64Url decoder works). Seeing your own claims sitting there in plain text is the fastest cure for "I'll just put the user's data in the token."

Follow-ups to expect:

  • "How do you log someone out if the server is stateless?" — You mostly can't revoke a live token; use short expiry plus refresh-token rotation, or a denylist if you must.
  • "HS256 vs RS256?" — Symmetric shared secret vs asymmetric key pair; RS256 lets other services validate without holding the signing key.
  • "What claim stops an expired token?" — The exp claim, validated on every request.

Red flag: "JWTs are encrypted, so it's safe to put anything in them." They are not encrypted, and secrets in a payload are secrets published. This one sentence has ended interviews.

Frequently asked

How does the ASP.NET Core middleware pipeline work?
The pipeline is an ordered chain of components: a request passes through each middleware on the way in, hits a terminal endpoint, and the response travels back out through the same chain in reverse, like layers of an onion. Each middleware can do work before calling the next one, after it returns, or short-circuit and never call the rest, which is how authentication returns a 401 without reaching your endpoint. Order matters: exception handling goes first, and authentication must run before authorization.
What is the difference between singleton, scoped, and transient in ASP.NET Core dependency injection?
Singleton is one instance for the whole application, used for caches and configuration readers; scoped is one instance per HTTP request, used for DbContext and unit-of-work services; transient is a brand-new instance every time it is requested, for lightweight stateless helpers. DbContext is scoped because it is not thread-safe. The trap is the captive dependency: a singleton that takes a scoped service in its constructor keeps that instance for the life of the app.
How do you design a clean REST API in ASP.NET Core?
URLs are nouns, HTTP methods are the verbs, and status codes tell the story: GET a collection or a single item (200 or 404), POST to create (201 with a Location header, 400, or 409 Conflict when the slot was just taken), PUT to update and DELETE to remove (204 or 404). Put a version like v1 in the URL from day one, page list endpoints with query parameters and a maximum page size, and model a cancellation as a state change rather than a DELETE when the business cares about canceled records.
How does JWT authentication work?
A JWT is three Base64Url-encoded parts joined by dots: a header naming the signing algorithm, a payload of claims such as user id, roles, and expiry, and a signature the server computes over the first two parts with a secret key. On each request the server recomputes the signature and compares, so tampering is detected, and no session is stored, which makes it stateless. The payload is encoded, not encrypted, so never put secrets in it; keep tokens short-lived with refresh tokens, and store them in an httpOnly secure cookie rather than localStorage.

Next up: the data round

You can now hold your own on the web layer — pipeline, lifetimes, API design, and auth. But every .NET interview eventually turns to the database, because that's where real applications win or lose. In Part 6, we tackle SQL and EF Core: indexes, the N+1 problem, and the production-slowness question that quietly checks whether you think like a senior.