Swagger / Swashbuckle у Minimal API: окремий класичний шлях
Swagger / Swashbuckle у Minimal API: окремий класичний шлях
Minimal API + OpenAPI + Scalar. Але в реальних проєктах ви дуже часто зустрінете інший стек: Swagger UI + Swashbuckle. Це не просто історичний артефакт. Це окремий, дуже поширений спосіб генерувати OpenAPI-документ і показувати його через вбудований UI.Цей матеріал потрібен з двох причин:- велика кількість існуючих Minimal API проєктів уже побудована саме на Swashbuckle;
- терміни
Swagger,Swashbuckle,Swagger UIіOpenAPIпостійно змішують, і без окремого розбору в голові лишається плутанина.
.NET 9+ базовим шляхом у цьому курсі лишається зв'язка AddOpenApi() + Scalar. За актуальною документацією Microsoft, Swashbuckle не є доступним шляхом для .NET 9+, тому ця стаття свідомо орієнтована на .NET 8 та підтримку існуючих систем.Що розберемо
- різницю між
Swagger UI,SwashbuckleіOpenAPI; - повний відтворюваний проєкт Minimal API на
.NET 8; AddEndpointsApiExplorer,AddSwaggerGen,UseSwagger,UseSwaggerUI;- Fluent-опис endpoint-ів без атрибутів;
- XML comments, route config, безпеку та кастомізацію UI.
Коли це треба
- ви підтримуєте API на
.NET 8; - команда вже використовує
Swashbuckle.AspNetCore; - у вас є Swagger UI і ви хочете зрозуміти, як він влаштований;
- потрібно впевнено читати legacy або чинний enterprise-код.
Що буде в кінці
swagger/v1/swagger.jsonвіддає OpenAPI-документ;/swaggerвідкриває інтерактивний Swagger UI;- endpoint-и Minimal API мають summaries, descriptions і responses;
- ви чітко розумієте, де закінчується OpenAPI і де починається Swagger tooling.
1. OpenAPI, Swagger UI, Swashbuckle: хто за що відповідає
Почнемо з найголовнішого розмежування.
| Термін | Що це таке | Роль |
|---|---|---|
| OpenAPI | Стандарт специфікації | Формат контракту API |
| Swagger UI | Web UI | Показує OpenAPI-документ у браузері |
| Swashbuckle | .NET tooling | Генерує OpenAPI-документ і вбудовує Swagger UI в ASP.NET Core |
Інженерна логіка така:
- Minimal API endpoint-и містять metadata.
Swashbuckleчитає цю metadata і генеруєswagger.json.Swagger UIв браузері читає цей JSON і малює інтерфейс.
2. Коли обирати Swagger окремо від Scalar
Після статті про Scalar природно виникає питання: навіщо ще окремо вивчати Swagger?
Відповідь прагматична:
- підтримка існуючих
.NET 8проєктів; - традиційний стек, який досі масово використовується;
- велика кількість прикладів у legacy-коді, блогах і корпоративних шаблонах;
- інтеграція в історичні пайплайни документації.
- сучасний шлях для нових
.NET 9+застосунків; - чистіша розв'язка між генерацією OpenAPI і UI;
- сучасніший reference UX;
- простіше мислити
OpenAPIяк окремий артефакт, аUIяк окремий шар.
Висновок тут не ідеологічний, а прикладний:
- якщо система вже на
Swashbuckle, потрібно вміти з ним працювати; - якщо ви починаєте новий
.NET 9+проєкт, тоScalarзазвичай краще відповідає сучасному стеку цього курсу.
3. Відтворюваний проєкт на .NET 8
Щоб матеріал був не абстрактним, створимо окремий невеликий Minimal API-проєкт під Swagger.
Крок 1: Створення
mkdir MinimalApiSwaggerDemo
cd MinimalApiSwaggerDemo
dotnet new web --framework net8.0
mkdir MinimalApiSwaggerDemo
cd MinimalApiSwaggerDemo
dotnet new web --framework net8.0
Крок 2: Пакет Swashbuckle
dotnet add package Swashbuckle.AspNetCore
Крок 3: Що ми побудуємо
Так само, як у статті про Scalar, зробимо невеликий каталог товарів:
GET /api/productsGET /api/products/{id}POST /api/productsPUT /api/products/{id}DELETE /api/products/{id}
Наприкінці перевіримо два URL:
/swagger/v1/swagger.json/swagger
4. Структура проєкту
5. Що робить AddEndpointsApiExplorer()
Це один із тих рядків, які копіюють майже всі, але пояснюють далеко не всі.
builder.Services.AddEndpointsApiExplorer();
Для Minimal API цей сервісовий виклик критично важливий, бо саме він дає ApiExplorer знання про endpoint-и, створені через MapGet, MapPost, MapPut і так далі.
Якщо його прибрати:
- застосунок продовжить працювати;
- самі endpoint-и нікуди не зникнуть;
- але
Swashbuckleперестане «бачити» Minimal API-маршрути так, як очікує генератор документації.
ApiExplorer виглядає інакше, тому розробники часто переносять інтуїцію з контролерів у Minimal API й недооцінюють роль AddEndpointsApiExplorer(). Для Minimal API це не декоративний, а структурний рядок.6. Що робить AddSwaggerGen()
Це вже серце інтеграції.
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new()
{
Title = "Minimal API Swagger Demo",
Version = "v1"
});
});
Саме тут ми кажемо:
- що OpenAPI-документ взагалі треба згенерувати;
- як він називається;
- яку версію документа ми реєструємо;
- які додаткові налаштування застосувати.
Найважливіші можливості SwaggerGen
v1.builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new()
{
Title = "Catalog API",
Version = "v1",
Description = "Swagger UI demo for Minimal API"
});
options.SupportNonNullableReferenceTypes();
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
if (File.Exists(xmlPath))
{
options.IncludeXmlComments(xmlPath);
}
});
7. UseSwagger() і UseSwaggerUI(): чому це два різні виклики
У багатьох Program.cs вони стоять поряд, тому створюється ілюзія, що це «одна функція у двох рядках». Насправді це дві різні відповідальності.
app.UseSwagger();
Цей middleware віддає OpenAPI JSON, зазвичай на маршруті:
/swagger/v1/swagger.json
Тобто він відповідає за машиночитаний документ.
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Demo v1");
});
Цей middleware віддає HTML/JS UI, який завантажує JSON-документ і рендерить його в браузері.
Простими словами:
UseSwagger()= «віддай JSON»;UseSwaggerUI()= «віддай інтерфейс, який цей JSON показує».
8. Документування Minimal API endpoint-ів під Swagger
Найважливіше інженерне зауваження: Swagger UI не живе окремим життям. Якщо ви хочете, щоб UI був багатим і точним, metadata треба закладати в самі endpoint-и.
У Minimal API для цього достатньо Fluent API:
group.MapPost("/", CreateProduct)
.WithName("CreateProduct")
.WithSummary("Створити товар")
.WithDescription("Створює товар і повертає 201 Created.")
.Accepts<CreateProductRequest>("application/json")
.Produces<ProductResponse>(StatusCodes.Status201Created)
.ProducesValidationProblem(StatusCodes.Status422UnprocessableEntity)
.ProducesProblem(StatusCodes.Status500InternalServerError)
.WithOpenApi();
Що з цього реально потрапляє в Swagger UI
| Fluent API | Що ви побачите в Swagger UI |
|---|---|
WithSummary(...) | короткий заголовок операції |
WithDescription(...) | розгорнутий опис |
WithTags(...) | групування операцій |
Accepts<T>(...) | request body schema |
Produces<T>(...) | schema успішної відповіді |
Produces(...) | статус-коди без тіла |
ProducesProblem(...) | ProblemDetails-style error |
WithName(...) + WithOpenApi() | стабільні operation metadata |
9. XML comments: коли вони ще корисні
Swagger historically дуже любить XML comments, і в екосистемі .NET 8 ви будете бачити їх часто.
Увімкнення в .csproj
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Підключення в AddSwaggerGen
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
if (File.Exists(xmlPath))
{
options.IncludeXmlComments(xmlPath);
}
Навіщо це потрібно
XML comments підтягують:
- summaries;
- descriptions;
- коментарі до моделей і властивостей.
Але тут є важлива практична межа.
- controller-heavy кодові бази;
- існуючі enterprise API;
- проєкти, де вже є культура XML documentation;
- генерація документації з класів і DTO.
- Minimal API, де хочеться бачити metadata поруч із маршрутом;
- endpoint-oriented дизайн;
- сценарії, де важливо тримати контракт прямо біля
MapGet/MapPost; - команди, які не хочуть розмазувати опис між handler-ом, DTO та XML-файлом.
Практичний висновок: у Minimal API Fluent API зазвичай читається краще, але XML comments усе ще залишаються корисним додатковим шаром, особливо для DTO та підтримки старих проєктів.
10. Кастомізація Swagger UI
Навіть базовий Swagger UI уже робочий, але в реальних системах його часто підлаштовують під інфраструктуру або UX-компроміси.
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Demo v1");
});
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("./swagger/v1/swagger.json", "Demo v1");
options.RoutePrefix = string.Empty;
});
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Demo v1");
options.DisplayRequestDuration();
});
На що звернути увагу
SwaggerEndpoint(...)повинен вказувати на правильний JSON.RoutePrefix = string.Emptyвідкриває UI на корені сайту.- відносний шлях
./swagger/v1/swagger.jsonкорисний за reverse proxy або віртуальних директорій.
/swagger/v1/swagger.json, а під reverse proxy або нестандартним base path UI перестає знаходити документ. У таких випадках відносний ./ шлях часто безпечніший.11. Безпека Swagger endpoint-ів
Помилка початківців: вважати, що якщо ваші API endpoint-и захищені, то documentation UI автоматично теж захищений. Це не так.
Окремо подумайте про:
swagger.json;/swagger;- доступність UI лише в development;
- потребу в авторизації.
Найпростіший варіант
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
Це стандартний стартовий компроміс: документація доступна лише в development.
Захистити сам JSON-маршрут
Офіційні приклади Microsoft також показують варіант із:
app.MapSwagger().RequireAuthorization();
Такий підхід корисний, якщо UI або документ мають бути доступні тільки аутентифікованим користувачам.
12. Типові помилки зі Swagger у Minimal API
.csproj є GenerateDocumentationFile, але IncludeXmlComments(...) відсутній або вказує на неправильний шлях../swagger/v1/swagger.json.13. Практичні завдання
Рівень 1: Базовий
Завдання 17.1: Підніміть базовий Swagger UI
Відтворіть проєкт зі статті й перевірте:
/swagger/v1/swagger.json/swagger
Завдання 17.2: Додайте ще один endpoint
Створіть GET /api/products/active, опишіть його через WithSummary, WithDescription і Produces.
Рівень 2: Metadata
Завдання 17.3: Покращіть POST
Для POST /api/products додайте:
- приклад payload;
- ще один статус-код помилки;
- детальніший опис сценарію.
Завдання 17.4: Підключіть XML comments
Додайте XML comments до DTO і перевірте, що частина описів з'явилась у Swagger UI.
Рівень 3: Інфраструктура
Завдання 17.5: RoutePrefix
Перенесіть Swagger UI на корінь сайту через RoutePrefix = string.Empty.
Завдання 17.6: Захистіть swagger endpoints
Налаштуйте авторизацію так, щоб доступ до /swagger і /swagger/v1/swagger.json був лише після аутентифікації.
14. Резюме
Swagger не дорівнює OpenAPI
Для .NET 8 це досі важливо
Metadata усе ще вирішує все
Scalar і Swagger варто знати обидва
.NET 8 рішень.Scalar у Minimal API: повний проєкт і Fluent OpenAPI
Покроковий проєкт ASP.NET Core Minimal API з Scalar, OpenAPI, повним Fluent-описом endpoint-ів без атрибутів, transformers, Route Groups і детальним розбором усіх варіантів metadata.
Що таке API. Клієнт-серверна архітектура
Визначення програмного інтерфейсу (API), аналогії з реального світу, клієнт-серверна модель, типи API та їх роль у сучасній розробці.