【问题标题】:How to gracefully return exception from a WebApi Message Handler如何从 WebApi 消息处理程序优雅地返回异常
【发布时间】:2012-08-05 15:46:29
【问题描述】:

我的 Mvc\WebApi 应用程序中有一个全局 ExceptionFilter 注册为:

public virtual void RegisterHttpFilters(HttpConfiguration config)
{
    config.Filters.Add(new MyExceptionFilter(_exceptionHandler));
}

MyExceptionFilter 在哪里:

public class MyExceptionFilter : ExceptionFilterAttribute
{
    private readonly IMyExceptionHandler m_exceptionHandler;

    public MyExceptionFilter(IMyExceptionHandler exceptionHandler)
    {
        m_exceptionHandler = exceptionHandler;
    }

    public override void OnException(HttpActionExecutedContext context)
    {
        Exception ex = context.Exception;
        if (ex != null)
        {
            object response = null;
            HttpStatusCode statusCode = m_exceptionHandler != null
                ? m_exceptionHandler.HandleException(ex, out response)
                : HttpStatusCode.InternalServerError;
            context.Response = context.Request.CreateResponse(statusCode, response ?? ex);
        }

        base.OnException(context);
    }
}

此过滤器将所有异常作为 json 对象返回,并允许一些 IMyExceptionHandler 实现自定义返回的对象。

这一切都很好。直到我的一些消息处理程序出现异常:

public class FooMessageHandler : DelegatingHandler
{
    private readonly Func<IBar> _barFactory;
    public FooMessageHandler(Func<IBar> barFactory)
    {
        _barFactory = varFactory;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Properties["MY_BAR"] = _barFactory();
        return base.SendAsync(request, cancellationToken);
    }
}

如您所见,此处理程序创建了一些组件并将其放入当前的 http 请求消息中。 当 FuncOfIBar 发生异常时,我会得到黄屏死机。我的 ExceptionFilter 没有被调用。

我试图在消息处理程序中专门捕获异常并返回 HttpResponseException 但它没有改变任何东西 - 仍然得到 YSOD:

public class XApplicationInitializerMessageHandler : DelegatingHandler
{
    private readonly Func<IXCoreApplication> _appFactory;
    private readonly IXExceptionHandler m_exceptionHandler;

    public XApplicationInitializerMessageHandler(Func<IXCoreApplication> appFactory, IXExceptionHandler exceptionHandler)
    {
        ArgumentValidator.EnsureArgumentNotNull(appFactory, "appFactory");
        _appFactory = appFactory;
        m_exceptionHandler = exceptionHandler;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        try
        {
            request.SetApplication(_appFactory());
        }
        catch (Exception ex)
        {
            object resultObject = null;
            HttpStatusCode statusCode = m_exceptionHandler != null
                ? m_exceptionHandler.HandleException(ex, out resultObject)
                : HttpStatusCode.InternalServerError;
            HttpResponseMessage responseMessage = request.CreateResponse(statusCode, resultObject ?? ex);

            throw new HttpResponseException(responseMessage);
        }
        return base.SendAsync(request, cancellationToken);
    }
}

}

我希望我的应用程序的行为是相同的,无论异常发生在哪里:在 ApiController 或消息处理程序中。

怎么做?

我知道 Application_Error,但我想保持 HttpApplication 自定义不可触及。

【问题讨论】:

标签: c# asp.net-web-api


【解决方案1】:

我应该只返回一个HttpResponseMessage,而不是抛出HttpResponseException

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
    try
    {
        request.SetApplication(_appFactory());
    }
    catch (Exception ex)
    {
        object resultObject = null;
        HttpStatusCode statusCode = m_exceptionHandler != null
            ? m_exceptionHandler.HandleException(ex, out resultObject)
            : HttpStatusCode.InternalServerError;
        HttpResponseMessage responseMessage = request.CreateResponse(statusCode, resultObject ?? ex);

        return Task<HttpResponseMessage>.Factory.StartNew(() => responseMessage);
    }
    return base.SendAsync(request, cancellationToken);
}

【讨论】:

    【解决方案2】:

    我对 succes 所做的是使用基于属性的方法。您在每个应该使用异常处理的操作上放置一个自定义属性:

    [ExceptionHandling] 
    public IEnumerable<string> Get()
    

    您的自定义异常处理属性是这样实现的:

    public class ExceptionHandlingAttribute : ExceptionFilterAttribute
    {
        public override void OnException(HttpActionExecutedContext context)
        {}
    

    如您所见,自定义属性继承自 ExceptionFilterAttribute。然后,您只需覆盖 OnException 方法。然后您可以做的是查看异常的类型并根据业务规则或任何适合您的需求来处理它。在某些情况下,您可能只是吞下服务器上的异常。如果要通知客户端,可以抛出一个HttpResponseException,它可以保存所有相关信息,比如消息、调用堆栈、内部异常等。

    如需更多灵感,请查看这篇精彩的博文:ASP.NET Web API Exception Handling

    【讨论】:

      【解决方案3】:

      我遇到了同样的问题,全局异常过滤器没有运行。解决方案是 Web API 异常过滤器必须配置在与 ASP.NET MVC 异常过滤器不同的全局集合中。

      所以要配置 ASP.NET MVC 错误处理,您需要运行:

      System.Web.Mvc.GlobalFilters.Filters.Add(new MyMvcExceptionFilter());
      

      但对于 Web API 错误处理,您必须运行:

      System.Web.Http.GlobalConfiguration.Configuration.Filters.Add(new MyWebApiExceptionFilter());
      

      根据我对 Web API 的有限经验,这种情况很典型:从表面上看,所使用的模式与 ASP.NET MVC 非常相似,因此很快就会熟悉。这给人一种错觉,即编写一段代码将对两个框架都产生影响,但实际上它们在其下的实现在很大程度上或完全独立,您需要编写两个非常相似的代码版本才能完成所有工作工作。

      Web API 异常处理的优秀参考:http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling

      【讨论】:

        【解决方案4】:

        在startup.cs中:

        config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
        

        我发现使用 GlobalExceptionHandler 可以在委托处理程序中捕获异常并返回自定义 JSON 对象。

        【讨论】:

          【解决方案5】:

          我发现这个博客有很好的例子: http://nodogmablog.bryanhogan.net/2016/07/getting-web-api-exception-details-from-a-httpresponsemessage/

          我采用了一些更新的代码:

          if ((int)response.StatusCode >= 400)
           {
                exceptionResponse = JsonConvert.DeserializeObject<ExceptionResponse>(LogRequisicao.CorpoResposta);
                LogRequisicao.CorpoResposta = exceptionResponse.ToString() ;
                if (exceptionResponse.InnerException != null)
                       LogRequisicao.CorpoResposta += "\r\n InnerException: " + exceptionResponse.ToString();
           }
          

          使用对象:

          public class ExceptionResponse
              {
                  public string Message { get; set; }
                  public string ExceptionMessage { get; set; }
                  public string ExceptionType { get; set; }
                  public string StackTrace { get; set; }
                  public ExceptionResponse InnerException { get; set; }
          
                  public override String ToString()
                  {
                      return "Message: " + Message + "\r\n "
                          + "ExceptionMessage: " + ExceptionMessage + "\r\n "
                          + "ExceptionType: " + ExceptionType + " \r\n "
                          + "StackTrace: " + StackTrace + " \r\n ";           
                  }  
              }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-07-10
            • 1970-01-01
            • 2015-12-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多