Asp.net WebAPI: отмененные (отмененные) запросы

Во-первых, это обсуждение, если кто-то из вас, ребята, делает такую ​​вещь, как аннулирование запроса в контроллерах WebAPI (возможно, также применимый для MVC).

В частности, я имею в виду следующий сценарий: Клиент (обычно браузер) запускает запросы, перемещается или более общий, прерывает запрос по любой причине. Теперь запрос прерывается с клиентской стороны и больше не рассматривается. Но на стороне сервера запрос все еще выполняется и, как правило, может делать две вещи, особенно интересные:

  • Сделайте (тяжелый) DB-Query
  • Сделать (тяжелый) вызов службы другой службе

И все в порядке в конце (по крайней мере, когда это будет операция свободного чтения с побочным эффектом, по крайней мере).

Кто-нибудь обрабатывает отмену текущего запроса/услуги в таком случае?

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

Ответ 1

В то время, когда WebAPI не поддерживает отмену, API OWIN абсолютно не работает. Если вы используете WebAPI 2.0, вы будете работать поверх OWIN и сможете получить доступ к контексту с помощью оболочек Microsoft, используя GetOwinContext().

Удобно, чтобы аннулирование распространялось с использованием значения CancellationToken, выставленного с помощью свойства CallCancelled объекта OwinRequest. Вы можете собрать все это, чтобы получить маркер внутри метода контроллера:

public async Task Get()
{
    var cancellation = Request.GetOwinContext().Request.CallCancelled;

    await database.FooAsync(cancellation);
}

Это довольно уродливо. Вы должны будете сделать этот вызов в каждом методе, который должен обрабатывать отмену, и это не очень хорошо работает с предлагаемым будущим, где WebAPI предоставит вам этот CancellationToken. Вместо этого было бы лучше, если бы мы могли сделать это в параметре?

public async Task Get(CancellationToken cancellation)
{
    await database.FooAsync(cancellation);
}

Чтобы сделать это, вы можете создать привязку настраиваемого параметра, которая захватывает CancellationToken из контекста OWIN:

public class OwinCancellationTokenBinding : HttpParameterBinding
{
    public OwinCancellationTokenBinding(HttpParameterDescriptor parameter)
        : base(parameter)
    {
    }

    public override Task ExecuteBindingAsync(
        ModelMetadataProvider metadataProvider, 
        HttpActionContext actionContext,
        CancellationToken cancellationToken)
    {
        actionContext.ActionArguments[Descriptor.ParameterName]
            = actionContext.Request.GetOwinContext().Request.CallCancelled;

        return Task.FromResult<object>(null);
    }
}

Чтобы использовать это, вы можете зарегистрировать привязку с помощью HttpConfiguration, используя ее коллекцию ParameterBindingRules:

config.ParameterBindingRules.Add(p
    => p.ParameterType == typeof(CancellationToken)
    ? new OwinCancellationTokenBinding (p)
    : null);

Это правило соответствует любому параметру типа CancellationToken. Здесь вы можете создать любое правило, которое соответствует параметрам, для которых вы хотите получить это значение.