Обработка исключений в веб-API ASP.NET
В этой статье описывается обработка ошибок и исключений в веб-API ASP.NET.
HttpResponseException
Что произойдет, если контроллер веб-API создает неперехваченное исключение? По умолчанию большинство исключений претворяются в HTTP-ответ с кодом состояния 500, внутренняя ошибка сервера.
Тип HttpResponseException является особым случаем. Это исключение возвращает любой код состояния HTTP, указанный в конструкторе исключений. Например, следующий метод возвращает значение 404, "Не найдено", если параметр id недопустим.
public Product GetProduct(int id)
{
Product item = repository.Get(id);
if (item == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return item;
}
Для более полного контроля над ответом можно также создать все ответное сообщение и включить его в httpResponseException:
public Product GetProduct(int id)
{
Product item = repository.Get(id);
if (item == null)
{
var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent(string.Format("No product with ID = {0}", id)),
ReasonPhrase = "Product ID Not Found"
};
throw new HttpResponseException(resp);
}
return item;
}
Фильтры исключений
Вы можете настроить обработку исключений в веб-API, написав фильтр исключений. Фильтр исключений выполняется, когда метод контроллера создает любое необработанное исключение, которое не является исключением HttpResponseException . Тип HttpResponseException является особым случаем, так как он предназначен специально для возврата HTTP-ответа.
Фильтры исключений реализуют интерфейс System.Web.Http.Filters.IExceptionFilter . Самый простой способ написать фильтр исключений — наследовать от класса System.Web.Http.Filters.ExceptionFilterAttribute и переопределить метод OnException .
Примечание
Фильтры исключений в веб-API ASP.NET аналогичны фильтрам в ASP.NET MVC. Однако они объявляются в отдельном пространстве имен и выполняются отдельно. В частности, класс HandleErrorAttribute , используемый в MVC, не обрабатывает исключения, создаваемые контроллерами веб-API.
Ниже приведен фильтр, который преобразует исключения NotImplementedException в код состояния HTTP 501, Not Implemented:
namespace ProductStore.Filters
{
using System;
using System.Net;
using System.Net.Http;
using System.Web.Http.Filters;
public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is NotImplementedException)
{
context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
}
}
}
}
Свойство Response объекта HttpActionExecutedContext содержит ответное сообщение HTTP, которое будет отправлено клиенту.
Регистрация фильтров исключений
Существует несколько способов регистрации фильтра исключений веб-API:
- Для действия
- Для контроллера
- Глобально
Чтобы применить фильтр к определенному действию, добавьте его в качестве атрибута в действие:
public class ProductsController : ApiController
{
[NotImplExceptionFilter]
public Contact GetContact(int id)
{
throw new NotImplementedException("This method is not implemented");
}
}
Чтобы применить фильтр ко всем действиям на контроллере, добавьте фильтр в качестве атрибута в класс контроллера:
[NotImplExceptionFilter]
public class ProductsController : ApiController
{
// ...
}
Чтобы применить фильтр глобально ко всем контроллерам веб-API, добавьте экземпляр фильтра в коллекцию GlobalConfiguration.Configuration.Filters . В этой коллекции фильтры исключений применяются к любому действию контроллера веб-API.
GlobalConfiguration.Configuration.Filters.Add(
new ProductStore.NotImplExceptionFilterAttribute());
Если вы используете шаблон проекта "веб-приложение ASP.NET MVC 4" для создания проекта, поместите код конфигурации веб-API в WebApiConfig
класс , который находится в папке App_Start:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new ProductStore.NotImplExceptionFilterAttribute());
// Other configuration code...
}
}
HttpError
Объект HttpError предоставляет согласованный способ возврата сведений об ошибке в тексте ответа. В следующем примере показано, как вернуть код состояния HTTP 404 (Не найдено) с httpError в тексте ответа.
public HttpResponseMessage GetProduct(int id)
{
Product item = repository.Get(id);
if (item == null)
{
var message = string.Format("Product with id = {0} not found", id);
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
else
{
return Request.CreateResponse(HttpStatusCode.OK, item);
}
}
CreateErrorResponse — это метод расширения, определенный в классе System.Net.Http.HttpRequestMessageExtensions . На внутреннем сервере CreateErrorResponse создает экземпляр HttpError , а затем создает HttpResponseMessage , содержащий httpError.
В этом примере, если метод выполнен успешно, он возвращает продукт в HTTP-ответе. Но если запрошенный продукт не найден, HTTP-ответ содержит httpError в тексте запроса. Ответ может выглядеть следующим образом:
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
Date: Thu, 09 Aug 2012 23:27:18 GMT
Content-Length: 51
{
"Message": "Product with id = 12 not found"
}
Обратите внимание, что httpError был сериализован в JSON в этом примере. Одним из преимуществ использования HttpError является то, что он проходит тот же процесс согласования содержимого и сериализации, что и любая другая строго типизированная модель.
HttpError и проверка модели
Для проверки модели можно передать состояние модели в CreateErrorResponse, чтобы включить ошибки проверки в ответ:
public HttpResponseMessage PostProduct(Product item)
{
if (!ModelState.IsValid)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
// Implementation not shown...
}
Этот пример может возвращать следующий ответ:
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Content-Length: 320
{
"Message": "The request is invalid.",
"ModelState": {
"item": [
"Required property 'Name' not found in JSON. Path '', line 1, position 14."
],
"item.Name": [
"The Name field is required."
],
"item.Price": [
"The field Price must be between 0 and 999."
]
}
}
Дополнительные сведения о проверке модели см. в статье Проверка модели в веб-API ASP.NET.
Использование HttpError с HttpResponseException
Предыдущие примеры возвращают сообщение HttpResponseMessage из действия контроллера, но вы также можете использовать HttpResponseException для возврата HttpError. Это позволяет возвращать строго типизированную модель в обычном случае успешного выполнения, сохраняя при этом httpError при возникновении ошибки:
public Product GetProduct(int id)
{
Product item = repository.Get(id);
if (item == null)
{
var message = string.Format("Product with id = {0} not found", id);
throw new HttpResponseException(
Request.CreateErrorResponse(HttpStatusCode.NotFound, message));
}
else
{
return item;
}
}