Коли ми створюємо проект, ми, по суті, генеруємо структуру папок і файлів, яка зрозуміла інструментам розробки (компілятору, IDE). Історично проекти .NET складалися з десятків конфігураційних файлів (.config, .csproj, скрипти збірки тощо). В епоху Minimal API все зведено до абсолютного мінімуму: одного файлу коду і одного файлу проекту.
Перш ніж ми пірнемо в написання коду, нам потрібно обрати "зброю" — інструмент, за допомогою якого ми будемо кувати наш код. У світі .NET є три основні шляхи:
Ми навчимося використовувати всі три.
.NET CLI (Command Line Interface) — це фундамент. Навіть коли ви натискаєте кнопку Run у Visual Studio чи Rider, під капотом вони часто звертаються саме до цих консольних команд. Розуміння CLI робить вас незалежним від конкретної IDE (наприклад, ви легко зможете писати код у легковаговику VS Code або навіть у Vim).
Що нам потрібно? Нам потрібен SDK (Software Development Kit).
dotnet --version
Очікуваний результат: ви побачите номер версії, наприклад, 8.0.200.
dotnet newCLI має вбудований генератор шаблонів. Коли ви пишете 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), але тут ми жорстко задали ім'я проекту.
Сервер готовий. Що далі? Запуск.
dotnet run
Коли ви натискаєте Enter, відбувається магія, що складається з кількох етапів:
.csproj) і завантажує всі відсутні NuGet-пакети з інтернету в локальний кеш комп'ютера. (Неявно викликається dotnet restore).dotnet build).В результаті ви побачите в консолі щось на кшталт:
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 run — це марнування часу. Використовуйте команду dotnet watch (або dotnet watch run). Вона запустить сервер, а потім буде стежити за змінами у ваших файлах. Щойно ви натиснете "Зберегти" у файлі Program.cs, сервер миттєво перезапуститься сам, або навіть застосує зміни без перезапуску (фіча відома як Hot Reload).Для багатьох розробників Visual Studio — це дім. Вона знімає з розробника когнітивне навантаження, запам'ятовуючи команди і надаючи візуальний інтерфейс.
ASP.NET Core Empty. Саме цей шаблон є синонімом команди dotnet new web. (Існує шаблон ASP.NET Core Web API, але він набагато важчий: до нього входить Swagger, контролери та купа коду погоди(WeatherForecast). Для вивчення фундаменту нам потрібен "Empty").Additional Information) найважливіші 체크бокси:

Натисніть зелену кнопку "Play" або клавішу F5 на клавіатурі.
У Visual Studio F5 означає "Start with Debugging". Вона не просто запустить програму, вона "причепить" до неї відладчик (Debugger). Ви зможете ставити точки зупинки (breakpoints) і перевіряти значення змінних у пам'яті "на льоту".
Браузер автоматично відкриється. Магія.
Rider став неймовірно популярним завдяки своїй неймовірній швидкості, ідеальному парсингу коду та тому факту, що він однаковий на Mac, Linux та Windows.
Процес дуже схожий на Visual Studio, але інтерфейс трохи інший:
.NET / .NET Core і оберіть Empty Web. (Це аналог ASP.NET Core Empty).Завдяки вбудованому серверному терміналу внизу вікна, ви побачите ті ж самі логі Kestrel, що і при роботі з CLI.
Незалежно від обраного інструменту, ви отримали папку. Давайте подивимося на структуру через лінзу розробника.
Ми розберемо кожен з ключових файлів. Інженер має знати свої інструменти.
.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, це автоматично ховає імпорти системних бібліотек..csproj — керування зовнішніми бібліотеками (NuGet-пакетами). Коли ви додаєте нову бібліотеку через консоль, наприклад dotnet add package Newtonsoft.Json, у цей файл автоматично розширюється вузлом <ItemGroup>, який міститиме <PackageReference Include="Newtonsoft.Json" Version="..." />. Саме завдяки цьому dotnet restore знає, які пакети необхідно завантажити перед компіляцією.
У Visual Studio ці залежності також відображаються у вузлі Dependencies (Залежності) в Оглядачі рішень (Solution Explorer).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 (якщо ви зараз у режимі розробки). Наприклад, ви можете логувати кожну деталь у дев-режимі, але в продакшені логувати тільки критичні помилки.
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.Program.csОсь він, святий Грааль усього проекту. Всього 4 рядки. Але за кожним рядком — тисячі годин інженерної роботи Microsoft. Ми розкладемо цей код на молекули.
Код виглядає так:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
var builder = WebApplication.CreateBuilder(args);
WebApplicationBuilder із заздалегідь налаштованими стандартними значеннями. Приймає масив рядків, переданий з командного рядка під час запуску програми. Повертає об'єкт WebApplicationBuilder.Аналогія: Ви наймаєте генерального підрядника для будівництва будинку.
Підрядник (це наш builder) не просто стоїть. Метод CreateBuilder(args) виконує гігантський обсяг роботи ще до того, як ви почнете писати код. Під "капотом" він:
appsettings.json (де ми були хвилину тому).launchSettings.json.На цьому етапі ви втручаєтеся і можете сказати підряднику: "Гей, побудуй мені також додатковий басейн (додай підключення до Бази Даних) або постав сигналізацію (додай систему Автентифікації)". Ми робимо це до написання builder.Build().
Що таке args? Якщо ви запустите програму в консолі командою dotnet run -- --port 8080, масив args передасть ці додаткові параметри нашому генеральному підряднику, щоб він міг переконфігурувати програму "на льоту".
var app = builder.Build();
Після того, як ви надали підряднику всі інструкції, ви кажете: "Будуй". Коли викликається Build(), контейнер об'єктів заморожується. Це операція в одну сторону (One-way operation). З цього моменту ви більше не можете додавати нові "фундаментальні сервіси" в архітектуру.
Натомість, вам повертається об'єкт app — це екземпляр класу WebApplication.
IApplicationBuilder (для налаштування middleware пайплайну) та IEndpointRouteBuilder (для налаштування маршрутизації).Аналогія: Будинок здано в експлуатацію. Стіни стоять, світло підведено. Тепер ви (як власник app) розставляєте меблі, визначаєте правила (хто першим заходить, де знімати взуття), тобто встановлюєте Middleware і Маршрути.
app.MapGet("/", () => "Hello World!");
Тут ми навчаємо наш веб-сервер спілкуватися.
Ми викликаємо метод MapGet.
IEndpointRouteBuilder. Додає новий кінцевий вузол (Endpoint), який реагуватиме виключно на HTTP запити з методом GET, якщо URL в браузері відповідатиме заданому pattern.
Параметри:pattern - шаблон шляху, рядок. Наприклад "/api/users".handler - логіка обробки. Зазвичай передається як анонімна функція (лямбда-вираз).Коли клієнт відкриває браузер і набирає адресу (наприклад, http://localhost:5032/), браузер неявно робить HTTP-запит типу GET. Сервер Kestrel ловить цей запит, перетворює його на об'єкт HttpContext і передає в рушій Маршрутизації (Router). Рушій дивиться у свою внутрішню карту (Map):
/ (кореневий каталог)?"() => "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-методи.
app.Run();
Сервер — це програма, яка в ідеалі ніколи не закінчується. Якби не метод Run(), програма просто дійшла б до 4-го рядка і завершилася б в ту саму мілісекунду, і вікно консолі закрилося б.
Run() — це, спрощено кажучи, велетенський безкінечний цикл while (true), який блокує основний потік (Thread) програми і змушує Kestrel затамувати подих і слухати мережеву плату комп'ютера. Він прийматиме нові й нові підключення, доки ви не натиснете Ctrl+C в консолі.
Код Run() розроблений так, що він асинхронно чекає подію закінчення процесу CancellationToken.
Замість того, щоб йти далі, закріпіть розуміння самостійними діями. Зробіть це прямо зараз у своєму проекті.
Properties/launchSettings.json і змініть порт з 5032 на 8080. Збережіть і натисніть dotnet run. Відкрийте в браузері http://localhost:8080.app.Run(), додайте:
app.MapGet("/about", () => "It's me, Minimal Server!");
/ і на /about. Що відбувається? Поспостерігайте за гнучкістю.Часте питання від спеціалістів, які переходять з 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) в майбутньому модулі.
.csproj для компілятора, appsettings.json для секретів і змінних, Program.cs для бізнес-логіки.Program.cs — це складний конвеєр:
CreateBuilder готує середовище з сотнями налаштувань.Build фіксує фундамент архітектури і створює додаток.MapGet визначає правила переадресації.Run запускає нескінченний процес-слухач Kestrel.Важко повірити, скільки інженерії приховано за простотою. У наступному модулі ми порушимо цю гармонію, "розкриємо" наш Builder і розберемо одну з найбільш геніальних філософій сучасного програмування — Dependency Injection (впровадження залежностей).
Якщо тест не завантажується, дайте відповідь на ці питання самостійно:
builder та app? Чому Microsoft не об'єднали це в один об'єкт?MapPost("/", ...) і звертаємось до сайту через рядок браузера?Вступ до ASP.NET та еволюція фреймворку
Глибоке занурення в анатомію ASP.NET Core, історію його розвитку, архітектуру Kestrel та причини переходу до Minimal API.
WebApplication, Builder та Dependency Injection
Глибинний розбір патерну Builder в ASP.NET Core, розуміння архітектури Dependency Injection, методів розширення та життєвого циклу додатка.