Можете ли вы вернуть HTTP-ответ из AuthorizeAttribute без исключения исключения?

Я использую AuthorizeAttribute на разных контроллерах, которым может потребоваться вернуть 403 или 429 (слишком много запросов) на основе определенных атрибутов самого запроса. Я реализовал его полностью в рамках пользовательской реализации OnAuthorization, а затем при необходимости добавил новое исключение HttpResponseException с соответствующим кодом ответа. Хорошо работал на моей машине...

В масштабе (много тысяч запросов в минуту) эта реализация отстойна до такой степени, что она разбивала сайт. Перемещение одной и той же логики в действие самого контроллера и просто возвращение соответствующего HttpResponseMessage прекрасно работают с точки зрения перфоманса, поэтому кажется, что затраты на выброс исключения в OnAuthorization являются основной причиной проблем с перфомансом.

Мне нравится идея реализовать это в атрибуте, который я могу использовать, чтобы украсить несколько контроллеров и действий, и я неловко не люблю перемещать даже небольшие количества логики в действия контроллера, которые затем повторяются много раз. Можно ли вернуть соответствующий HTTP-статус из аннотации, не вызывая исключения? Даже если он не наследуется от AuthorizeAttribute, оформление кода таким образом было бы намного предпочтительнее.

Изменить: это Web API 2, а не MVC

Ответы

Ответ 1

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

MVC

Настройка AuthorizationContext.Result позволяет эффективно отменить действие. Установка этого значения предотвратит запуск действия, к которому он привязан, от запуска:

public override void OnAuthorization(AuthorizationContext filterContext)
{
    if(Throw403)
    {
        filterContext.Result = new HttpStatusCodeResult(403);
    }
}

WebAPI

Очень похоже, но вы должны установить свойство HttpActionContext.Response. Одной из удобных особенностей этого является то, что вы получите хороший enum для кода состояния HTTP:

public override void OnAuthorization(HttpActionContext actionContext)
{
    if(Throw403)
    {
        actionContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
    }
}

Ответ 2

Как о персонализированных обработчиках сообщений, где вы могли бы откликнуться даже до нажатия на контроллер? Это происходит в начале процесса.

Изменить - вставка соответствующей информации с веб-сайта

Обработчик делегирования также может пропускать внутренний обработчик и напрямую создавать ответ:

public class MessageHandler2    :DelegatingHandler
{
 protected override    Task<HttpResponseMessage> SendAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
{
    // Create the response.
    var response = new HttpResponseMessage(HttpStatusCode.OK)
    {
        Content = new StringContent("Hello!")
    };

    // Note: TaskCompletionSource creates a task that does not contain a delegate.
    var tsc = new TaskCompletionSource<HttpResponseMessage>();
    tsc.SetResult(response);   // Also sets the task state to "RanToCompletion"
    return tsc.Task;
   }
}

И вот как вы регистрируете обработчик

  public static class WebApiConfig
    {
  public static void        Register(HttpConfiguration config)
{
    config.MessageHandlers.Add(new MessageHandler1());
    config.MessageHandlers.Add(new MessageHandler2());

    // Other code not shown...
}
}

Ссылка здесь: http://www.asp.net/web-api/overview/advanced/http-message-handlers

Извините за плохое форматирование, это лучшее, что я мог сделать с помощью мобильного