Отмена Операции при Отмене HTTP Запроса
Я уже много раз писал про скоординированную отмену, например, здесь и здесь. И даже про улучшения, которые ожидаются в .NET6. Однако, я, к своему удивлению, упустил момент, что Kestrel может перехватывать событие отмены запроса со стороны пользователя, и передавать отмену операции методу действия контроллера.
Таким образом, можно просто передать
CancellationToken в метод контроллера, который запускает длительную операцию:public MyController {
public async Task<IActionResult>
DoSomethingAsync(CancellationToken token)
{
…
}
}
Entity Framework и ADO, а также, например,
HttpClient, принимают токен отмены, и в случае отмены операции выбрасывают TaskCanceledException. В методе действия контроллера вы можете перехватить и обработать это исключение.В своём коде вы можете использовать два способа:
- булево свойство
IsCancellationRequested, которое вы можете проверять вручную в цикле или между вызовами длительных операций,- метод
ThrowIfCancellationRequested(), выбрасывающий исключение OperationCanceledException, которое вы впоследствии можете обработать.Необработанные исключения в методах контроллера – это, конечно, плохая идея. Чтобы не засорять код контроллеров их обработкой, можно использовать фильтр исключений, например, такой:
public class OperationCanceledExceptionFilter :
ExceptionFilterAttribute
{
public OperationCanceledExceptionFilter()
{}
public override void OnException(ExceptionContext context)
{
if(context.Exception is OperationCanceledException)
{
context.ExceptionHandled = true;
context.Result = new StatusCodeResult(400);
}
}
}
Обратите внимание, что мы перехватываем исключение
OperationCanceledException. TaskCanceledException является его потомком, поэтому также будет перехвачено.Также имейте в виду, что пользователь, отменивший запрос, конечно, не увидит никакого сообщения или кода ответа от сервера, поэтому какой именно код ответа будет использован, не играет роли, хотя формально существует код 499 Client Closed Request.
Стоит отметить ещё одну интересную особенность токена отмены. Swagger «понимает» смысл передачи его в метод действия контроллера, поэтому он не включает его в общедоступные параметры метода. Таким образом, в сгенерированной документации API у вас не будет лишних параметров конечных точек.
Конечно, токен отмены в методах действия контроллеров стоит добавлять с умом. Например, вы вряд ли хотите, чтобы транзакция по изменению сущности в базе поддерживала отмену посреди операции. Таким образом, наиболее безопасно использовать токен отмены в операциях без побочных эффектов, вроде получения данных от API.
Источники:
- https://www.youtube.com/watch?v=b5dyPJ3zyRg
- https://andrewlock.net/using-cancellationtokens-in-asp-net-core-mvc-controllers/