Обработка исключений в WebAPI
Обработка исключений - одна из важнейших тем, о которой часто забывают и оставляют “на потом”. Типичные поведение веб-приложения при возникновении необработанного исключения - ответить клиенту с кодом HTTP 500
и формулировкой “что-то пошло не так”.
Это логично, когда мы генерируем HTML-страницы. Но реализация REST API часто требует от нас другого. Например, при появлении необработанного исключения из предметной области выдавать какой-то специфичный код возврата, чтобы наши клиенты знали в чем проблема.
Первое, что приходит в голову — добавить в контроллер логику, которая будет генерировать нужные коды возврата для каждой ситуации.
В WebAPI есть такой механизм — Exception Filter — он позволяет для таких ситуаций создать более прозрачный код.
Создание Exception Filter
Например, имеется WebAPI контроллер, который на определенном наборе данных генерирует NotSupportedException
:
public class UsersController : ApiController
{
[HttpPost]
public void Create(UsersCreateModel model)
{
if (....)
throw new NotSupportedException();
}
}
Если обратиться к такому методу через HTTP и смоделировать ситуацию, когда выбрасывается данное исключение, то в ответ будет отправлено сообщение похожее на такое:
HTTP/1.1 500 Internal Server Error
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
Content-Length: 2687
{"Message":"An error has occurred.","ExceptionMessage": ...
Если приложение запущено в отладочной среде, здесь даже будет содержаться информация о типе исключения с подробным описанием.
Клиент не всегда ожидает подобного поведения. К примеру, если он отправил некорректный набор данных почему бы не вернуть ему Bad Request
с описанием причины? Чтобы переопределить это поведение создадим ExceptionFilter - наследник ExceptionFilterAttribute
, в котором переопределен метод OnException
:
public class NotSupportedExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is NotSupportedException)
{
context.Response = new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent("Data you provided is not supported.")
};
}
}
}
Как видно из этого кода, этот фильтр проверяет тип обрабатываемого исключения и при необходимости подменяет ответ. Осталось только подключить этот фильтр к приложению.
Подключение к приложению
Подключить Exception Filter можно двумя способами:
- Разметить атрибутом нужный контроллер/действия
- Подключить глобально
Если логика работы данного фильтра актуальна только для конкретного контроллера или действия, то имеет смысл разметить их данным атрибутом, чтобы фильтр начал работу:
public class UsersController : ApiController
{
[HttpPost]
[NotSupportedExceptionFilter]
public void Create(UsersCreateModel model)
{
throw new NotSupportedException();
}
}
Возможна такая ситуация, когда логика фильтра должна работать для всего приложения. Например, мы обрабатываем все исключения, связанные с бизнес логикой и формируем корректные ответы. В этом случае можно не размечать контроллеры данным атрибутом, а добавить их при конфигурировании глобально:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new NotSupportedExceptionFilterAttribute());
Теперь при выбрасывании необработанных исключений NotSupportedException
, клиенту будет отправлен HTTP-ответ следующего вида:
HTTP/1.1 400 Bad Request
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 32
Content-Type: text/plain; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
Data you provided is not supported.
Добавить комментарий