Elmah在使用HttpResponseException时无法记录异常。

12
在我的WebApi代码中,我会引发HttpResponseException,它可以短路请求管道并生成有效的Http响应。但是,我正在尝试将webApi与elmah日志集成,但HttpResponseExeptions没有显示出来。
我已经为elmah设置了web.config,并具有以下代码:
在Global.asx.cs中:
static void ConfigureWebApi(HttpConfiguration config)
{  
    config.Filters.Add(new ServiceLayerExceptionFilter());            
    config.Filters.Add(new ElmahHandledErrorLoggerFilter());
    config.DependencyResolver = new WebApiDependencyResolver(ObjectFactory.Container);                            
}     

筛选器:

public class ElmahHandledErrorLoggerFilter : ExceptionFilterAttribute
{
    public override void OnException(HttpActionExecutedContext actionExecutedContext)
    {
        base.OnException(actionExecutedContext);
        ErrorSignal.FromCurrentContext().Raise(actionExecutedContext.Exception);
    }
}

引发异常的代码:

public Task<FileUpModel> UploadFile()
{
    if (Request.Content.IsMimeMultipartContent())
    {                
        var provider = new TolMobileFormDataStreamProvider("C:\images\");

        var task = Request.Content.ReadAsMultipartAsync(provider).ContinueWith(
        t =>
        {

            if (t.IsFaulted || t.IsCanceled)
                throw new HttpResponseException(HttpStatusCode.InternalServerError);

            var fileInfo = provider.FileData.FirstOrDefault();
            if (fileInfo == null)
                // the exception here isn't logged by Elmah?!
                throw new HttpResponseException(HttpStatusCode.InternalServerError);    

            var uploadModel = new FileUpModel { success = true };
            return uploadModel;
        });

        return task;
    }
    else
    {
        throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
    }            
}   

有没有已经实现过这个功能的人能告诉我我做错了什么?

3个回答

11

如上所述,Elmah过滤器在您引发HttpResponseException时不会捕获和记录任何内容。 更具体地说,如果使用以下语法:

return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "It was a bad request");
or
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "HttpResponseException - This request is not properly formatted"));

我希望在两种情况下捕获和记录错误。方法是使用 "ActionFilterAttribute",覆盖 "OnActionExecuted",然后检查 actionExecutedContext.Response.IsSuccessStatusCode。

public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
    // when actionExecutedContext.Response is null, the error will be caught and logged by the Elmah filter
    if ((actionExecutedContext.Response != null) && !actionExecutedContext.Response.IsSuccessStatusCode)
    {
        try
        {
            var messages = (System.Web.Http.HttpError)((System.Net.Http.ObjectContent<System.Web.Http.HttpError>)actionExecutedContext.Response.Content).Value;
            StringBuilder stringBuilder = new StringBuilder();
            foreach (var keyValuePair in messages) {
                stringBuilder.AppendLine("Message: Key - " + keyValuePair.Key + ", Value - " + keyValuePair.Value); 
            }
            Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Web API Failed Status Code returned - " + stringBuilder.ToString()));
        }
        catch (Exception ex)
        {
            Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Error in OnActionExecuted - " + ex.ToString()));
        }
    }
}

顺便提一下,我还覆盖了"OnActionExecuting"来验证模型状态。这使我能够在我的动作中删除所有检查。

public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
    if (actionContext.ModelState != null && !actionContext.ModelState.IsValid)
    {
        StringBuilder stringBuilder = new StringBuilder();
        foreach (var obj in actionContext.ModelState.Values)
        {
            foreach (var error in obj.Errors)
            {
                if(!string.IsNullOrEmpty(error.ErrorMessage)) {
                    stringBuilder.AppendLine("Error: " + error.ErrorMessage);
                }
            }
        }
        Elmah.ErrorSignal.FromCurrentContext().Raise(new Exception("Invalid Model State -- " + stringBuilder.ToString()));
        actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
    }
}

当然,您需要使用 "config.Filters.Add" 来添加筛选器。


是的,这也可以捕获HttpResponseException。 - lnaie
帮了很多忙!谢谢。 - mike123

4

Web API中,如果在操作中抛出HttpResponseException异常,则会将其转换为HttpResponseMessage异常,因此您不会看到调用异常过滤器。

但是,如果从过滤器中抛出HttpResponseException异常,则情况并非如此。然而,理想情况下,您不需要从过滤器中抛出HttpResponseException异常,因为您可以通过设置提供的输入上下文的Response属性来短路请求。


你不能这样做。只有在从操作中抛出非HttpResponseExceptions时,才会调用你的过滤器。 - Kiran
1
我在创建HttpResponseException时使用了手动Elmah错误信号,这似乎运行良好。 - jaffa
@jaffa 你的意思是什么?你会在 ApiController 中添加 try/catch 块,然后手动信号 Elmah 吗?虽然这样可以捕获控制器中引发的任何 HttpResponseExeption,但如果管道中的其他地方引发了异常,你仍然会遇到麻烦。 - Kevin Babcock
@KevinBabcock 确实。ElmahHandledErrorLoggerFilter将捕获API控制器中的所有异常,因为这提供了一个单一的信号点。但我的问题出现在不属于控制器的代码内部,因此需要在过滤器外手动发出信号。我没有找到在控制器之外捕获此问题的方法。 - jaffa

3
你需要在 HttpFilters 中启用 Elmah,才能让它在 WebApi 中按照你的预期工作。
可以使用 Elmah.Contrib.WebApi,这是一个 NuGet 包, 它将包含一个类,你可以按照 Elmah.Contrib.WebApi 项目网站上的说明进行配置。
如果你想自己完成此操作,可以参考 使用 ELMAH 捕获 ASP.NET Web API 中未处理的异常 来了解 Elmah.Contrib.WebApi 的操作过程。
另外,我还必须更改错误响应的抛出方式,以便 Elmah 可以捕获它:
 throw new HttpException((int)HttpStatusCode.NotAcceptable, "This request is not properly formatted");

我还建议使用 Elmah.MVC NuGet Package

5
你所提到的项目和博客文章都没有解决@jaffa提出的问题。在ASP.NET Web API项目中使用属性或全局过滤器来捕获异常已经在网络上有很好的文档说明。但是,没有很好的文档说明一个事实,那就是当从ApiController抛出HttpResponseExeption时,Web API会调用ExceptionFilterAttribute.OnException()来处理每个异常,除了这种情况。在这种情况下,Web API会吞噬异常,并且您的异常过滤器永远不会被调用。我一直在寻找解决方法,但还没有找到。 - Kevin Babcock
这个不处理HttpResponseException。 - lnaie

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