Хотя ASP.NET Core 7 является последней версией среды разработки веб-приложений Microsoft с открытым исходным кодом, она использует преимущества бесчисленных важных функций предыдущих версий .NET. Одной из таких важных функций являются токены отмены, которые обеспечивают возможность корректной обработки многопоточных приложений.
При работе с приложениями ASP.NET Core рекомендуется отключать длительные операции (такие как запрос к базе данных или фоновый процесс) либо по истечении определенного периода времени, либо по запросу пользователя, чтобы приложение могло освободить ресурсы и оставаться отзывчивым. Здесь в игру вступают токены отмены.
В этой статье обсуждаются токены отмены, их полезность и способы их использования в минимальных обработчиках API в ASP.NET Core. Для работы с примерами кода, представленными в этой статье, в вашей системе должна быть установлена предварительная версия Visual Studio 2022. Если у вас еще нет копии, вы можете скачать Visual Studio 2022 здесь.
Создайте минимальный проект веб-API ASP.NET Core 7 в Visual Studio 2022.
Прежде всего давайте создадим минимальный проект API ASP.NET Core в Visual Studio. Выполнение этих шагов приведет к созданию нового проекта веб-API ASP.NET Core 7 в предварительной версии Visual Studio 2022:
- Запустите среду разработки Visual Studio 2022 Preview.
- Нажмите «Создать новый проект».
- В окне «Создать новый проект» выберите «ASP.NET Core Web API» из списка отображаемых шаблонов.
- Нажмите “Далее.
- В окне «Настроить новый проект» укажите имя и местоположение нового проекта.
- При желании установите флажок «Поместить решение и проект в один каталог», в зависимости от ваших предпочтений.
- Нажмите “Далее.
- В следующем окне «Дополнительная информация» снимите флажок «Использовать контроллеры…», поскольку в этом примере мы будем использовать минимальные API. В поле «Тип аутентификации» оставьте значение «Нет» (по умолчанию).
- Убедитесь, что флажки «Включить Docker», «Настроить HTTPS» и «Включить поддержку Open API» сняты, поскольку мы не будем здесь использовать ни одну из этих функций.
- Нажмите Создать.
Мы будем использовать этот проект веб-API ASP.NET Core 7 для создания минимальных конечных точек API и работы с токенами отмены.
Что такое токены отмены? Когда нам следует их использовать?
CancellationToken — это облегченный объект, созданный экземпляром CancellationTokenSource. Когда CancellationTokenSource отменяется, все потребители CancellationTokens уведомляются соответствующим образом. Кроме того, для свойства IsCancellationRequested экземпляра токена отмены установлено значение true, что указывает на то, что CancellationTokenSource был отменен и была запрошена отмена задачи.
Вы можете использовать CancellationToken, чтобы остановить длительную операцию, когда пользователь отменяет запрос в веб-браузере. Другими словами, использование CancellationToken может помочь вам остановить длительные запросы на использование ресурсов, когда пользователь остановил или обновил веб-страницу.
Вы также можете использовать CancellationTokens для остановки асинхронных задач. В асинхронной задаче отмена означает, что задача должна прекратить выполнение своей текущей деятельности. Асинхронная задача получает токен отмены и проверяет его, чтобы определить, запрошена ли отмена. В этом случае текущая операция должна быть немедленно прекращена.
Длительные запросы в ASP.NET Core
При работе с веб-приложениями вы часто можете использовать длительные задачи, например, вызовы базы данных, обработку файлов и т. д. Когда объект создает одну или несколько длительных операций, он должен передавать токены отмены всем этим операциям. Токены отмены также следует передавать другим внутренним операциям.
В конце концов, объект, создавший длительные операции, может — после достижения крайнего срока или когда пользователь останавливает запрос — распространить уведомление об отмене на эти токены отмены. Следует отметить, что все длительные операции должны учитывать запрос на отмену и отменять длительные операции, чтобы можно было освободить ресурсы.
Прослушивание запросов на отмену в ASP.NET Core
Вы можете прослушивать запросы на отмену, опрашивая значение свойства CancellationToken.IsCancellationRequested, как показано в приведенном ниже фрагменте кода.
while(!cancellationToken.IsCancellationRequested)
{
//Write your code here to perform some operation
}
Другой способ прослушивания запросов на отмену — вызвать метод ThrowIfCancellationRequested, как показано в приведенном ниже фрагменте кода.
while(true)
{
//Write your code here to perform some operation
cancellationToken.ThrowIfCancellationRequested();
}
Вы можете прослушивать запросы на отмену, зарегистрировав обратный вызов, как показано в следующем фрагменте кода.
WebClient webClient = new WebClient();
cancellationToken.Register(() =>
{
webClient.CancelAsync();
});
Создайте минимальный обработчик API в ASP.NET Core.
Давайте теперь смоделируем длительный запрос и посмотрим, как работает отмена. Сначала мы попробуем выполнить длительный запрос без отмены. Напишите следующий фрагмент кода в файле Program.cs.
app.MapGet("/hello", async () =>
{
app.Logger.LogInformation("Request started at: "+DateTime.Now.ToLongTimeString());
await Task.Delay(TimeSpan.FromSeconds(5));
app.Logger.LogInformation("Request completed at: " + DateTime.Now.ToLongTimeString());
return "Success";
});
Когда вы запустите это приложение и достигнете конечной точки /hello, вы увидите, что обработчик выполняется полностью, даже если вы попытаетесь остановить запрос, обновив веб-браузер, как показано на рисунке 1.
Рисунок 1. Без отмены обработчик выполняется полностью, даже если запрос прерван пользователем.
Используйте CancellationToken в минимальном обработчике API.
В следующем листинге кода показано, как можно внедрить CancellationToken в наш обработчик конечной точки и передать этот токен методу Task.Delay.
app.MapGet("/hello", async (CancellationToken token) =>
{
app.Logger.LogInformation("Request started at: " + DateTime.Now.ToLongTimeString());
await Task.Delay(TimeSpan.FromSeconds(5), token);
app.Logger.LogInformation("Request completed at: " + DateTime.Now.ToLongTimeString());
return "Success";
});
Теперь, когда вы запускаете приложение и нажимаете конечную точку /hello, обновление веб-браузера до завершения запроса остановит запрос. Из журналов вы увидите, что запрос никогда не завершается. Вместо этого будет создано исключение TaskCancelledException, поскольку для свойства CancellationToken.IsCancellationRequested будет установлено значение true.
Сообщения об исключениях будут содержать информацию трассировки прерванного запроса, как показано на рисунке 2.
Рисунок 2. Если в коде обработчика используется отмена, запрос, прерванный на полпути, не будет завершен.
Проверка состояния отмены в ASP.NET Core
Часто вам может потребоваться узнать, была ли запрошена отмена для токена отмены. Вы можете проверить состояние отмены, проверив свойство IsCancellationRequested. Свойство IsCancellationRequested будет иметь значение true (т. е. будет иметь логическое значение true), если для токена была запрошена отмена, и false в противном случае.
Как упоминалось выше, метод CancellationToken.ThrowIfCancellationRequested выдаст исключение OperationCanceledException, если экземпляр токена отмены запросил отмену.
В следующем фрагменте кода показано, как свойство IsCancellationRequested можно использовать для проверки того, запросил ли токен отмену. Если запрос был отменен, создается экземпляр OperationCanceledException.
if (token.IsCancellationRequested)
{
app.Logger.LogInformation("Request has been cancelled..");
throw new OperationCanceledException();
}
Использование токенов отмены — хорошая практика, но не всегда рекомендуется в методах действий вашего контроллера. Если запрос изменяет состояние, вы не захотите, чтобы такой запрос был отменен. Токены отмены полезны в основном для длительных вызовов, которые потребляют значительные ресурсы, при этом остановка запроса не будет иметь никаких побочных эффектов.
Наконец, для целей этой статьи мы использовали Task.Delay для имитации длительной операции. Вместо него вы можете заменить любые длительные вызовы, специфичные для требований вашего приложения, такие как вызовы базы данных, сетевые вызовы, операции обработки файлов, загрузка файлов из Интернета и т. д.