The middleware pipeline & useful logs
Understand request ordering and add diagnostics that help explain what your application is doing.
Here's what you'll be able to do.
- Trace middleware on the way in and out
- Add a response correlation identifier
- Write structured logs without recording sensitive input
Understand the MVC request flow and locate the pipeline in TaskBoard Program.cs.
Understand the idea
Middleware forms a chain around each request. A component can inspect the request, call the next component, then inspect the result. It can also short-circuit the chain. Order changes behavior: authentication must establish a principal before authorization evaluates it.
Useful diagnostics explain an operation without exposing the user’s secrets. TaskBoard logs the method, path, status, and elapsed time. It adds a request identifier header so a browser observation can be connected to a server log.
Choose your environment
Open TaskBoard.csproj from the example download. Install the ASP.NET and web development workload and a Visual Studio release supporting .NET 10. Select the TaskBoard launch profile and press F5. Use Solution Explorer to find the files named below.
Install the .NET 10 SDK and C# Dev Kit. Open the TaskBoard folder in VS Code. Run dotnet restore, then dotnet run in the integrated terminal. Browse http://localhost:5300. Open files with Ctrl+P (Cmd+P on macOS).
Read the pipeline in order
Static files can return a response before MVC runs. Routing selects endpoint metadata. Authentication establishes identity. Authorization enforces endpoint requirements. The mapped controller or page finally handles the request.
Code after await next runs as control returns outward through the chain. An exception can interrupt that return path unless an outer handler catches it. Put exception handling early enough to cover the operations you intend to handle.
app.UseExceptionHandler("/Home/Error"); // outside Development
app.UseHttpsRedirection();
app.UseStaticFiles();
// Request diagnostics go here.
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();Add timing and a response identifier
The stopwatch measures elapsed request work downstream from this component. OnStarting registers a callback before response headers are sent; this is the correct point to add a header even if later middleware starts the response.
The measurement is not a database-query duration or a browser rendering metric. Be precise about what a number represents before using it to diagnose performance.
app.Use(async (context, next) =>
{
var clock = System.Diagnostics.Stopwatch.StartNew();
context.Response.OnStarting(() =>
{
context.Response.Headers["X-Request-Id"] = context.TraceIdentifier;
return Task.CompletedTask;
});
await next(context);
app.Logger.LogInformation("{Method} {Path} returned {Status} in {Elapsed} ms",
context.Request.Method, context.Request.Path,
context.Response.StatusCode, clock.ElapsedMilliseconds);
});Use structured fields rather than prose alone
Named placeholders become useful dimensions in a structured log store. Filtering on Status or Path is more reliable than parsing a long interpolated sentence. Keep parameter names consistent so related events can be searched together.
Do not log passwords, authorization headers, reset tokens, or complete form bodies. Query strings can contain tokens too. The example logs Request.Path, not the full URL, and avoids logging posted form values.
logger.LogInformation(
"Updated task {TaskId} for request {RequestId}",
task.Id, HttpContext.TraceIdentifier);
// Avoid logging credentials, cookies, or private task notes.Inspect a real request
Request /health and inspect the response headers. Find X-Request-Id and compare the request with the application console. Then request a nonexistent route and observe the status.
For production, use a durable log sink, an appropriate retention policy, and distributed tracing when requests cross services. A console stream is convenient locally but can vanish with an ephemeral process. Add observability that answers real support questions rather than logging every variable.
curl -i http://localhost:5300/health
# Inspect HTTP status and X-Request-Id in the response headers.Make it your own.
Add RequestId to the structured completion log template. Make a request and verify you can find the same identifier in the response header and server log.
See the solution & reasoning
Add a {RequestId} placeholder and pass context.TraceIdentifier in the corresponding position. Keep the template constant so the logging provider can preserve the named field. Use the response header to locate that specific event.
When something doesn't work
“Headers are read-only, response has already started” means you tried to mutate headers too late. Use OnStarting before awaiting the next middleware.
A missing log may reflect log-level filtering rather than code that never ran. Inspect Logging configuration.
Sensitive values can appear in URLs as well as request bodies. Exclude token-bearing routes and avoid logging raw query strings.
Why register a response header with OnStarting?
That's another skill in your toolkit.
Your progress is saved in this browser. Sign in to save completion to your account.