TGViewer
Библиотека шарписта | C#, F#, .NET, ASP.NET Библиотека шарписта | C#, F#, .NET, ASP.NET @csharpproglib · 21.7K subscribers
Post #6901 3.48K
🧑‍💻 Пусть приложение падает при старте, а не в 2 часа ночи

Представьте сценарий. Платёжный сервис ушёл в прод. Конфиг собран наспех, API-ключ не тот, URL без HTTPS. Всё тихо до первой реальной транзакции. Потом звонок в ночь, инцидент, откат.

Паттерн Options с валидацией на старте решает именно эту проблему.

Вместо того чтобы читать конфиг в рантайме и падать где попало, мы проверяем всё один раз при запуске. Если что-то не так, то приложение не поднимается вообще. Это лучше, чем ловить NPE или невалидный URL в середине бизнес-логики.

Шаг первый. Описываем класс опций с атрибутами валидации:
public sealed class PaymentGatewayOptions
{
[Required(ErrorMessage = "API Key is required - check your key")]
[StringLength(100, MinimumLength = 20)]
public string ApiKey { get; set; } = string.Empty;

[Required]
[Range(1, 10, ErrorMessage = "Retry count must be between 1 and 10")]
public int MaxRetries { get; set; } = 3;

[Required]
[RegularExpression(@"^https://", ErrorMessage = "Base URL must use HTTPS")]
public string BaseUrl { get; set; } = string.Empty;

[Required]
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30);
}


Шаг второй. Регистрируем в Program.cs с вызовом ValidateOnStart():
builder.Services.AddOptions<PaymentGatewayOptions>()
.BindConfiguration("PaymentGateway")
.ValidateDataAnnotations()
.ValidateOnStart(); // упадёт при старте, если конфиг невалиден


Шаг третий. Используем в сервисе через IOptions<T>:
public class PaymentService
{
private readonly PaymentGatewayOptions _options;

public PaymentService(IOptions<PaymentGatewayOptions> options)
{
_options = options.Value;
}

public async Task ProcessPaymentAsync(decimal amount)
{
using var client = new HttpClient
{
BaseAddress = new Uri(_options.BaseUrl),
Timeout = _options.Timeout
};

client.DefaultRequestHeaders.Add("X-API-Key", _options.ApiKey);

for (int i = 0; i < _options.MaxRetries; i++)
{
try { /* логика запроса */ }
catch { if (i == _options.MaxRetries - 1) throw; }
}
}
}


Паттерн не новый, но cтоит использовать везде, где конфиг критичен для работы сервиса.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#sharp_view
  • 👍 26
More from @csharpproglib
  1. Sep 23, 2026⚙️ yield return не бесплатный Итераторы выглядят просто, но работают иначе: IEnumerable<in…
  2. Sep 22, 2026💡 Replace, Regex или StringBuilder? Для замены текста в C# есть несколько инструментов. И…
  3. Sep 21, 2026⚙️ Настоящие атомарные операции Если Volatile решает проблему видимости, то Interlocked ре…
  4. Sep 20, 2026💪 Разминка перед трудовыми буднями Что произойдёт? ❤️ — список станет [1, 3] 🔥 — Invalid…
  5. Sep 19, 2026🧩 Middleware в ASP.NET Core: 3 ловушки Middleware — звено HTTP pipeline: app.Use(async (c…
  6. Sep 18, 2026📍 Навигация: Вакансии • Задачи • Собесы 🐸Библиотека шарписта #garbage_collector
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →