Skip to content

Expert · Web APIs with ASP.NET Core and EF Core · topic 34 of 43 · 30 min

Configuration, secrets and the middleware pipeline

Explanation

Settings live outside code: in appsettings.json, appsettings.Development.json, environment variables and, during development, user-secrets, with later sources overriding earlier ones. Secrets such as passwords and API keys must never be committed to Git. Every request passes through a pipeline of middleware components (error handling, HTTPS redirection, authentication, authorization and your own), in the order you add them.

Why configuration?

The same code runs on your laptop, a test server and the live server, but each needs different settings: database address, payment gateway keys, log level. Hard-coding them means changing code for every environment and leaking secrets. Configuration keeps settings outside the code.

appsettings.json

json
{
  "Logging": { "LogLevel": { "Default": "Information" } },
  "ConnectionStrings": {
    "Shop": "Data Source=shop.db"
  },
  "Sms": {
    "Sender": "SHOPIN",
    "MaxPerMinute": 30
  }
}
Nested sections are read with a colon: Sms:Sender. Do not put real passwords or keys here if this file is committed.
csharp
string? sender = builder.Configuration["Sms:Sender"];
int max = builder.Configuration.GetValue<int>("Sms:MaxPerMinute");
string? conn = builder.Configuration.GetConnectionString("Shop");   // reads ConnectionStrings:Shop
Reading values directly from IConfiguration.

Where values come from, and who wins

The default builder reads several sources. If the same key appears in more than one, the source read later wins.

  1. appsettings.json
  2. appsettings.{Environment}.json, for example appsettings.Development.json
  3. User secrets (only in the Development environment)
  4. Environment variables
  5. Command-line arguments

The environment name comes from the ASPNETCORE_ENVIRONMENT variable: usually Development, Staging or Production. If it is not set, it is Production. Locally, Properties/launchSettings.json sets it to Development for dotnet run.

bash
# Linux / macOS
export ConnectionStrings__Shop="Host=db;Database=shop;Username=app;Password=..."
# Windows PowerShell
$env:ConnectionStrings__Shop = "..."
In environment variable names, a double underscore __ stands for the colon, because some systems do not allow colons in names.

Secrets in development: user-secrets

bash
dotnet user-secrets init
dotnet user-secrets set "Sms:ApiKey" "test-key-from-provider-dashboard"
dotnet user-secrets list
User secrets are stored in your user profile folder, outside the project, so they never get committed. They are for development only; they are not encrypted and not used in production.
Careful: Never commit passwords, connection strings with passwords, API keys or tokens to Git, even in a private repository. Bots scan public repositories for keys within minutes. In production, use environment variables set by the hosting platform or a secrets vault (such as Azure Key Vault). If a secret is ever committed, treat it as leaked: rotate it (create a new one and disable the old one).

The options pattern: typed settings

csharp
builder.Services.Configure<SmsOptions>(builder.Configuration.GetSection("Sms"));

public class SmsOptions
{
    public string Sender { get; set; } = "";
    public int MaxPerMinute { get; set; }
    public string ApiKey { get; set; } = "";
}

public class SmsSender(IOptions<SmsOptions> options)
{
    private readonly SmsOptions _o = options.Value;
    public string Describe() => $"{_o.Sender}, max {_o.MaxPerMinute}/min";
}
Bind a section to a class and inject IOptions<T> (from Microsoft.Extensions.Options). Typed settings avoid typos in string keys.

Middleware: the request pipeline

Middleware is a component that sits in the path of every request. Each one can do something before passing the request to the next one, and something after the response comes back. Together they form the pipeline. The order you write them in Program.cs is the order they run.

Think of it like this: The pipeline is like airport security. You pass the ticket check, then ID check, then bag scan, then boarding. Each counter can stop you (short-circuit) or let you through to the next. Put the ID check after boarding and the system makes no sense, which is why order matters.
csharp
var app = builder.Build();

if (!app.Environment.IsDevelopment())
    app.UseExceptionHandler("/error");   // catch crashes, return a safe error

app.UseHttpsRedirection();               // send http:// to https://
app.UseAuthentication();                 // who are you?
app.UseAuthorization();                  // are you allowed?

app.Use(async (context, next) =>        // your own middleware
{
    var watch = System.Diagnostics.Stopwatch.StartNew();
    await next(context);                 // run the rest of the pipeline
    Console.WriteLine($"{context.Request.Method} {context.Request.Path} -> {context.Response.StatusCode} in {watch.ElapsedMilliseconds} ms");
});

app.MapGet("/", () => "Hello");
app.Run();
Code before await next runs on the way in; code after runs on the way out. Not calling next short-circuits: the request stops here.

Common mistakes and fixes

  • UseAuthorization before UseAuthentication: the user is not identified yet, so authorization fails. Authentication first.
  • Committing appsettings.Development.json with real keys: move them to user-secrets and rotate the leaked keys.
  • Typos in configuration keys: builder.Configuration["Sms:Sendr"] silently returns null. Prefer the options pattern.
  • Writing to the response after calling next when the response has already started: causes an error. Log, do not modify, after next.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Configuration, secrets and the middleware pipeline · 1/7

Configuration and middleware

In this lesson you will keep settings out of your code and understand how each request flows through middleware.

Prefer a video by a teacher? Find free videos on “ASP.NET Core configuration appsettings user secrets middleware pipeline” on YouTube ↗

In real life: A payment API that behaves differently in each environment

A fintech startup in Bengaluru runs the same payment API code in Development (fake gateway, detailed errors), Staging (sandbox gateway) and Production (live gateway, safe errors). Only configuration changes: appsettings files hold non-secret settings, user-secrets hold the developer's sandbox key, and production keys are environment variables set by the hosting platform.

csharp
// appsettings.json (committed): no secrets
// { "Payments": { "BaseUrl": "https://sandbox.example-gateway.com", "TimeoutSeconds": 10 } }

// production server environment variables (NOT in Git):
// Payments__BaseUrl=https://api.example-gateway.com
// Payments__ApiKey=<set in the hosting dashboard>

builder.Services.Configure<PaymentOptions>(builder.Configuration.GetSection("Payments"));

public class PaymentOptions
{
    public string BaseUrl { get; set; } = "";
    public string ApiKey { get; set; } = "";
    public int TimeoutSeconds { get; set; } = 10;
}
Environment variables override appsettings.json, so production uses the live URL and key without any code change. The gateway names are placeholders.

Practice (0/4 done)

Task 1 · Question

The key Sms:Sender is "A" in appsettings.json and the environment variable Sms__Sender is "B". What value does the app see?

Task 2 · Question

Where should a developer keep a payment sandbox API key on their laptop?

Task 3 · Question

Which order is correct?

Task 4 · Practice

In a webapi project: add a "Shop" section to appsettings.json with Name and SupportPhone, bind it with the options pattern, and expose GET /info that returns them. Store a fake "Shop:ApiKey" with user-secrets and confirm it is readable but not in any file in your project folder. Add a middleware that adds a response header X-Elapsed-Ms with the time taken.

Quick quiz

Question 1

1.What is the default environment if ASPNETCORE_ENVIRONMENT is not set?

Question 2

2.In environment variable names, what replaces the colon of nested keys?

Question 3

3.A secret key was committed to Git by mistake. What should you do?

Question 4

4.What happens if a middleware does not call next?

Question 5

5.Are user secrets meant for production?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What is the default environment if ASPNETCORE_ENVIRONMENT is not set?

Answer: Production

Unset means Production, the safe default.

In environment variable names, what replaces the colon of nested keys?

Answer: A double underscore __

ConnectionStrings__Shop maps to ConnectionStrings:Shop.

A secret key was committed to Git by mistake. What should you do?

Answer: Rotate the key (issue a new one and revoke the old one)

Assume it leaked; rotating is the only safe fix.

What happens if a middleware does not call next?

Answer: The request stops there (short-circuit) and later middleware and endpoints do not run

Short-circuiting is used for things like returning cached responses.

Are user secrets meant for production?

Answer: No, they are a development-only convenience and are not encrypted

Production should use environment variables or a vault.

Where should the error-handling middleware be added?Related

Answer: After all routes, at the end

Errors from routes travel down the chain, so the error handler must come after them.

From Middleware: checks before your handler

Where should a production database password live?Related

Answer: In the hosting platform's environment settings or a secret manager

Environment settings and secret managers keep the value out of code and control who can read it.

From Keeping secrets safe

A session secret was pushed to a public GitHub repo. What fixes the problem?Related

Answer: Rotate: make a new secret, deploy it, and stop using the old one

The old value stays in git history and may already be copied. Only replacing it makes it useless.

From Environment variables and secrets

Why wrap http.ResponseWriter in a logging middleware?Related

Answer: To remember the status code the handler wrote

ResponseWriter has no method to read back the status.

From Middleware: logging, recovery and request IDs

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 1 more topic in “Web APIs with ASP.NET Core and EF Core” before its exam: Entity Framework Core: databases with C#.
  2. Module 8 exam: Web APIs with ASP.NET Core and EF Core · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Testing, tooling and Unity. 43 topics left in the course before the final exam.

← Previous: Dependency injection and service lifetimes