Обработка ошибок в ASP.NET Web API

| Воскресенье, 3 марта, 2013

Метки: ASP.NET Web API Комментарии: 0

ASP.NET Web API упрощает разработку HTTP-сервисов, а также предоставляет много способов возврата полных и информативных сообщений об ошибках для различных ситуаций. Рассмотрим эти возможности.

Для начала, посмотрим как выглядит обычное сообщение об ошибке для Web API:

{ 
"Message": "No HTTP resource was found that matches the request URI 'http://localhost/Foo'.", 
"MessageDetail": "No type was found that matches the controller named 'Foo'." 
}

То есть ошибка, это просто коллекция пар ключей и значений, которая сообщает нам, что пошло не так. Эта коллекция отсылается клиенту через HTTP-запросы. В примере выше содержимое представлено в формате JSON.

Но если указать в запросе в заголовке Accept "text/html", то ответ будет в xml виде:

<Error> 
  <Message>No HTTP resource was found that matches the request URI 'http://localhost/Foo'.</Message> 
  <MessageDetail>No type was found that matches the controller named 'Foo'.</MessageDetail> 
</Error>

Этот простой формат предоставляет информацию клиентам об ошибках, что дает возможность делать запись в журнал, либо отсылать отчеты на email.

HttpError

Примеры приведенные выше - это объекты HttpError, сериализованные с помощью Json.NET и DataContactSerializer.

Тип HttpError определяет содержательный и расширяемый способ передачи информации об ошибках между различными платформами. Фактически, это просто класс Dictionary, который предоставляет еще несколько вспомогательных конструкторов для создания сообщений об ошибках, исключениях и некорректных состояниях модели.

public HttpError();
public HttpError(string message);
public HttpError(Exception exception, bool includeErrorDetail);
public HttpError(ModelStateDictionary modelState, bool includeErrorDetail);

Так выглядят исключения в формате JSON:

{ 
"Message": "An error has occurred.", 
"ExceptionMessage": "Index was outside the bounds of the array.", 
"ExceptionType": "System.IndexOutOfRangeException", 
"StackTrace": "   at WebApiTest.TestController.Post(Uri uri) in c:\\Temp\\WebApiTest\\WebApiTest\\TestController.cs:line 18\r\n   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClassf.<GetExecutor>b__9(Object instance, Object[] methodParameters)\r\n   at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)\r\n   at System.Threading.Tasks.TaskHelpers.RunSynchronously[TResult](Func`1 func, CancellationToken cancellationToken)" 
}

Так некорректная модель:

{ 
  "Message": "The request is invalid.", 
  "ModelState": 
   { 
       "s": [ "Required property 's' not found in JSON. Path '', line 1, position 2."  ] 
   }
} 

Можно создавать и собственные виды ошибок в классе HttpError, например так:

public HttpResponseMessage Get()
{
   HttpError myCustomError = new HttpError("My custom error message") { { "CustomErrorCode", 37 } };
   return Request.CreateErrorResponse(HttpStatusCode.BadRequest, myCustomError);
}

Результат будет таким:

{ 
  "Message": "My custom error message", 
  "CustomErrorCode": 37 
}

В этом примере метод возвращает ошибку с помощью Request.CreateErrorMessage. Пользовательская ошибка обернута в класс HttpResponseMessage. Это рекомендованный способ создания ответов c сообщениями об ошибках. Ниже приведены расширяемые методы для возврата такой информации:

public static HttpResponseMessage CreateErrorResponse(this HttpRequestMessage request, HttpStatusCode statusCode, Exception exception);
public static HttpResponseMessage CreateErrorResponse(this HttpRequestMessage request, HttpStatusCode statusCode, HttpError error);
public static HttpResponseMessage CreateErrorResponse(this HttpRequestMessage request, HttpStatusCode statusCode, ModelStateDictionary modelState);
public static HttpResponseMessage CreateErrorResponse(this HttpRequestMessage request, HttpStatusCode statusCode, string message);
public static HttpResponseMessage CreateErrorResponse(this HttpRequestMessage request, HttpStatusCode statusCode, string message, Exception exception);

Но можно и просто генерировать любое исключение в методе действия. Web API автоматически перехватывает исключение, конвертирует в ответ сервера с кодом 500 (Internal Server Error) и возвращает сообщение в точно таком же формате, как и с методом CreateErrorResponse.

HttpResponseException

Но как быть, если нужно вернуть ошибку в методе, который возвращает объекты не являющиеся классами HttpResponseMessage? Тут на помощь приходит класс HttpResponseException. Данный тип исключения определен специально для Web API и служит двум целям:

  1. Позволяет возвращать специальные ответы HTTP из методов, которые не возвращают HttpResponseMethod.
  2. Упрощает код в методах Web API, выполняя вывод ответа сервера с информацией об ошибке немедленно.

Технически, HttpResponseException может быть использован для возврата любых http-ответов, но особенно полезен данный класс для ошибок. Это позволяет нам писать методы следующим образом:

public Person Get(int id)
{
   if (!_contacts.ContainsKey(id))
   {
      throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, String.Format("Contact {0} not found.", id)));
   }
   return _contacts[id];
}

Метод предназначен для возврата типа Person, но мы можем также и вернуть пользовательскую ошибку в случае отсутствия возвращаемого значения.

Подробное описание ошибок

В начале статьи в примере упоминался параметр «includeErrorDetails» в конструкторе класса HttpError. Это для случаев, когда клиенту нужно предоставить отладочную информацию, для разрешения проблем на сервере. По умолчанию подробная информация не высылается удаленным клиентам, но предоставляется клиентам на локальном компьютере. В самом первом примере статьи, локальный клиент получит:

{ 
"Message": "No HTTP resource was found that matches the request URI 'http://localhost/Foo'.", 
"MessageDetail": "No type was found that matches the controller named 'Foo'." 
}

А до удаленного клиента дойдет только:

{ 
"Message": "No HTTP resource was found that matches the request URI 'http://localhost/Foo'." 
}

MessageDetails содержит специфичную информацию, которую удаленные клиенты в большинстве случаев видеть не должны. То есть ошибки содержащие отладочную информацию (exception message, exception type, stack trace) не предоставляют подробное описание по умолчанию, но ошибки состояния моделей (кроме исключений) подробно отсылаются и удаленным клиентам.

Явно задавать возврат подробной информации можно в объекте HttpConfiguration:

config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.LocalOnly;
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Never;

Как было упомянуто выше, значение по умолчанию LocalOnly.

Обработка ошибок для некорректных моделей

Еще один распространенный способ использования обработки ошибок Web API – это немедленный возврат ошибки в случае некорректной модели. Для этого лучше всего реализовать следующий фильтр метода действия:

public class ValidationFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (!actionContext.ModelState.IsValid)
        {
            actionContext.Response = 
                 actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest,   
                 actionContext.ModelState);
         }
     }
}

Можно прописать этот фильтр для каждого метода а можно зарегистрировать его глобально сразу для всех методов:

config.Filters.Add(new ValidationFilterAttribute());

Это позволяет не писать проверку модели в каждом методе, а сразу возвращать ошибку клиенту в случае некорректной модели.

Комментарии
Никто еще не оставил здесь комментарий.
Войдите, чтобы написать комментарий , или воспользуйтесь формой ниже.
 

Copyright © CodeHint.ru 2013-2019