Когда 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_люминатор