Minimal API + OpenAPI + Scalar. Але в реальних проєктах ви дуже часто зустрінете інший стек: Swagger UI + Swashbuckle. Це не просто історичний артефакт. Це окремий, дуже поширений спосіб генерувати OpenAPI-документ і показувати його через вбудований UI.Цей матеріал потрібен з двох причин:Swagger, Swashbuckle, Swagger UI і OpenAPI постійно змішують, і без окремого розбору в голові лишається плутанина..NET 9+ базовим шляхом у цьому курсі лишається зв'язка AddOpenApi() + Scalar. За актуальною документацією Microsoft, Swashbuckle не є доступним шляхом для .NET 9+, тому ця стаття свідомо орієнтована на .NET 8 та підтримку існуючих систем.Що розберемо
Swagger UI, Swashbuckle і OpenAPI;.NET 8;AddEndpointsApiExplorer, AddSwaggerGen, UseSwagger, UseSwaggerUI;Коли це треба
.NET 8;Swashbuckle.AspNetCore;Що буде в кінці
swagger/v1/swagger.json віддає OpenAPI-документ;/swagger відкриває інтерактивний Swagger UI;Почнемо з найголовнішого розмежування.
| Термін | Що це таке | Роль |
|---|---|---|
| OpenAPI | Стандарт специфікації | Формат контракту API |
| Swagger UI | Web UI | Показує OpenAPI-документ у браузері |
| Swashbuckle | .NET tooling | Генерує OpenAPI-документ і вбудовує Swagger UI в ASP.NET Core |
Інженерна логіка така:
Swashbuckle читає цю metadata і генерує swagger.json.Swagger UI в браузері читає цей JSON і малює інтерфейс.Після статті про Scalar природно виникає питання: навіщо ще окремо вивчати Swagger?
Відповідь прагматична:
.NET 8 проєктів;.NET 9+ застосунків;OpenAPI як окремий артефакт, а UI як окремий шар.Висновок тут не ідеологічний, а прикладний:
Swashbuckle, потрібно вміти з ним працювати;.NET 9+ проєкт, то Scalar зазвичай краще відповідає сучасному стеку цього курсу.Щоб матеріал був не абстрактним, створимо окремий невеликий Minimal API-проєкт під Swagger.
mkdir MinimalApiSwaggerDemo
cd MinimalApiSwaggerDemo
dotnet new web --framework net8.0
mkdir MinimalApiSwaggerDemo
cd MinimalApiSwaggerDemo
dotnet new web --framework net8.0
dotnet add package Swashbuckle.AspNetCore
Так само, як у статті про Scalar, зробимо невеликий каталог товарів:
GET /api/productsGET /api/products/{id}POST /api/productsPUT /api/products/{id}DELETE /api/products/{id}Наприкінці перевіримо два URL:
/swagger/v1/swagger.json/swaggerAddEndpointsApiExplorer()Це один із тих рядків, які копіюють майже всі, але пояснюють далеко не всі.
builder.Services.AddEndpointsApiExplorer();
Для Minimal API цей сервісовий виклик критично важливий, бо саме він дає ApiExplorer знання про endpoint-и, створені через MapGet, MapPost, MapPut і так далі.
Якщо його прибрати:
Swashbuckle перестане «бачити» Minimal API-маршрути так, як очікує генератор документації.ApiExplorer виглядає інакше, тому розробники часто переносять інтуїцію з контролерів у Minimal API й недооцінюють роль AddEndpointsApiExplorer(). Для Minimal API це не декоративний, а структурний рядок.AddSwaggerGen()Це вже серце інтеграції.
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new()
{
Title = "Minimal API Swagger Demo",
Version = "v1"
});
});
Саме тут ми кажемо:
SwaggerGenv1.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);
}
});
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 показує».Найважливіше інженерне зауваження: 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();
| Fluent API | Що ви побачите в Swagger UI |
|---|---|
WithSummary(...) | короткий заголовок операції |
WithDescription(...) | розгорнутий опис |
WithTags(...) | групування операцій |
Accepts<T>(...) | request body schema |
Produces<T>(...) | schema успішної відповіді |
Produces(...) | статус-коди без тіла |
ProducesProblem(...) | ProblemDetails-style error |
WithName(...) + WithOpenApi() | стабільні operation metadata |
Swagger historically дуже любить XML comments, і в екосистемі .NET 8 ви будете бачити їх часто.
.csproj<GenerateDocumentationFile>true</GenerateDocumentationFile>
AddSwaggerGenvar xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
if (File.Exists(xmlPath))
{
options.IncludeXmlComments(xmlPath);
}
XML comments підтягують:
Але тут є важлива практична межа.
MapGet/MapPost;Практичний висновок: у Minimal API Fluent API зазвичай читається краще, але XML comments усе ще залишаються корисним додатковим шаром, особливо для DTO та підтримки старих проєктів.
Навіть базовий 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 перестає знаходити документ. У таких випадках відносний ./ шлях часто безпечніший.Помилка початківців: вважати, що якщо ваші API endpoint-и захищені, то documentation UI автоматично теж захищений. Це не так.
Окремо подумайте про:
swagger.json;/swagger;if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
Це стандартний стартовий компроміс: документація доступна лише в development.
Офіційні приклади Microsoft також показують варіант із:
app.MapSwagger().RequireAuthorization();
Такий підхід корисний, якщо UI або документ мають бути доступні тільки аутентифікованим користувачам.
.csproj є GenerateDocumentationFile, але IncludeXmlComments(...) відсутній або вказує на неправильний шлях../swagger/v1/swagger.json.Відтворіть проєкт зі статті й перевірте:
/swagger/v1/swagger.json/swaggerСтворіть GET /api/products/active, опишіть його через WithSummary, WithDescription і Produces.
Для POST /api/products додайте:
Додайте XML comments до DTO і перевірте, що частина описів з'явилась у Swagger UI.
Перенесіть Swagger UI на корінь сайту через RoutePrefix = string.Empty.
Налаштуйте авторизацію так, щоб доступ до /swagger і /swagger/v1/swagger.json був лише після аутентифікації.
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 та їх роль у сучасній розробці.