我正在开发一个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."
}
]
}
这适用于应用程序本身(即控制器内部)抛出的异常。但是,如果用户请求错误的URI(并获得404)或使用错误的动词(并获得405)等,Web Api 2会发出默认错误消息,例如。
{
Message: "No HTTP resource was found that matches the request URI 'http://localhost/abc'."
}
有没有办法捕获这些类型的错误(404,405等)并将它们格式化为上面第一个例子中的错误响应?
到目前为止,我已经尝试过:
ExceptionFilterAttribute
ApiControllerActionInvoker
IExceptionHandler
(Web API 2.1中新的全局错误处理功能)然而,这些方法都没有能够捕获这些类型的错误(404,405等)。关于如何实现这一目标的任何想法?
......或者,我是否采取了错误的方式?我是否应该仅针对应用程序/用户级别错误格式化我的特定样式的错误响应,并依赖404之类的默认错误响应?
答案 0 :(得分:9)
您可以覆盖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<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());
<强>更新强> 正如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";
}
}
}
答案 1 :(得分: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
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" });