来自ASP.Net Web API 2的统一,一致的错误响应

我正在开发一个Web API 2应用程序,我正在尝试以统一的方式格式化错误重定位(这样消费者也将知道他们可以检查哪些数据对象/结构以获得有关错误的更多信息)。 这是我到目前为止所得到的:

{ "Errors": [ { "ErrorType":5003, "Message":"Error summary here", "DeveloperAction":"Some more detail for API consumers (in some cases)", "HelpUrl":"link to the docs etc." } ] } 

这适用于应用程序本身抛出的exception(即内部控制器)。 但是,如果用户请求错误的URI(并获得404)或使用错误的动词(并获得405)等,Web Api 2会发出默认错误消息,例如

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

有没有办法捕获这些类型的错误(404,405等)并将它们格式化为上面第一个例子中的错误响应?

到目前为止,我已经尝试过:

  • 自定义ExceptionAttribute inherting ExceptionFilterAttribute
  • 自定义ControllerActionInvoker加入ApiControllerActionInvoker
  • IExceptionHandler (Web API 2.1的新全局error handlingfunction)

但是,这些方法都不能捕获这些类型的错误(404,405等)。 关于如何实现这一目标的任何想法?

……或者,我是否采取了错误的方式? 我是否应该仅针对应用程序/用户级别错误格式化我的特定样式的错误响应,并依赖404之类的默认错误响应?

您可以覆盖DelegatingHandler抽象类并拦截对客户端的响应。 这将使您能够返回您想要的内容。

这里有一些信息。 http://msdn.microsoft.com/en-us/library/system.net.http.delegatinghandler(v=vs.118).aspx

这是Web Api管道的海报,显示了可以覆盖的内容。 http://www.asp.net/posters/web-api/asp.net-web-api-poster.pdf

像这样创建一个Handler类来覆盖响应

 public class MessageHandler1 : DelegatingHandler { protected override Task 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()); 

更新正如Kiran在评论中提到的,您可以使用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"; } } } 

我和@Dan H提到的方式一样

  public class ApiGatewayHandler : DelegatingHandler { protected async override Task 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()); } } } 

添加了如下所示的路由,现在它点击了无效url的try catch

  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" });