Единообразные, последовательные ответы об ошибках от ASP.Net Web API 2
Я разрабатываю приложение Web API 2, и в настоящее время я пытаюсь форматировать ошибки resposnes единым способом (чтобы потребитель также знал, какой объект/структура данных они могут проверять, чтобы получить больше информации об ошибках), Это то, что у меня есть до сих пор:
{
"Errors":
[
{
"ErrorType":5003,
"Message":"Error summary here",
"DeveloperAction":"Some more detail for API consumers (in some cases)",
"HelpUrl":"link to the docs etc."
}
]
}
Это отлично подходит для исключений, создаваемых самим приложением (то есть внутри контроллеров). Однако, если пользователь запрашивает плохой URI (и получает 404) или использует неправильный глагол (и получает 405) и т.д., Web Api 2 выплескивает сообщение об ошибке по умолчанию, например.
{
Message: "No HTTP resource was found that matches the request URI 'http://localhost/abc'."
}
Есть ли способ улавливать эти ошибки (404, 405 и т.д.) и форматировать их в ответ на ошибку в первом примере выше?
До сих пор я пробовал:
- Настраивание пользовательских исключенийAttribute
ExceptionFilterAttribute
- Пользовательский ControllerActionInvoker inherting
ApiControllerActionInvoker
-
IExceptionHandler
(новая функция глобальной обработки ошибок из Web API 2.1)
Однако ни один из этих подходов не способен поймать такие ошибки (404, 405 и т.д.). Любые идеи о том, как/если это может быть достигнуто?
... или я об этом неправильно? Должен ли я форматировать ответы об ошибках в моем конкретном стиле для ошибок приложения/уровня пользователя и полагаться на ответы об ошибках по умолчанию для таких вещей, как 404?
Ответы
Ответ 1
Вы можете переопределить абстрактный класс DelegatingHandler и перехватить ответ клиенту. Это даст вам возможность вернуть то, что вы хотите.
Вот некоторая информация об этом.
http://msdn.microsoft.com/en-us/library/system.net.http.delegatinghandler(v = vs .118).aspx
Здесь представлен плакат веб-конвейера Api, который показывает, что можно переоценить.
http://www.asp.net/posters/web-api/asp.net-web-api-poster.pdf
Создайте класс Handler, подобный этому, чтобы переопределить ответ
public class MessageHandler1 : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
Debug.WriteLine("Process request");
// Call the inner handler.
var response = base.SendAsync(request, cancellationToken);
Debug.WriteLine("Process response");
if (response.Result.StatusCode == HttpStatusCode.NotFound)
{
//Create new HttpResponseMessage message
}
;
return response;
}
}
В вашем классе WebApiConfig.cs добавьте обработчик.
config.MessageHandlers.Add(new MessageHandler1());
UPDATE
Как упоминает Киран в комментариях, вы можете использовать OwinMiddleware для перехвата ответа, возвращаемого клиенту. Это будет работать для MVC и Web Api, работающих на любом хосте.
Вот пример того, как получить ответ и изменить его, как он идет на клиента.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use(typeof(MyMiddleware));
}
}
public class MyMiddleware : OwinMiddleware
{
public MyMiddleware(OwinMiddleware next) : base(next) { }
public override async Task Invoke(IOwinContext context)
{
await Next.Invoke(context);
if(context.Response.StatusCode== 404)
{
context.Response.StatusCode = 403;
context.Response.ReasonPhrase = "Blah";
}
}
}
Ответ 2
Я сделал так же, как @Dan H, упомянутый
public class ApiGatewayHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
var response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.NotFound)
{
var objectContent = response.Content as ObjectContent;
return await Task.FromResult(new ApiResult(HttpStatusCode.NotFound, VmsStatusCodes.RouteNotFound, "", objectContent == null ? null : objectContent.Value).Response());
}
return response;
}
catch (System.Exception ex)
{
return await Task.FromResult(new ApiResult(HttpStatusCode.BadRequest, VmsStatusCodes.UnHandledError, ex.Message, "").Response());
}
}
}
Добавлена маршрутизация, как показано ниже, и теперь она попадает в try catch для недопустимого URL
config.Routes.MapHttpRoute(name: "DefaultApi",routeTemplate: "api/{controller}/{id}",defaults: new { id = RouteParameter.Optional });
config.Routes.MapHttpRoute(name: "NotFound", routeTemplate: "api/{*paths}", defaults: new { controller = "ApiError", action = "NotFound" });