Minimal API

Перший додаток на ASP.NET Core

Створення проекту Minimal API за допомогою .NET CLI, Visual Studio та JetBrains Rider, а також глибокий розбір згенерованого коду.

Перший додаток на ASP.NET Core: .NET CLI, Visual Studio та JetBrains Rider

Примітка для читача: Вітаю на нашій другій сесії! Минулого разу ми говорили про філософію та історію фреймворку, а також про те, що таке Kestrel. Сьогодні ми беремося за інструменти. Ми не просто "натиснемо кнопку і отримаємо результат". Ми розберемо кожен файл, який згенерується, і кожну опцію, яку ми оберемо, під мікроскопом. Наприкінці ви будете розуміти свій проект так, ніби написали його ядро власноруч.

1. Контекст: Що таке "Створення проекту" насправді?

Коли ми створюємо проект, ми, по суті, генеруємо структуру папок і файлів, яка зрозуміла інструментам розробки (компілятору, IDE). Історично проекти .NET складалися з десятків конфігураційних файлів (.config, .csproj, скрипти збірки тощо). В епоху Minimal API все зведено до абсолютного мінімуму: одного файлу коду і одного файлу проекту.

Перш ніж ми пірнемо в написання коду, нам потрібно обрати "зброю" — інструмент, за допомогою якого ми будемо кувати наш код. У світі .NET є три основні шляхи:

  1. .NET CLI (Інтерфейс командного рядка) — вибір хакерів, фанатів Linux, macOS та прихильників автоматизації (CI/CD).
  2. Visual Studio — класичний, потужний, корпоративний вибір для користувачів Windows. "Все включено".
  3. JetBrains Rider — сучасна кросплатформена IDE (працює на Mac, Linux, Windows), яка стала стандартом де-факто для багатьох сеньйорів завдяки вбудованому ReSharper (штучному інтелекту для рефакторингу).

Ми навчимося використовувати всі три.


2. Шлях Ніндзя: Створення через .NET CLI

.NET CLI (Command Line Interface) — це фундамент. Навіть коли ви натискаєте кнопку Run у Visual Studio чи Rider, під капотом вони часто звертаються саме до цих консольних команд. Розуміння CLI робить вас незалежним від конкретної IDE (наприклад, ви легко зможете писати код у легковаговику VS Code або навіть у Vim).

Крок 1: Встановлення .NET SDK

Що нам потрібно? Нам потрібен SDK (Software Development Kit).

.NET Runtime vs .NET SDK
Concept
В чому різниця?
  • .NET Runtime — це середовище виконання. Його встановлюють звичайні користувачі або на серверах, щоб просто запустити вже написану програму. Воно містить віртуальну машину (CoreCLR) і базові бібліотеки поліології.
  • .NET SDK — це набір інструментів для розробника. Він включає в себе Runtime, але також компілятор (Roslyn), збирач (MSBuild), систему шаблонів та інструменти тестування. Нам потрібен саме SDK.
  1. Перейдіть на офіційний сайт завантаження .NET.
  2. Оберіть поточну LTS (Long Term Support) версію (наприклад, .NET 8).
  3. Після встановлення відкрийте 터мінал (Terminal на macOS/Linux або PowerShell на Windows) і перевірте, чи все працює:
dotnet --version

Очікуваний результат: ви побачите номер версії, наприклад, 8.0.200.

Крок 2: Декомпозиція команди dotnet new

CLI має вбудований генератор шаблонів. Коли ви пишете dotnet new, працює рушій скаффолдингу (scaffolding).

Створімо нову теку і перейдемо в неї:

mkdir MyAwesomeApi
cd MyAwesomeApi

Тепер магічна команда:

dotnet new web -n MinimalShowcase

Давайте розберемо, що ми щойно ввели:

  • dotnet — виклик головного виконуваного файлу CLI.
  • new — команда генерації коду з шаблону. Командний рядок переглядає встановлені шаблони.
  • webнайважливіший параметр. Це коротка назва шаблону "ASP.NET Core Empty". Він створює абсолютно порожній додаток Minimal API. (Існують інші шаблони: mvc для класичних контролерів, blazor для фронтенду, console для консольних утиліт).
  • -n MinimalShowcase — аргумент name (назва). Якщо не вказати, назва проекту буде збігатися з назвою папки (MyAwesomeApi), але тут ми жорстко задали ім'я проекту.

Крок 3: Запуск сервера (Команда dotnet run)

Сервер готовий. Що далі? Запуск.

dotnet run

Коли ви натискаєте Enter, відбувається магія, що складається з кількох етапів:

  1. Restore: CLI перевіряє файл проекту (.csproj) і завантажує всі відсутні NuGet-пакети з інтернету в локальний кеш комп'ютера. (Неявно викликається dotnet restore).
  2. Build: Викликається компілятор Roslyn. Ваш C# код перетворюється на Intermediate Language (IL) — байт-код. Якщо є синтаксичні помилки, процес зупиниться тут. (Неявно викликається dotnet build).
  3. Execute: Запускається .NET віртуальна машина, яка стартує сервер Kestrel.

В результаті ви побачите в консолі щось на кшталт:

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5032
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.

Відкрийте браузер за вказаною адресою http://localhost:5032 і ви побачите магічні слова Hello World!.

Суперсила: dotnet watch Під час розробки постійно зупиняти сервер (Ctrl+C) і знову писати dotnet run — це марнування часу. Використовуйте команду dotnet watch (або dotnet watch run). Вона запустить сервер, а потім буде стежити за змінами у ваших файлах. Щойно ви натиснете "Зберегти" у файлі Program.cs, сервер миттєво перезапуститься сам, або навіть застосує зміни без перезапуску (фіча відома як Hot Reload).

3. Шлях Архітектора: Створення через Visual Studio 2022

Для багатьох розробників Visual Studio — це дім. Вона знімає з розробника когнітивне навантаження, запам'ятовуючи команди і надаючи візуальний інтерфейс.

Крок 1: Встановлення

  1. Перейдіть на сайт Visual Studio. Для навчання ідеально підходить версія Community, вона безкоштовна і повнофункціональна.
  2. При встановленні ви побачите вікно "Workloads" (Робочі навантаження). Це модулі. Обов'язково виберіть "ASP.NET and web development". Саме цей модуль містить всі шаблони і локальний веб-сервер IIS Express.

Крок 2: Процес створення (Візард)

  1. Запустіть Visual Studio. Натисніть "Create a new project".
  2. У пошуку зірвалася сотня шаблонів. Не губіться. Пишіть ASP.NET Core Empty. Саме цей шаблон є синонімом команди dotnet new web. (Існує шаблон ASP.NET Core Web API, але він набагато важчий: до нього входить Swagger, контролери та купа коду погоди(WeatherForecast). Для вивчення фундаменту нам потрібен "Empty").
  3. Оберіть назву та розтошування на диску.
  4. На наступному екрані (Additional Information) найважливіші 체크бокси:
    • Framework: виберіть найновішу LTS версію (наприклад, .NET 8).
    • Configure for HTTPS: за замовчуванням увімкнено. Увімкніть. Ваш локальний сервер створить самопідписаний сертифікат, щоб працювати безпечно.
    • Do not use top-level statements: Залиште вимкненим! Ми обговорювали це раніше. Ми хочемо сучасний Minimal API стиль без класового шуму.

Крок 3: Запуск однією кнопкою (F5)

Натисніть зелену кнопку "Play" або клавішу F5 на клавіатурі. У Visual Studio F5 означає "Start with Debugging". Вона не просто запустить програму, вона "причепить" до неї відладчик (Debugger). Ви зможете ставити точки зупинки (breakpoints) і перевіряти значення змінних у пам'яті "на льоту".

Браузер автоматично відкриється. Магія.


4. Шлях Кросплатформеника: Створення через JetBrains Rider

Rider став неймовірно популярним завдяки своїй неймовірній швидкості, ідеальному парсингу коду та тому факту, що він однаковий на Mac, Linux та Windows.

Особливості створення в Rider

Процес дуже схожий на Visual Studio, але інтерфейс трохи інший:

  1. На екрані вітання натисніть "New Solution".
  2. У лівому боковому меню знайдіть розділ .NET / .NET Core і оберіть Empty Web. (Це аналог ASP.NET Core Empty).
  3. Заповніть Solution Name.
  4. Зверніть увагу на поле "Project framework" — впевніться, що там обрана потрібна версія.
  5. Натисніть Create.
  6. У правому верхньому куті вікна Rider є панель запуску. Натисніть зелений жучок (Debug) або зелену стрілку (Run).

Завдяки вбудованому серверному терміналу внизу вікна, ви побачите ті ж самі логі Kestrel, що і при роботі з CLI.


5. Анатомія Проекту: Що всередині папки?

Незалежно від обраного інструменту, ви отримали папку. Давайте подивимося на структуру через лінзу розробника.

Ми розберемо кожен з ключових файлів. Інженер має знати свої інструменти.

Вузол 1: .csproj (C-Sharp Project)

Цей файл — це XML-документ, який говорить компілятору і системі збірки (MSBuild), що це за проект і які математичні/фізичні обмеження він має. Відкрийте його у звичайному блокноті. Ви здивуєтеся, який він короткий:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

</Project>

Що тут цікавого?

  • Sdk="Microsoft.NET.Sdk.Web" — саме цей атрибут говорить системі: "Це не консольний додаток. Це не бібліотека. Це ВЕБ-ДОДАТОК". Цей SDK автоматично підключає сотні dll-файлів, пов'язаних з Kestrel, HTTP, роутингом. Якби ми змінили його на Microsoft.NET.Sdk, наш веб-сервер перестав би компілюватися.
  • <TargetFramework>net8.0</TargetFramework> — версія платформи. Змінивши цифру 8 на 7 і виконавши dotnet restore, ви зміните фундамент усього додатку.
  • <Nullable>enable</Nullable> — увімкнення жорсткої перевірки на можливі null значення під час компіляції (захист від NullReferenceException).
  • <ImplicitUsings>enable</ImplicitUsings> — "секрет" того, чому фійл Program.cs такий чистий. Як обговорювалося в Модулі 1, це автоматично ховає імпорти системних бібліотек.
NuGet та Залежності Одне з ключових завдань файлу .csproj — керування зовнішніми бібліотеками (NuGet-пакетами). Коли ви додаєте нову бібліотеку через консоль, наприклад dotnet add package Newtonsoft.Json, у цей файл автоматично розширюється вузлом <ItemGroup>, який міститиме <PackageReference Include="Newtonsoft.Json" Version="..." />. Саме завдяки цьому dotnet restore знає, які пакети необхідно завантажити перед компіляцією. У Visual Studio ці залежності також відображаються у вузлі Dependencies (Залежності) в Оглядачі рішень (Solution Explorer).

Вузол 2: appsettings.json

У будь-якому додатку є "секрети" або "конфігурації". Наприклад, адреса бази даних (Connection String), рівні логування або налаштування пошти. "Харакірі" для програміста — вписувати ці налаштування безпосередньо в код C# (hardcoding).

Для цього існує appsettings.json:

{
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft.AspNetCore": "Warning"
        }
    },
    "AllowedHosts": "*"
}

Чому їх два? (appsettings.json та appsettings.Development.json) ASP.NET Core побудований з розумінням "Середовищ" (Environments). У вас є середовище розробки (у вас на комп'ютері) і середовище продакшену (на бойовому сервері в датацентрі). Фреймворк налаштований так: він спочатку читає appsettings.json, а потім зверху на нього накладає зміни з appsettings.Development.json (якщо ви зараз у режимі розробки). Наприклад, ви можете логувати кожну деталь у дев-режимі, але в продакшені логувати тільки критичні помилки.

Вузол 3: Properties/launchSettings.json

Цей файл існує ЛИШЕ ДЛЯ РОЗРОБКИ. Він НІКОЛИ не йде на бойовий сервер. Він пояснює вашій локальній середі (Visual Studio, Rider або dotnet run), як саме стартувати Kestrel.

{
  "profiles": {
    "http": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "applicationUrl": "http://localhost:5032",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    ...
  }
}
  • "commandName": "Project" — означає, що запускатиметься Kestrel безпосередньо.
  • "launchBrowser": true — коли код скомпілюється, IDE сама відкриє Google Chrome.
  • "applicationUrl": "http://localhost:5032" — порт, який слухатиме наш локальний сервер. За замовчуванням він генерується випадковим чином при створенні проекту (щоб уникнути конфліктів портів).
  • "ASPNETCORE_ENVIRONMENT": "Development" — саме ця системна змінна вчиняє магію і змушує ASP.NET читати appsettings.Development.json.

6. Глибока Анатомія: Розтин Program.cs

Ось він, святий Грааль усього проекту. Всього 4 рядки. Але за кожним рядком — тисячі годин інженерної роботи Microsoft. Ми розкладемо цей код на молекули.

Код виглядає так:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

Рядок 1: Створення Будівельника (Builder Pattern)

var builder = WebApplication.CreateBuilder(args);
WebApplication.CreateBuilder(string[] args)
Method Signature
Документація: Статичний метод, що ініціалізує нову версію класу WebApplicationBuilder із заздалегідь налаштованими стандартними значеннями. Приймає масив рядків, переданий з командного рядка під час запуску програми. Повертає об'єкт WebApplicationBuilder.

Аналогія: Ви наймаєте генерального підрядника для будівництва будинку. Підрядник (це наш builder) не просто стоїть. Метод CreateBuilder(args) виконує гігантський обсяг роботи ще до того, як ви почнете писати код. Під "капотом" він:

  1. Завантажує змінні середовища ОС (наприклад, PATH або системні секрети).
  2. Завантажує файл appsettings.json (де ми були хвилину тому).
  3. Налаштовує вбудований Dependency Injection контейнер (наш склад з інструментами — ми детально поговоримо про нього в наступному модулі).
  4. Налаштовує систему логування, щоб вона одразу вміла писати логи в консоль кольоровими буквами.
  5. Налаштовує веб-сервер Kestrel і читає launchSettings.json.

На цьому етапі ви втручаєтеся і можете сказати підряднику: "Гей, побудуй мені також додатковий басейн (додай підключення до Бази Даних) або постав сигналізацію (додай систему Автентифікації)". Ми робимо це до написання builder.Build().

Що таке args? Якщо ви запустите програму в консолі командою dotnet run -- --port 8080, масив args передасть ці додаткові параметри нашому генеральному підряднику, щоб він міг переконфігурувати програму "на льоту".

Рядок 2: Затвердження плану (Build)

var app = builder.Build();

Після того, як ви надали підряднику всі інструкції, ви кажете: "Будуй". Коли викликається Build(), контейнер об'єктів заморожується. Це операція в одну сторону (One-way operation). З цього моменту ви більше не можете додавати нові "фундаментальні сервіси" в архітектуру.

Натомість, вам повертається об'єкт app — це екземпляр класу WebApplication.

WebApplication
Class
Документація: Ядро ASP.NET Core додатка. Виступає в ролі хоста (Host), який керує життєвим циклом (Startup/Shutdown). Реалізує інтерфейси IApplicationBuilder (для налаштування middleware пайплайну) та IEndpointRouteBuilder (для налаштування маршрутизації).

Аналогія: Будинок здано в експлуатацію. Стіни стоять, світло підведено. Тепер ви (як власник app) розставляєте меблі, визначаєте правила (хто першим заходить, де знімати взуття), тобто встановлюєте Middleware і Маршрути.

Рядок 3: Маршрутизація (Routing) і Делегати. Ваша перша бізнес-логіка

app.MapGet("/", () => "Hello World!");

Тут ми навчаємо наш веб-сервер спілкуватися. Ми викликаємо метод MapGet.

MapGet(string pattern, Delegate handler)
Method Signature (Extension)
Документація: Метод розширення для IEndpointRouteBuilder. Додає новий кінцевий вузол (Endpoint), який реагуватиме виключно на HTTP запити з методом GET, якщо URL в браузері відповідатиме заданому pattern. Параметри:
  1. pattern - шаблон шляху, рядок. Наприклад "/api/users".
  2. handler - логіка обробки. Зазвичай передається як анонімна функція (лямбда-вираз).

Коли клієнт відкриває браузер і набирає адресу (наприклад, http://localhost:5032/), браузер неявно робить HTTP-запит типу GET. Сервер Kestrel ловить цей запит, перетворює його на об'єкт HttpContext і передає в рушій Маршрутизації (Router). Рушій дивиться у свою внутрішню карту (Map):

  • "Є в мене щось на метод GET для шляху / (кореневий каталог)?"
  • "Так, є! Ось вона, мета-функція (лямбда) () => "Hello World!""

Сервер заходить всередину лямбда-виразу. Лямбда-вираз відпрацьовує і повертає звичайний C# string (рядок). Сервер достатньо розумний, щоб сказати: "Ага, клієнту повертається рядок. Я сам оберну це в правильний HTTP-відповідь! Я сам додам статус-код 200 OK. Я сам додам заголовок Content-Type: text/plain!"

І ми бачимо результат на екрані.

!CAUTIONЩо буде, якщо змінити код? Спробуйте змінити MapGet на MapPost. Запустіть сервіс і перезавантажте сторінку браузера. Браузери через адресний рядок вміють відправляти тільки GET-запити. Сервер побачить GET-запит на "/", подивиться у карту і побачить "У мене є POST на '/', але немає GET на '/'". Сервер поверне вам сумну помилку 404 Not Found (або 405 Method Not Allowed, залежно від конфігурації маршрутизатора). Це доводить, що ASP.NET жорстко контролює HTTP-методи.

Рядок 4: Безкінечний цикл очікування

app.Run();

Сервер — це програма, яка в ідеалі ніколи не закінчується. Якби не метод Run(), програма просто дійшла б до 4-го рядка і завершилася б в ту саму мілісекунду, і вікно консолі закрилося б.

Run() — це, спрощено кажучи, велетенський безкінечний цикл while (true), який блокує основний потік (Thread) програми і змушує Kestrel затамувати подих і слухати мережеву плату комп'ютера. Він прийматиме нові й нові підключення, доки ви не натиснете Ctrl+C в консолі.

Код Run() розроблений так, що він асинхронно чекає подію закінчення процесу CancellationToken.


7. Практичні вправи: Тренуємо м'язи кодування

Замість того, щоб йти далі, закріпіть розуміння самостійними діями. Зробіть це прямо зараз у своєму проекті.

  1. Завдання 1: Змініть порт. Відкрийте Properties/launchSettings.json і змініть порт з 5032 на 8080. Збережіть і натисніть dotnet run. Відкрийте в браузері http://localhost:8080.
  2. Завдання 2: Мульти-роутинг. Додайте підрядком ще один ендпоінт. Перед app.Run(), додайте:
    app.MapGet("/about", () => "It's me, Minimal Server!");
    
    Перезапустіть сервер. Зайдіть на / і на /about. Що відбувається? Поспостерігайте за гнучкістю.
  3. Завдання 3: Метод розширення. Змініть текст "Hello World!" на "Привіт, Світ!". Зверніть увагу на кодування тексту в браузері. Чи правильно відображаються літери? (Це перевірка того, як автоматично встановлюється кодування UTF-8).

8. Експертний розділ: Чи є в Minimal API багатопоточність?

Часте питання від спеціалістів, які переходять з Node.js чи PHP. У Node.js ми маємо єдиний потік виконання (Single Thread Event Loop). Якщо один HTTP запит виконає важку математичну функцію while(true), вебсервер "повисне", і ніхто інший не зможе достукатися до сайту.

ASP.NET Core (включно з Minimal API) є багатопоточним і використовує Асинхронну модель на основі пулу потоків (Thread Pool). Якщо одночасно прийде 100 запитів на сторінку /, ASP.NET візьме з "пулу потоків" 100 вільних робітників, і вони ВСІ паралельно виконають вашу лямбду. Це величезна перевага в системній продуктивності. Але це вимагає від вас бути уважними, якщо ви намагаєтесь записати дані в загальний для всіх ресурс (наприклад, у спільний статичний масив) — ми поговоримо про цю проблему (Thread-Safety) в майбутньому модулі.


9. Підсумок модуля

  • Ми навчились створювати проекти декількома способами. CLI дає контроль. Visual Studio дає підтримку "в один клік", а Rider дає гнучкість кросплатформеності.
  • Проект Minimal API структурно примітивний. Головні кити: .csproj для компілятора, appsettings.json для секретів і змінних, Program.cs для бізнес-логіки.
  • 4 рядки Program.cs — це складний конвеєр:
    • CreateBuilder готує середовище з сотнями налаштувань.
    • Build фіксує фундамент архітектури і створює додаток.
    • MapGet визначає правила переадресації.
    • Run запускає нескінченний процес-слухач Kestrel.

Важко повірити, скільки інженерії приховано за простотою. У наступному модулі ми порушимо цю гармонію, "розкриємо" наш Builder і розберемо одну з найбільш геніальних філософій сучасного програмування — Dependency Injection (впровадження залежностей).


Перевірка знань

Якщо тест не завантажується, дайте відповідь на ці питання самостійно:

  1. Який файл відповідає за параметри під час запуску додатку (порт, браузер), і чому його не варто відправляти на Production сервер?
  2. В чому принципова різниця між builder та app? Чому Microsoft не об'єднали це в один об'єкт?
  3. Чому сервер видає помилку, якщо ми використовуємо MapPost("/", ...) і звертаємось до сайту через рядок браузера?
Не знаєте відповідей? Поверніться на кілька екранів назад, ми детально проаналізували кожну конструкцію.
Copyright © 2026