Web API OWIN启动时的异常处理

9
在我的C# Web API中,我试图添加一个全局异常处理程序。我一直在使用自定义的全局ExceptionFilterAttribute来处理异常并返回一个HttpResponseMessage:
public override void OnException(HttpActionExecutedContext context)
{
    ...

    const string message = "An unhandled exception was raised by the Web API.";
    var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.InternalServerError)
    {
        Content = new StringContent(message),
        ReasonPhrase = message
    };
    context.Response = httpResponseMessage;
}

这在处理控制器级别的异常时效果很好。

然而,在开发过程中,由于数据库连接问题,我们从OWIN启动文件中抛出了一个错误,但是返回了标准的IIS异常,而不是通过全局异常处理程序,并且完整的HTML被返回给API消费者。

我尝试了几种不同的方法来捕获在我的OWIN启动中抛出的异常:

自定义ApiControllerActionInvoker

public class CustomActionInvoker : ApiControllerActionInvoker
{
    public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
    {
        var result = base.InvokeActionAsync(actionContext, cancellationToken);

        if (result.Exception != null && result.Exception.GetBaseException() != null)
        {
            ...
        }

        return result;
    }
}

自定义 ExceptionHandler

public class CustomExceptionHandler : ExceptionHandler
{
    public override void Handle(ExceptionHandlerContext context)
    {
        ...

        base.Handle(context);
    }

    public override bool ShouldHandle(ExceptionHandlerContext context)
    {
        return true;
    }
}

自定义OwinMiddleware组件:

public class CustomExceptionMiddleware : OwinMiddleware
{
    public CustomExceptionMiddleware(OwinMiddleware next) : base(next)
    {
    }

    public override async Task Invoke(IOwinContext context)
    {
        try
        {
            await Next.Invoke(context);
        }
        catch (Exception ex)
        {
            ...
        }
    }
}

最后只需使用 Application_Error

protected void Application_Error(object sender, EventArgs e)
{
    ...
}

但是似乎没有任何方法能够捕获这个异常。

有人知道如何捕获该异常并返回 HttpResponseMessage 吗?或者是否我已经尝试的任何方法都应该有效?

非常感谢任何帮助。


如果这些方法都不起作用,你可以订阅AppDomainUnhandledException事件。当出现异常且没有适当的处理程序来处理异常时,该事件必须触发。 - Amit Kumar Ghosh
谢谢您的评论。我刚刚尝试通过Application_Start方法添加订阅UnhandledException,但似乎没有捕获到异常。在Web API中,UnhandledException是否一定有效? - Dan Ellis
文档中说,这是在发生任何未处理的异常时必须触发的事件。 - Amit Kumar Ghosh
找到解决方案了吗? - ajpetersen
你最终选择了哪种解决方案? - tymtam
我的解决方案可行且是唯一的答案。你能将其标记为正确答案吗? - bikeman868
1个回答

5

我有一个应用程序可以正确地执行这个功能。在我的情况下,我编写了一个中间件类,它始终返回一条消息,告诉调用者该服务不可用,因为启动过程中发生了错误。在我的解决方案中,这个类被称为FailedSetupMiddleware。它的大纲如下:

public class FailedSetupMiddleware
{
    private readonly Exception _exception;

    public FailedSetupMiddleware(Exception exception)
    {
        _exception = exception;
    }

    public Task Invoke(IOwinContext context, Func<Task> next)
    {
        var message = ""; // construct your message here
        return context.Response.WriteAsync(message);
    }
}

在我的Configuration类中,我有一个try...catch块,用于在配置过程中出现异常的情况下,仅使用FailedSetupMiddleware配置OWIN管道。

我的OWIN启动类看起来像这样:

[assembly: OwinStartup(typeof(Startup))]

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        try
        {
            //
            // various app.Use() statements here to configure
            // OWIN middleware
            //
        }
        catch (Exception ex)
        {
          app.Use(new FailedSetupMiddleware(ex).Invoke);
        }
    }
}

一旦以这种方式抛出异常,如何在不重启应用程序池的情况下控制重试逻辑?回到原始帖子,如果OWIN语句依赖于数据库连接,并且该数据库连接短暂中断,则您的解决方案将清除异常并且不会重新触发“启动”类,直到应用程序池被重启。这不是最理想的情况;但是,如果您重新抛出异常,则OWIN启动类将在后续页面刷新时重新运行。唯一的问题是用户将无法获得干净的错误页面。 - spyder1329
通过这种实现方式,如果应用程序在 OWIN 管道配置阶段抛出异常,则它将永久处于返回所有后续请求所抛出的异常状态。这是有意设计的。如果没有这个 try/catch 语句,调试管道配置问题将非常困难。 - bikeman868

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