DotNetT2 Light_1200x303

Начиная с ASP.NET Core 3.1, у вас есть возможность изменять версии своих API с помощью пакета Microsoft NuGet. Сегодня мы научимся использовать этот пакет для простого создания версий ваших API.

Размещая API, вы приглашаете разработчиков использовать его на основании согласованного контракта. Что будет, если контракт изменится? Давайте посмотрим на простой пример.

Если я вызову API с URL-адресом https://mybandapi.com/api/bands/4, я получу следующий ответ:

{
   "id": 4,
   "name": "The Eagles, man"
}

Теперь предположим, что я решил обновить свою схему API новым полем YearFounded. Вот как теперь выглядит новый ответ:

{
   "id": 4,
   "name": "The Eagles, man",
   "YearFounded": 1971
}

С этим новым полем существующие клиенты по-прежнему будут работать. Это не серьезное изменение, поскольку существующие приложения могут его игнорировать. Вы, конечно же, должны задокументировать новое поле в «Долгой перспективе», но в конечном итоге это не так уж важно.

Допустим, вы хотите, чтобы name стало набором names, связанных с группой, например:

{
   "id": 4,
   "names": 
   [
     "The Eagles, man",
     "The Eagles"
   ],
   "FoundedYear": 1971
}

Вы внесли критическое изменение. Пользователи вашего API ожидали, что поле name будет строковым значением, а теперь вы возвращаете набор строк. В результате, когда потребители вызывают API, их приложения не будут работать, как предполагалось, и ваши пользователи , будут не довольны.

Ваши пользователи должны знать, чего ожидать, будь то небольшое изменение, которое не нарушает правила, или изменение, которое что-то ломает. Чтобы помочь управлять развивающимися API, вам понадобится стратегия управления версиями API.

Начиная с ASP.NET Core 3.1, Microsoft предоставляет библиотеки для помощи в управлении версиями API. Они предоставляют простой и эффективный способ добавления семантики управления версиями к вашим службам REST, а также соответствуют рекомендациям Microsoft REST Guidelines.

В этом посте я покажу вам, как можно использовать пакет NuGet Microsoft.AspNetCore.Mvc.Versioning для применения управления версиями API к веб-API REST ASP.NET Core.

Мы собираемся рассмотреть различные подходы к управлению версиями ваших API и способы включения этих функций в ASP.NET Core. Какой подход использовать? Как всегда, . Не существует догматичного универсального подхода к управлению версиями API. Вместо этого используйте подход, который лучше всего подходит для вашей ситуации.

Начнем.

Для начала вам понадобится пакет NuGet Microsoft.AspNetCore.Mvc.Versioning. Самый простой способ — через .NET CLI. Выполните следующую команду из каталога вашего проекта:

dotnet add package Microsoft.AspNetCore.Mvc.Versioning

Когда пакет установлен в вашем проекте, вам нужно будет добавить службу в контейнер внедрения зависимостей ASP.NET Core. Из Startup.ConfigureServices добавьте следующее:

public void ConfigureServices(IServiceCollection services)
{
   services.AddApiVersioning();
   // ..
}

Что произойдет, если я сделаю запрос GET на https://mybandapi.com/api/bands/4? Меня приветствует следующий ответ 400 Bad Request:

{
    "error": {
        "code": "ApiVersionUnspecified",
        "message": "An API version is required, but was not specified.",
        "innerError": null
    }
}

По умолчанию вам нужно добавить? Api-request = 1.0 к URL-адресу, чтобы это работало, например:

https://mybandapi.com/api/bands/4?api-request=1.0

Это не лучший опыт для разработчиков. Чтобы помочь в этом, мы можем ввести версию по умолчанию. Если потребители явно не указывают версию в своем запросе, мы предполагаем, что они хотят использовать версию 1.0. Наша библиотека принимает тип ApiVersioningOptions, который мы можем использовать для указания версии по умолчанию. Итак, вы говорите потребителям: «Если вы не выберете другую версию, вы будете использовать версию 1.0 нашего API».

В Startup.ConfigureServices обновите свой код AddApiVersioning следующим образом:

services.AddApiVersioning(options =>
{
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.DefaultApiVersion = new ApiVersion(1, 0);
});

После этого ваши потребители смогут вызывать версию по умолчанию с https://mybandapi.com/api/bands/4.

Представление нескольких версий для одной конечной точки

Допустим, мы хотим работать с двумя версиями нашего API: 1.0 и 2.0. Мы можем использовать это, украсив наш контроллер атрибутом ApiVersionAttribute.

[Produces("application/json")]
[Route("api/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
public class BandsController : ControllerBase
{}

Как только я это сделаю, я могу использовать MapToApiVersionAttribute, чтобы сообщить ASP.NET Core, какие методы действий сопоставлены с моими версиями API. В моем случае у меня есть два метода, подключенных к GET на api / Band: GetById и GetById20.

Вот как выглядят аннотации:

[MapToApiVersion("1.0")]
[HttpGet("{id}")]
public async Task<ActionResult<Band>> GetById(int id)
{}
    
[MapToApiVersion("2.0")]
[HttpGet("{id}")]
public async Task<ActionResult<Band>> GetById20(int id)
{}

При этом контроллер выполнит версию 1.0 GetById с обычным URI (или /api/bands/4?api-version=1.0), а контроллер выполнит версию 2.0, когда потребитель использует https://mybandapi.com. /api/bands/4?api-version=2.0.

Теперь, когда мы поддерживаем несколько версий, рекомендуется сообщить вашим потребителям, какие версии поддерживаются вашими конечными точками. Для этого вы можете использовать следующий код, чтобы сообщить клиенту о версиях API.

services.AddApiVersioning(options =>
{
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.ReportApiVersions = true;
});

Когда вы это делаете, ASP.NET Core предоставляет заголовок ответа api-supported-versions, который показывает, какие версии поддерживает конечная точка.

ASP.NET core returns an "api-supported-versions" header

По умолчанию библиотека поддерживает управление версиями из строк запроса. Мне нравится этот метод. Ваши клиенты могут подписаться на новые версии, когда они будут готовы. А если версия не указана, потребители могут полагаться на версию по умолчанию.

Конечно, это не единственный способ версии API. Помимо строк запроса, мы рассмотрим другие способы версии API в ASP.NET Core:

  • URI / URL-путь
  • Пользовательские заголовки запросов
  • Управление версиями мультимедиа с заголовками Accept

Управление версиями с помощью пути URI

Если я хочу выполнить версию по знакомой схеме / api / v {номер версии}, я могу легко это сделать. Затем в верхней части моего контроллера я могу использовать RouteAttribute, который соответствует моей версии API. Вот как теперь выглядят мои аннотации к контроллеру:

[ApiVersion("1.0")]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class BandsController : ControllerBase
{}

Затем, когда я вызываю свой API из api / v2 / Band / 4, я буду вызывать версию 2 своего API. Хотя это популярный метод и его легко настроить, он не лишен недостатков. Это не подразумевает версию по умолчанию, поэтому клиенты могут чувствовать себя вынужденными обновлять URI во всех своих приложениях при каждом изменении.

Независимо от того, использует ли он строку запроса или путь URI, Microsoft.AspNetCore.Mvc.Versioning упрощает работу с управлением версиями на уровне URI. Многие клиенты могут предпочесть отказаться от управления версиями URI по разным причинам. В этих случаях вы можете использовать заголовки.

Пользовательские заголовки запросов

Если вы хотите оставить свои URI в покое, вы можете попросить потребителей передать версию из заголовка запроса. Например, если потребители хотят использовать нестандартную версию моего API, я могу передать им значение заголовка запроса X-Api-Version.

При переходе к заголовкам также учтите, что вы немного усложняете доступ к API. Вместо того, чтобы обращаться к URI, клиентам необходимо обращаться к вашим конечным точкам программно или с помощью инструментов API. Возможно, это не будет большим скачком для ваших клиентов, но это следует учитывать.

Чтобы использовать настраиваемые заголовки запросов, вы можете установить для ApiVersionReader библиотеки значение HeaderApiVersionReader, а затем передать имя заголовка. (Если вы хотите выиграть викторину в пабе, ответ на вопрос «Что такое ApiVersionReader по умолчанию?» — «QueryStringApiVersionReader».)э

services.AddApiVersioning(options =>
{
   options.DefaultApiVersion = new ApiVersion(2, 0);
   options.AssumeDefaultVersionWhenUnspecified = true;
   options.ReportApiVersions = true;
   options.ApiVersionReader = new HeaderApiVersionReader("X-Api-Version");
});

В моем инструменте тестирования API я могу передать значение X-Api-Version, чтобы убедиться, что оно работает. И это так.

A client passes in a version to the request header

Управление версиями мультимедиа с помощью заголовков принятия

Когда клиент передает вам запрос, он использует заголовок Accept для управления форматом запроса, который он может обработать. В наши дни наиболее распространенным значением Accept является тип носителя application / json. Мы также можем использовать управление версиями с нашими типами мультимедиа.

Чтобы включить эту функцию, измените AddApiVersioning на следующее:

services.AddApiVersioning(options =>
{
     options.DefaultApiVersion = new ApiVersion(1, 0);
     options.AssumeDefaultVersionWhenUnspecified = true;
     options.ReportApiVersions = true;
     options.ApiVersionReader = new MediaTypeApiVersionReader("v");
});

Затем клиенты могут передать версию API с заголовком Accept следующим образом.

Passing an API version with the media header

Сочетание нескольких подходов

При работе с пакетом NuGet Microsoft.AspNetCore.Mvc.Versioning вы не обязаны использовать один метод управления версиями. Например, вы можете позволить клиентам выбирать между передачей строки запроса или заголовка запроса. ApiVersionReader поддерживает статический метод Combine, который позволяет указать несколько способов чтения версий.

services.AddApiVersioning(options =>
{
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.ReportApiVersions = true;
    options.ApiVersionReader =
    ApiVersionReader.Combine(
       new HeaderApiVersionReader("X-Api-Version"),
       new QueryStringApiVersionReader("version"));
});

После этого клиенты получают версию 2.0 нашего API, вызывая / api / Band / 4? Version = 2 или задавая значение заголовка X-Api-Version, равное 2.

Выучить больше

Этот пост поверхностный — вы можете сделать гораздо больше с пакетом NuGet Microsoft.AspNetCore.Mvc.Versioning. Например, вы можете работать с OData и включить версионный обозреватель API, позволяющий добавлять версионную документацию к вашим службам. Если вам не нравится украшать свои контроллеры и методы атрибутами, вы можете использовать подход, основанный на соглашениях о контроллерах.