如何在WebApi消息处理程序中优雅地返回异常

13

我在我的Mvc\WebApi应用程序中注册了一个全局的异常过滤器:

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中发生异常时,我会得到 Yellow Screen of Death,我的 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的自定义不变。


https://dev59.com/k2nWa4cB1Zd3GeqP5ekK - Timeless
5个回答

8

我应该返回一个 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);
}

5

我成功地使用了基于属性的方法。您可以在应该使用异常处理的每个操作上放置一个自定义属性:

[ExceptionHandling] 
public IEnumerable<string> Get()

您的自定义异常处理属性实现如下:

您的自定义异常处理属性实现如下:

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

如您所见,自定义属性继承自ExceptionFilterAttribute。然后,您只需重写OnException方法。您可以根据业务规则或任何适合您需求的方式来处理异常类型。在某些情况下,您可能只需在服务器上吞下异常。如果要通知客户端,您可以抛出HttpResponseException,其中可以包含所有相关信息,例如消息、调用堆栈、内部异常等。
要获取更多灵感,请查看这篇优秀的博客文章:ASP.NET Web API异常处理

5

我曾经遇到过全局异常过滤器无法运行的问题。解决方案是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


2
在startup.cs文件中:
config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());

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

0
我发现了这篇博客,里面有非常好的例子: 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 ";           
        }  
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接