Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/spring-mvc/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 来自ASP.Net Web API 2的统一、一致的错误响应_C#_Asp.net_Asp.net Web Api_Asp.net Web Api2 - Fatal编程技术网

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

C# 来自ASP.Net Web API 2的统一、一致的错误响应,c#,asp.net,asp.net-web-api,asp.net-web-api2,C#,Asp.net,Asp.net Web Api,Asp.net Web Api2,我正在开发一个WebAPI2应用程序,目前我正在尝试以统一的方式格式化错误响应(这样消费者也将知道他们可以检查什么数据对象/结构,以获得有关错误的更多信息)。到目前为止,我得到的是: { "Errors": [ { "ErrorType":5003, "Message":"Error summary here", "DeveloperAction":"Some more detail f

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

{   
    "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'."
}
有没有办法捕获这些类型的错误(404405等)并将它们格式化为上面第一个示例中的错误响应

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

  • 自定义例外属性插入
    ExceptionFilterAttribute
  • 自定义ControllerActionInvoker InHirting
    ApiControllerActionInvoker
  • IEExceptionHandler
    (来自Web API 2.1的新全局错误处理功能)
然而,这些方法都不能捕获这些类型的错误(404405等)。关于如何/如果能够实现这一点,有什么想法吗


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

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

这里有一些信息。

下面是WebAPI管道的海报,展示了可以覆盖的内容。

创建这样的处理程序类来覆盖响应

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;
    }
}
更新 正如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<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());
        }

    }
}

对于应用程序/用户级别的错误,我是否应该仅以我的特定风格格式化错误响应,对于404之类的错误,我是否应该依赖默认错误响应?
…我的观点是,我也越来越倾向于这种方法。感谢您的回答/评论。仅此一点是不够的,因为在某些情况下,由于没有Web API路由匹配,请求甚至不会进入Web API。(此问题特定于IIS托管的场景)。这个问题可以通过在Web API路由的末尾设置一个catch-all路由来避免,该路由可以发回一个定制的404错误消息……其他可能的解决方案是使用一个位于管道中较早位置的OwinMiddleware,这样您就可以将所有逻辑放在一个地方(我自己还没有玩过这个,但我猜这个应该有用)即使路由不正确,它也会对所有api调用命中DelegatingHandler,因为HttpRoutingDispatcher在DelegatingHandler之后被调用。您是正确的,如果请求不是api请求,它不会命中此处理程序,因为非api调用由System.Web.Mvc命名空间处理程序处理。示例:将命中此处理程序r但不会命中此处理程序…假设所有api调用都设置为从开始只是为了清除…在
httproutindispatcher
处进行路由匹配只在SelfHost和内存中场景下发生…@Kiran Charla-您有关于此的文档吗?我的理解是OWIN的全部目的是将应用程序与主机分离。这意味着从DelegatingHandler开始进入应用程序的所有内容都不关心它的宿主方式。这包括HttpRoutingDispatcher。实际上,您可以使用上面的示例来验证这一点…创建一个基于IIS的Web API应用程序,而不使用任何MVC相关的内容。现在拥有模板提供的默认API路由。即
API/{controller}/{id}
…现在发出一个请求,如
localhost/yourwebapplicationname/nonexistingpath
…如果路由匹配确实发生在
HttpRoutingDispatcher
上,那么应该调用您的委托处理程序…但它没有…因为路由匹配发生在通过委托处理程序的请求之前。。。
 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());
        }

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