🔥 Zapisy zamknięte, ale możesz pobrać Roadmapę .NET i dołączyć do listy oczekujących — Pobierz i dołącz do Listy VIP →

Konfiguracja w ASP.NET Core — appsettings, Options i sekrety

Konfiguracja w ASP.NET Core — warstwy appsettings, User Secrets i wzorzec Options

Twoje API potrzebuje ustawień: connection string do bazy, klucz do bramki płatności, adres zewnętrznej usługi, limit rozmiaru pliku. Trzy pytania decydują, czy zrobisz to dobrze: gdzie te wartości trzymać, jak je czytać w kodzie i — najważniejsze — jak nie wrzucić hasła do bazy na publiczne repozytorium GitHub. ASP.NET Core ma na to spójny system konfiguracji; ten post pokazuje, jak z niego korzystać, nie popełniając klasycznych błędów bezpieczeństwa.

Warstwy konfiguracji — i kto kogo nadpisuje

Konfiguracja w ASP.NET Core to nie jeden plik, lecz warstwy ładowane po kolei, gdzie późniejsza nadpisuje wcześniejszą. Domyślna kolejność:

KolejnośćŹródłoTypowe zastosowanie
1appsettings.jsonWartości bazowe, wspólne dla wszystkich
2appsettings.{Environment}.jsonNadpisania per środowisko (Development/Production)
3User SecretsSekrety w czasie developmentu (poza projektem)
4Zmienne środowiskoweSekrety i nadpisania na serwerze
5Argumenty wiersza poleceńDoraźne nadpisanie przy starcie

Ten porządek to projekt, nie przypadek: bazowe appsettings.json commitujesz do repo, a wrażliwe i środowiskowe wartości wstrzykujesz wyżej w łańcuchu (zmienne środowiskowe na produkcji), gdzie nadpiszą to, co w pliku. Ten sam mechanizm warstw widziałeś już przy middleware — kolejność decyduje o wyniku.

// appsettings.json — bazowo
{ "Platnosci": { "Waluta": "PLN", "TimeoutSekundy": 30 } }
// appsettings.Development.json — nadpisanie w dev
{ "Platnosci": { "TimeoutSekundy": 5 } }

W środowisku Development Waluta = PLN (z bazy), a TimeoutSekundy = 5 (nadpisane).

Odczyt — od IConfiguration do wzorca Options

Najprościej sięgnąć po IConfiguration, ale to podejście „stringly-typed” — literówka w kluczu daje null, nie błąd:

var waluta = builder.Configuration["Platnosci:Waluta"];          // "PLN" albo null
var timeout = builder.Configuration.GetValue<int>("Platnosci:TimeoutSekundy");

Lepszy jest wzorzec Options: mapujesz sekcję konfiguracji na klasę C# i wstrzykujesz ją tam, gdzie jej potrzebujesz.

public class PlatnosciOptions
{
    public string Waluta { get; set; } = "PLN";
    public int TimeoutSekundy { get; set; }
}

// rejestracja — wiążemy sekcję "Platnosci" z klasą
builder.Services.Configure<PlatnosciOptions>(
    builder.Configuration.GetSection("Platnosci"));

Od teraz ustawienia wstrzykujesz przez DI jako silnie typowany obiekt — z autouzupełnianiem i kontrolą typów zamiast magicznych stringów:

public class BramkaPlatnosci(IOptions<PlatnosciOptions> options)
{
    private readonly PlatnosciOptions _cfg = options.Value;

    public void Zaplac() => Console.WriteLine(
        $"Płatność w {_cfg.Waluta}, timeout {_cfg.TimeoutSekundy}s");
}

Konfiguracja przez IConfiguration["..."] to stringi, które kompilator ignoruje. Wzorzec Options zamienia je w klasę — literówka staje się błędem kompilacji, a nie cichym null na produkcji.

Sekrety — nigdy w appsettings.json commitowanym do gita

Najważniejsza reguła całego tematu: appsettings.json trafia do repozytorium, więc nie może zawierać sekretów. Hasło do bazy, klucz API czy token wrzucone do tego pliku wyciekają do historii gita w chwili pierwszego commita — i zostają tam, nawet gdy je później usuniesz.

W developmencie służą do tego User Secrets — plik trzymany poza katalogiem projektu (w profilu użytkownika), którego git nigdy nie widzi:

dotnet user-secrets init
dotnet user-secrets set "Platnosci:KluczApi" "sk_test_abc123"

Odczytujesz je dokładnie tak samo jak resztę konfiguracji — warstwa User Secrets wstrzykuje się automatycznie w środowisku Development.

Na produkcji sekrety podajesz przez zmienne środowiskowe (albo magazyn typu Azure Key Vault). Zagnieżdżenie zapisujesz podwójnym podkreśleniem:

Platnosci__KluczApi=sk_live_xyz789

Ta sama nazwa klucza, inne źródło — kod się nie zmienia. To właśnie po to istnieją warstwy.

Środowiska — jeden build, różne zachowania

Aktywne środowisko ustala zmienna ASPNETCORE_ENVIRONMENT (Development, Staging, Production). Odczytasz je z app.Environment i uzależnisz od niego zachowanie:

if (app.Environment.IsDevelopment())
    app.MapOpenApi();          // dokumentacja API tylko lokalnie

Dzięki temu ten sam artefakt wdrażasz na każde środowisko — różnicę robi konfiguracja, nie osobny build.

Pułapki konfiguracji

Pułapka #1 — sekret w repo. Connection string z hasłem w appsettings.json to najczęstszy wyciek juniora. Raz zacommitowany, żyje w historii gita na zawsze. Sekrety → User Secrets (dev) i zmienne środowiskowe (prod), nigdy do pliku pod kontrolą wersji.

Pułapka #2 — IOptions nie widzi zmian. IOptions<T> jest singletonem — wartość wczytuje raz przy starcie. Jeśli chcesz, by zmiana w appsettings.json była widoczna bez restartu, użyj IOptionsSnapshot<T> (przeładowywany per żądanie) albo IOptionsMonitor<T>. Mylenie ich to godziny debugowania „czemu nowa wartość się nie łapie”.

Pułapka #3 — brakująca sekcja daje ciche zera. GetSection("Platnosci") na nieistniejącej sekcji nie rzuca wyjątku — zwraca obiekt z domyślnymi wartościami (0, null). Wymuś kontrolę już przy starcie:

builder.Services.AddOptions<PlatnosciOptions>()
    .Bind(builder.Configuration.GetSection("Platnosci"))
    .Validate(o => o.TimeoutSekundy > 0, "TimeoutSekundy musi być dodatni")
    .ValidateOnStart();   // błąd przy starcie, nie przy pierwszym żądaniu

Pułapka #4 — connection string wpisany w kodzie. UseSqlite("Data Source=...") z zaszytą wartością działa, dopóki nie musisz jej zmienić per środowisko. Czytaj ją z konfiguracji (GetConnectionString("Domyslny")), a wartość produkcyjną podawaj zmienną środowiskową.

Podsumowanie

Konfiguracja w ASP.NET Core to warstwy — od bazowego appsettings.json, przez plik per środowisko, po User Secrets i zmienne środowiskowe — gdzie każda wyższa nadpisuje niższą. W kodzie sięgasz po wzorzec Options (IOptions<T>) zamiast stringów z IConfiguration, żeby literówka była błędem kompilacji, nie cichym null. Żelazna zasada bezpieczeństwa: sekrety nigdy nie trafiają do plików pod kontrolą wersji — w developmencie idą do User Secrets, na produkcji do zmiennych środowiskowych. Reszta (środowiska, walidacja ValidateOnStart) to warianty tego samego, spójnego systemu.

Co dalej

Masz komplet fundamentów Etapu 12: HTTP, pierwsze API, routing, middleware i konfigurację. Teraz zobacz, jak spiąć to w utrzymywalną całość w Clean Architecture w .NET 10 — REST API — gdzie konfiguracja, dostęp do bazy i logika biznesowa trafiają do osobnych warstw za wyraźnymi granicami.

👨‍💻
Mariusz Jurczenko
Senior .NET Developer · 10+ lat doświadczenia komercyjnego

Programista .NET z doświadczeniem komercyjnym w firmach takich jak NFZ, Kamsoft, Diagnostyka, Hermes Reply Polska czy Etisoft Smart Solutions. Twórca kursów, z których skorzystało już ponad 11 000 osób w Strefie Kursów i ponad 1 000 kursantów na dev-hobby.pl.

Specjalizacja: Clean Code, Clean Architecture i uczenie programowania tak, żeby dało się je naprawdę zrozumieć — nie wykuć.

🚀 Co dalej?

Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.

Dodaj komentarz

czytanie to początek

Zamień wiedzę w umiejętności

Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.

Pobieram roadmapę →