Структура проекта

Анатомия проекта: Program.cs, csproj, appsettings.json — где что лежит и зачем.

Суть: сердце приложения ASP.NET Core — файл Program.cs, где собирается приложение и настраивается конвейер обработки запросов. Конфигурация хранится в appsettings.json, а зависимости и метаданные — в .csproj.

В современных версиях (.NET 6+) проект стал предельно компактным. Нет больше отдельных Startup.cs и громоздкого Main — благодаря top-level statements весь старт умещается в один файл. Разберём его.

Program.cs — точка входа

var builder = WebApplication.CreateBuilder(args);

// 1. Регистрация сервисов в DI-контейнере
builder.Services.AddControllers();

var app = builder.Build();

// 2. Настройка конвейера middleware
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

Здесь два чётких этапа. До Build() — мы регистрируем сервисы (что приложение умеет: контроллеры, БД, аутентификация). После Build() — настраиваем конвейер: как именно обрабатывать каждый запрос. app.Run() запускает сервер и блокирует поток до остановки.

Структура папок

MyApi/
 +-- Program.cs          точка входа и сборка
 +-- MyApi.csproj        зависимости, версия .NET
 +-- appsettings.json    конфигурация (строки подключения и т.п.)
 +-- appsettings.Development.json  переопределение для dev
 +-- Controllers/        контроллеры (если MVC-стиль)
 +-- Properties/
      +-- launchSettings.json  настройки запуска (порты, профили)

Как работает под капотом

Файл .csproj — это XML, описывающий проект: какую версию .NET использовать (TargetFramework), какие NuGet-пакеты подключены. Когда вы делаете dotnet add package, в csproj добавляется строчка <PackageReference>. Конфигурация читается слоями: сначала appsettings.json, потом appsettings.{Environment}.json (например Development), потом переменные окружения — каждый следующий слой переопределяет предыдущий. Это позволяет хранить общие настройки в одном файле, а секреты и dev-специфику — отдельно.

Частые ошибки

  • Класть секреты (пароли, ключи) прямо в appsettings.json. Их видно в git. Для dev используют User Secrets, для прода — переменные окружения или хранилища секретов.
  • Менять порядок middleware наугад. Порядок строк после Build() критичен — об этом отдельный раздел.
  • Путать appsettings.json и launchSettings.json. Первый — конфигурация приложения, второй — только локальные настройки запуска (в проде не используется).

Best practices

  • Держите Program.cs чистым: выносите длинную настройку в extension-методы (builder.Services.AddMyServices()).
  • Чувствительные настройки читайте через IConfiguration/IOptions, а не хардкодьте.
  • Среда (Development/Staging/Production) задаётся переменной ASPNETCORE_ENVIRONMENT — используйте её для разного поведения.

Окружения и почему их три

ASP.NET Core из коробки знает про окружения: Development, Staging, Production. Текущее берётся из переменной ASPNETCORE_ENVIRONMENT. От него зависит многое: в Development показываются подробные страницы ошибок и включается Swagger, в Production — лаконичные ответы и строгие настройки. Это позволяет одному и тому же коду вести себя по-разному в зависимости от того, где он запущен, без правки исходников.

Конфигурация устроена как стопка слоёв-провайдеров, и порядок важен: базовый appsettings.json переопределяется файлом окружения (appsettings.Production.json), затем переменными окружения, затем аргументами командной строки. Последний слой всегда побеждает. Благодаря этому в проде секреты и адреса БД подставляются через переменные окружения контейнера, не трогая закоммиченные файлы.

Как читать конфигурацию правильно

Доступ к настройкам идёт через сервис IConfiguration, но напрямую обращаться к нему по строковым ключам — не лучший стиль. Предпочтительный путь — паттерн Options: вы заводите класс настроек, привязываете к нему секцию конфигурации и получаете типизированный объект через DI. Это даёт автодополнение, проверку типов и одно место, где видно, какие настройки вообще есть у компонента.

Секреты в разработке хранят в User Secrets — отдельном файле вне репозитория, привязанном к проекту. Команда dotnet user-secrets set кладёт туда значение, и оно автоматически подмешивается в конфигурацию в Development. В проде эту роль берут переменные окружения или специализированные хранилища (Azure Key Vault, HashiCorp Vault). Главное правило неизменно: ключи и пароли никогда не попадают в git.

Итог: Program.cs собирает приложение в два этапа (сервисы и конвейер), конфигурация лежит слоями в appsettings, а зависимости — в csproj. Теперь напишем первый эндпоинт.

Проверьте себя
1. Что происходит в Program.cs до вызова builder.Build()?
AНастраивается конвейер middleware
BРегистрируются сервисы в DI-контейнере
CЗапускается сервер
DЧитаются HTTP-запросы
2. Как обычно поступают с секретами (паролями, ключами)?
AКладут прямо в appsettings.json и коммитят
BХранят в User Secrets (dev) и переменных окружения или секрет-хранилищах (прод)
CХардкодят в Program.cs
DКладут в launchSettings.json