Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Поддерживая существующие уже какое-то время Web API проекты, мы нередко сталкиваемся с проблемой устаревания логики методов контроллеров и необходимостью ее изменения в соответствии с новыми требованиями. Но, как правило, на момент возникновения такой необходимости, уже существует определенное число сервисов, использующих текущую реализацию наших API, и не нуждающихся в ее модернизации. Более того, такие сервисы могут легко «сломаться» при изменении используемых ими API.
Для решения такого рода проблем в ASP.Net Core существует механизм версионирования API – когда контроллеры и их методы могут существовать одновременно в разных версиях. В таком случае, те сервисы, которым достаточно существующего состояния используемых ими API, могут продолжать использовать определенные версии этих API, а для сервисов, которые требуют модернизации логики контроллеров, мы можем создавать новые параллельные версии, и все эти версии могут работать в нашем проекте одновременно.
В Visual Studio создаем новый проект ASP.NET Core Web API:
В новый проект добавляем NuGet пакет: Microsoft.AspNetCore.Mvc.Versioning
Для этого в SolutionExplorer (Обозреватель решений) правой кнопкой мыши жмем по названию рабочего проекта и выбираем Manage NuGet Packages… (Управление пакетами Nuget).
Далее переходим на крайнюю левую вкладку Browse, и в строку поиска вводим название устанавливаемого пакета NuGet.
В левом окне выбираем нужный нам пакет, а в правом жмем кнопку Install.
В метод ConfigureServices класса Startup.cs добавляем строку "services.AddApiVersioning();":
public void ConfigureServices(IServiceCollection services)
{
// другой код
services.AddControllers();
services.AddApiVersioning();
// другой код
}
В папке ‘Controllers’ создаем новую папку “V2”.
В созданную папку добавляем новый API контроллер:
И называем его WeatherForecastController, так же, как и созданный по умолчанию контроллер.
В новый контроллер добавляем логику уже существующего контроллера, и немного
изменяем ее. Также добавляем к контроллеру атрибут [ApiVersion("2.0")]
и в атрибуте Route изменяем маршрут:
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
[ApiVersion("2.0")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Сильный мороз", "Мороз", "Холодно", "Прохладно", "Свежо", "Тепло", "Духота", "Жара", "Сильная жара"
};
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-40, 40),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
Маршрут нового контроллера теперь будет: "/api/v2/WeatherForecast".
К автоматически созданному с проектом контроллеру добавим атрибут [ApiVersion("1.0")]:
[ApiController]
[Route("[controller]")]
[ApiVersion("1.0")]
public class WeatherForecastController : ControllerBase
{
// созданная по умолчанию логика
}
Таким образом, мы имеем две версии по сути одного контроллера – 1 и 2.
Маршрут вызова первого контроллера остается прежним, но теперь мы обязаны сообщить маршрутизатору нужную нам версию.
Передать версию контроллера можно несколькими способами:
в параметрах запроса: https://localhost:44335/WeatherForecast?api-version=1.0
в HTTP заголовках запроса. Для этого нужно видоизменить метод AddApiVersioning в методе ConfigureServices класса Startup.cs:
services.AddApiVersioning(config =>
{
config.ApiVersionReader = new HeaderApiVersionReader("api-version");
});
и добавить в запрос соответствующий заголовок:
в URL запроса: https://localhost:44335/api/v2/WeatherForecast
Вызовем метод первого контроллера. Для этого воспользуемся способом передачи версии через параметры запроса.
Запустим наш проект, и в браузере или инструменте для работы с API (Postman, Insomnia и т.д.) введем Url: “https://localhost:44335/WeatherForecast?api-version=1.0”. Разумеется, порт нужно указывать соответствующий вашему приложению.
Получим результат:
[
{
"date":"2022-02-06T00:22:54.6248567+06:00",
"temperatureC":-13,
"temperatureF":9,
"summary":"Scorching"
},
{
"date":"2022-02-07T00:22:54.6261864+06:00",
"temperatureC":24,
"temperatureF":75,
"summary":"Freezing"
},
{
"date":"2022-02-08T00:22:54.6261919+06:00",
"temperatureC":-12,
"temperatureF":11,
"summary":"Freezing"
},
{
"date":"2022-02-09T00:22:54.6261927+06:00",
"temperatureC":40,
"temperatureF":103,
"summary":"Sweltering"
},
{
"date":"2022-02-10T00:22:54.6261931+06:00",
"temperatureC":27,
"temperatureF":80,
"summary":"Balmy"
}
]
8. Вызовем метод второго контроллера. Для этого воспользуемся способом передачи версии в Url запроса, так как мы задали его с помощью атрибута [Route("api/v{version:apiVersion}/[controller]")] и должны теперь ему следовать.
Запустим наш проект, и в браузере или инструменте для работы с API (Postman, Insomnia и т.д.) введем Url: “https://localhost:44335/api/v2/WeatherForecast”.
Получим результат:
[
{
"date":"2022-02-06T00:32:43.5572436+06:00",
"temperatureC":27,
"temperatureF":80,
"summary":"Жара"
},
{
"date":"2022-02-07T00:32:43.5577678+06:00",
"temperatureC":14,
"temperatureF":57,
"summary":"Сильная жара"
},
{
"date":"2022-02-08T00:32:43.5577702+06:00",
"temperatureC":15,
"temperatureF":58,
"summary":"Тепло"
},
{
"date":"2022-02-09T00:32:43.5577706+06:00",
"temperatureC":0,
"temperatureF":32,
"summary":"Духота"
},
{
"date":"2022-02-10T00:32:43.5577707+06:00",
"temperatureC":-14,
"temperatureF":7,
"summary":"Тепло"
}
]
Вызов первого контроллера по его первоначальному маршруту без указания версии - https://localhost:44335/WeatherForecast
Предположим, что первый контроллер уже используется несколькими другими проектами (сервисами) в его первоначальном виде, каким он был до добавления в проект версионирования, и никто не собирается рефакторить эти проекты и менять Url для вызова. Они по-прежнему обращаются к нашему API по маршруту “https://localhost:44335/WeatherForecast”.
В таком случае, мы задаем версию по умолчанию, и если версия в запросе не будет указана, то запрос направляется на заданную версию.
Для этого изменим метод AddApiVersioning в методе ConfigureServices класса Startup.cs:
services.AddApiVersioning(config =>
{
config.DefaultApiVersion = new ApiVersion(1, 0);
config.AssumeDefaultVersionWhenUnspecified = true;
});
В данном случае, все запросы, в которых не указана версия, будут направляться на контроллер первой версии. Теперь мы можем вызывать первый контроллер по его первоначальному маршруту, он будет работать.
Мы можем отметить первую версию как устаревшую, немного изменив атрибут версии: [ApiVersion("1.0", Deprecated = true)]
С помощью атрибута MapToApiVersion мы имеем возможность управлять версиями на уровне методов:
[MapToApiVersion("3.0")]
[HttpGet]
public int DoSomething()
{
// логика метода
}