TGViewer
Библиотека шарписта | C#, F#, .NET, ASP.NET Библиотека шарписта | C#, F#, .NET, ASP.NET @csharpproglib · 21.7K subscribers
Post #7016 2.86K
💡 Версионирование API в .NET 10 вместе с OpenAPI

Когда API растёт, рано или поздно встаёт вопрос: как добавить новые возможности и не сломать тех, кто уже использует старую версию? Стандартный ответ — версионирование. В .NET 10 появился удобный способ совместить версионирование с OpenAPI-документацией без лишнего кода.

Зачем нужно версионирование

Без версионирования любое изменение контракта API потенциально ломает клиентов. Версионирование позволяет выпускать новые версии параллельно со старыми, пока клиенты не перейдут самостоятельно.

Популярные стратегии:
- По URL: /api/v1/users
- По query string: /api/users?api-version=1.0
- По заголовку: X-API-Version: 1.0

Что изменилось в .NET 10

С .NET 9 Microsoft.AspNetCore.OpenApi стал стандартным инструментом для генерации OpenAPI вместо Swashbuckle.AspNetCore. Но удобной интеграции с версионированием не было.

В .NET 10 вышел пакет Asp.Versioning.OpenApi версии 10 — первый, который официально поддерживает и .NET 10, и новую OpenAPI-библиотеку от Microsoft.

Как подключить: Minimal APIs

Установите пакеты:
Asp.Versioning.Http@10.0.0
Asp.Versioning.Mvc.ApiExplorer@10.0.0
Asp.Versioning.OpenApi@10.0.0-rc.1


Настройка:
builder.Services.AddApiVersioning()
.AddApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
})
.AddOpenApi();

app.MapOpenApi().WithDocumentPerVersion();


Регистрация эндпоинтов:
var usersApi = app.NewVersionedApi("Users");

var v1 = usersApi.MapGroup("api/users").HasApiVersion("1.0");
var v2 = usersApi.MapGroup("api/users").HasApiVersion("2.0");

v1.MapGet("", () => TypedResults.Ok(new[]
{
new UserV1(1, "John Doe"),
}));

v2.MapGet("", () => TypedResults.Ok(new[]
{
new UserV2(1, "John Doe", new DateOnly(1990, 1, 1)),
}));


После запуска OpenAPI-документы доступны по адресам /openapi/v1.json и /openapi/v2.json.

Как подключить контроллеры

Пакеты:
Asp.Versioning.Mvc@10.0.0
Asp.Versioning.Mvc.ApiExplorer@10.0.0
Asp.Versioning.OpenApi@10.0.0-rc.1


Настройка идентична Minimal APIs, только добавляется .AddMvc():
builder.Services.AddApiVersioning()
.AddApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
})
.AddMvc()
.AddOpenApi();


Контроллеры с версиями:
[ApiController]
[Route("api/users")]
[ApiVersion("1.0")]
public class UsersV1Controller : ControllerBase
{
[HttpGet]
public ActionResult<UserV1[]> Get() =>
Ok(new[] { new UserV1(1, "John Doe") });
}

[ApiController]
[Route("api/users")]
[ApiVersion("2.0")]
public class UsersV2Controller : ControllerBase
{
[HttpGet]
public ActionResult<UserV2[]> Get() =>
Ok(new[] { new UserV2(1, "John Doe", new DateOnly(1990, 1, 1)) });
}


Визуализация: SwaggerUI и Scalar

Оба инструмента умеют показывать версионированные документы. SwaggerUI подключается через Swashbuckle.AspNetCore.SwaggerUI, Scalar через Scalar.AspNetCore.

SwaggerUI:
app.UseSwaggerUI(options =>
{
foreach (var desc in app.DescribeApiVersions().Reverse())
{
options.SwaggerEndpoint(
$"/openapi/{desc.GroupName}.json",
desc.GroupName.ToUpperInvariant());
}
});


Scalar:
app.MapScalarApiReference(options =>
{
var descriptions = app.DescribeApiVersions();
for (var i = 0; i < descriptions.Count; i++)
{
var desc = descriptions[i];
options.AddDocument(desc.GroupName, desc.GroupName,
isDefault: i == descriptions.Count - 1);
}
});


SwaggerUI откроется по /swagger, Scalar по /scalar.

Что изменилось по сравнению с v8

В старой версии Asp.Versioning.OpenApi v8 нужно было вызывать AddOpenApi() отдельно для каждой версии:
// v8
builder.Services.AddOpenApi("v1");
builder.Services.AddOpenApi("v2");


Теперь достаточно одного вызова, а WithDocumentPerVersion() берёт на себя генерацию отдельного документа для каждой версии автоматически.

➡️ Блог разработчиков

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека шарписта

#il_люминатор
  • ❤ 9
  • 👍 5
More from @csharpproglib
  1. Sep 23, 2026⚙️ yield return не бесплатный Итераторы выглядят просто, но работают иначе: IEnumerable<in…
  2. Sep 22, 2026💡 Replace, Regex или StringBuilder? Для замены текста в C# есть несколько инструментов. И…
  3. Sep 21, 2026⚙️ Настоящие атомарные операции Если Volatile решает проблему видимости, то Interlocked ре…
  4. Sep 20, 2026💪 Разминка перед трудовыми буднями Что произойдёт? ❤️ — список станет [1, 3] 🔥 — Invalid…
  5. Sep 19, 2026🧩 Middleware в ASP.NET Core: 3 ловушки Middleware — звено HTTP pipeline: app.Use(async (c…
  6. Sep 18, 2026📍 Навигация: Вакансии • Задачи • Собесы 🐸Библиотека шарписта #garbage_collector
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →