Elmah не регистрирует исключения с использованием WebAPI с HttpResponseException

В моем коде WebApi я создаю исключение HttpResponseException, которое замыкает конвейер запроса и генерирует действительный ответ Http. Тем не менее, я пытаюсь интегрировать webApi с ведением журнала elmah, но HttpResponseExeptions не отображаются.

У меня есть настройка web.config для elmah и есть следующий код:

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

Может ли кто-нибудь, кто это сделал раньше, дать мне знать, что я делаю неправильно?

Ответ 1

Особые случаи веб-API HttpResponseException генерируются в действии и преобразуются в HttpResponseMessage и, следовательно, вы не видите, что ваш фильтр исключения вызывается.

Это неверно в случае исключения HttpResponseException из фильтров. Однако в идеале не нужно выбрасывать HttpResponseException из фильтров, так как вы могли бы коротко закоротить запрос, установив свойство Response в предоставленный входной контекст.

Ответ 2

Как уже упоминалось выше, фильтр 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" и проверить действиеExecutedContext.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".

Ответ 3

Вам нужно включить Elmah для HttpFilters, чтобы заставить это работать так, как вы ожидаете от WebApi.

Используйте Elmah.Contrib.WebApi, доступный как пакет NuGet, он будет включать класс, который затем можно подключить по инструкциям на сайте проекта Elmah.Contrib.WebApi.

Если вы хотите сделать это самостоятельно, Захват необработанных исключений в веб-интерфейсе ASP.NET с помощью ELMAH проведет вас через то, что Elmah.Contrib. WebApi делает для вас.

Кроме того, мне пришлось изменить способ получения ответа об ошибке, чтобы он был выбран Elmah:

 throw new HttpException((int)HttpStatusCode.NotAcceptable, "This request is not properly formatted");

Я также рекомендовал бы использовать пакет Elmah.MVC NuGet.