.NET Academy

Inside a backend server

The request lifecycle, middleware, statelessness, configuration, secrets and logging.

12 min read

You will learn

  • Describe the lifecycle of a request inside a server
  • Explain the middleware (pipeline) pattern
  • Explain why servers should be stateless and where state goes instead
  • Handle configuration, secrets and logging properly

What happens inside the server

When a request reaches your server, it goes through the same stages in nearly every framework — ASP.NET Core, Express, Django, Spring:

  1. Accept the connection and parse the HTTP request.
  2. Run it through a pipeline of middleware: logging, error handling, HTTPS redirection, authentication, CORS, compression…
  3. Route it: match method + path to a handler (GET /orders/{id} → GetOrder).
  4. Bind inputs: path values, query string, headers, JSON body → typed parameters, then validate them.
  5. Run your business logic: talk to the database, caches, other services.
  6. Serialize the result (usually JSON) and write the response, which travels back through the pipeline.

The middleware pattern

Each middleware is a small component that receives the request, can do something, then decides whether to call the next one:

// Conceptually, every middleware looks like this:
async Task Middleware(HttpContext context, Func<Task> next)
{
    // ... before: inspect the request, maybe reject it early ...
    await next();                 // pass control down the pipeline
    // ... after: inspect or modify the response ...
}

This makes cross-cutting concerns composable, and order matters: exception handling must wrap everything, authentication must run before authorization, static files should short-circuit before expensive work. Follow different requests through a real ASP.NET Core pipeline:

Interactive

ASP.NET Core middleware pipeline

Follow a request in through each middleware and the response back out

1/10
GET https://shop.example/api/orders
Authorization: Bearer eyJhbGci…
  1. 1Exception handler↓ request
  2. 2HTTPS redirection
  3. 3Static files
  4. 4Routing
  5. 5Authentication
  6. 6Authorization
  7. 7Endpoint

Program.cs

var app = builder.Build();
 
app.UseExceptionHandler("/error");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/api/orders", GetOrders).RequireAuthorization();
 
app.Run();

Middleware runs in the order it's registered. Each one can act, then call next() — or stop the request right there.

The request enters the pipeline. The exception handler wraps everything after it in a try/catch.

Stateless servers

In production you run several instances of your app behind a load balancer, and they come and go (deployments, autoscaling, crashes). So a server must not keep anything important only in its own memory:

StateWhere it should live
Business dataDatabase
Logged-in userA signed cookie/token, or a session in Redis/database
Uploaded filesObject storage (S3, Azure Blob), not the server's disk
Cached dataShared cache (Redis) — or a local cache that's safe to lose
Background workA queue, not a fire-and-forget task

A stateless server can be restarted or replaced at any moment without losing anything — the foundation of scaling (later in this track).

Configuration

The same build should run in development, testing and production with different settings. Keep configuration outside the code, layered by precedence:

appsettings.json                    ← defaults, committed
appsettings.Production.json         ← per-environment overrides
environment variables               ← set by the hosting platform
command-line arguments
{
  "ConnectionStrings": { "Shop": "Host=localhost;Database=shop" },
  "Payment": { "TimeoutSeconds": 10 }
}

An environment variable Payment__TimeoutSeconds=30 overrides the file (double underscore = nesting).

Secrets

Connection strings with passwords, API keys and signing keys are secrets. They must never be committed to source control.

  • Development: dotnet user-secrets set "Stripe:ApiKey" "sk_test_…" (stored outside the project folder).
  • Production: a secrets manager — Azure Key Vault, AWS Secrets Manager, HashiCorp Vault — or environment variables injected by the platform.
  • Rotate secrets regularly, and immediately if one leaks.

Logging

When something goes wrong in production, logs are often all you have. Good logs are:

  • Leveled — Trace, Debug, Information, Warning, Error, Critical; production usually records Information and above.
  • Structured — values are kept as named fields, not just baked into text:
logger.LogInformation("Order {OrderId} placed by {CustomerId} for {Total}", order.Id, customer.Id, order.Total);

Log platforms (Seq, Elastic, Application Insights, Grafana Loki) can then answer "show all errors for OrderId 42".

  • Correlated — a request/trace id ties together everything one request did, across services.
  • Safe — never log passwords, tokens or full credit card numbers.

Key takeaways

  • Requests flow through a pipeline: middleware → routing → binding/validation → logic → serialization.
  • Middleware can act before and after next(), or short-circuit; order matters.
  • Keep servers stateless; put state in databases, caches, object storage and queues.
  • Externalize configuration, keep secrets out of git, and log in a structured, leveled way.

Quiz

4 questions · 3 correct to pass
  1. 1.Why should web servers be stateless?

  2. 2.In a middleware pipeline, what can a middleware do?

  3. 3.Where should a production database password live?

  4. 4.What's the advantage of structured logging?

To complete this lesson

  • ○ Pass the quiz (3/4 correct)