Можете ли вы вернуть 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
Извините за плохое форматирование, это лучшее, что я мог сделать с помощью мобильного