appsettings.json is the committed baseline configuration file for an ASP.NET Core application. It uses standard JSON with a nested structure that maps directly to the hierarchical configuration key system. Understanding how to structure complex configuration objects, bind them to C# classes, and work with arrays and connection strings is the practical foundation for all configuration work in the Web API chapters.
appsettings.json Structure and Binding
// ── appsettings.json ───────────────────────────────────────────────────────
// {
// "ConnectionStrings": {
// "Default": "Server=(localdb)\\mssqllocaldb;Database=BlogApp;Trusted_Connection=True",
// "Redis": "localhost:6379"
// },
// "JwtSettings": {
// "Issuer": "https://api.blogapp.com",
// "Audience": "https://blogapp.com",
// "SecretKey": "REPLACE_IN_PRODUCTION_WITH_SECURE_VALUE",
// "ExpiryMinutes": 60
// },
// "Cors": {
// "AllowedOrigins": [
// "http://localhost:4200",
// "https://blogapp.com"
// ]
// },
// "Logging": {
// "LogLevel": {
// "Default": "Information",
// "Microsoft.AspNetCore": "Warning",
// "Microsoft.EntityFrameworkCore.Database.Command": "Warning"
// }
// }
// }
// ── Strongly-typed binding with GetSection().Get() ────────────────────────
public class JwtSettings
{
public string Issuer { get; init; } = string.Empty;
public string Audience { get; init; } = string.Empty;
public string SecretKey { get; init; } = string.Empty;
public int ExpiryMinutes { get; init; } = 60;
}
// In Program.cs — bind and validate
builder.Services
.AddOptions<JwtSettings>()
.BindConfiguration("JwtSettings")
.ValidateDataAnnotations()
.ValidateOnStart();
// Or as a one-liner (no validation):
var jwtSettings = builder.Configuration
.GetSection("JwtSettings")
.Get<JwtSettings>() ?? throw new InvalidOperationException("JwtSettings not configured.");
// ── Binding arrays ─────────────────────────────────────────────────────────
string[] allowedOrigins = builder.Configuration
.GetSection("Cors:AllowedOrigins")
.Get<string[]>() ?? Array.Empty<string>();
GetSection("JwtSettings").Get<JwtSettings>() returns null if the section does not exist in any loaded configuration source. Always use the null-coalescing operator with a descriptive exception to fail fast: ?? throw new InvalidOperationException("JwtSettings section is missing"). The IOptions pattern with ValidateOnStart() is better for required configuration because it validates at startup, but for one-off bootstrapping in Program.cs the direct binding approach is acceptable.appsettings.json to match your options classes — one top-level section per configuration concern. Use PascalCase for section names to match C# property naming conventions. Keep the base appsettings.json complete but safe for development: use non-sensitive placeholder values for secrets ("SecretKey": "REPLACE_IN_PRODUCTION"), localhost URLs, and LocalDB connection strings. Production values arrive through environment variables or vault providers.appsettings.json or appsettings.Production.json. Even if the repository is private today, it may become public, be cloned to a developer machine, or be accessed by someone who does not need production access. Rotate any secret that has been committed to source control immediately. Use a .gitignore rule to prevent appsettings.Production.json from ever being committed: appsettings.Production.json in .gitignore.appsettings.Development.json
// appsettings.Development.json — overrides for local development
// This file IS committed to source control (non-sensitive dev settings)
// {
// "Logging": {
// "LogLevel": {
// "Default": "Debug",
// "BlogApp": "Debug",
// "Microsoft.EntityFrameworkCore.Database.Command": "Information"
// }
// },
// "ConnectionStrings": {
// "Default": "Server=(localdb)\\mssqllocaldb;Database=BlogApp_Dev;Trusted_Connection=True"
// },
// "JwtSettings": {
// "SecretKey": "dev-only-secret-key-replace-in-production-12345"
// }
// }
// When ASPNETCORE_ENVIRONMENT=Development:
// - appsettings.json loads first (base values)
// - appsettings.Development.json loads second (overrides logging level, DB, JWT key)
// - User Secrets load third (overrides anything else for the developer)
// Net result: developer-friendly settings without touching production config
Common Mistakes
Mistake 1 — Committing appsettings.Production.json with real secrets
❌ Wrong — real API keys, connection strings, JWT secrets in source control.
✅ Correct — only non-sensitive defaults in appsettings.json; secrets via environment variables or vault.
Mistake 2 — Using GetSection without checking for null
❌ Wrong — .Get<T>() returns null when section is missing; downstream NullReferenceException:
✅ Correct — always null-check or use IOptions with ValidateOnStart().