Вопрос

In my WebApi code, I raise a HttpResponseException which short-circuits the request pipeline and generates a valid Http response. However, I'm trying to integrate webApi with elmah logging, yet the HttpResponseExeptions aren't showing up.

I have the web.config set-up for elmah and have the following code:

In Global.asx.cs:

static void ConfigureWebApi(HttpConfiguration config)
{  
    config.Filters.Add(new ServiceLayerExceptionFilter());            
    config.Filters.Add(new ElmahHandledErrorLoggerFilter());
    config.DependencyResolver = new WebApiDependencyResolver(ObjectFactory.Container);                            
}     

Filter:

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

Code where exception is raised:

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

Can anyone who has implemented this before let me know what I'm doing wrong?

Это было полезно?

Решение 2

Web API special cases HttpResponseException thrown in action and converts into HttpResponseMessage and hence you are not seeing your exception filter getting invoked.

This is not true in the case of throwing HttpResponseException from filters. However, ideally one need not throw HttpResponseException from filters as you could short-circuit a request by setting the Response property on the supplied input context.

Другие советы

As mentioned above, the Elmah filter does not catch and log anything when you raise a HttpResponseException. More specifically, if the following syntax is used:

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

I wanted to trap and log an error in both cases. The way to do it is to use an "ActionFilterAttribute", override "OnActionExecuted", and check 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()));
        }
    }
}

On a side note, I also overwrote "OnActionExecuting" to validate the model state. This allowed me to remove all of the checks within my actions.

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

Of course, you will need to add the filter using "config.Filters.Add".

You need to turn on Elmah for HttpFilters in order to get this to work as you expect for WebApi.

Use Elmah.Contrib.WebApi available as a NuGet Package, it will wire include a class that you can then wire up following the instructions on the Elmah.Contrib.WebApi project site.

If you want to do this yourself, Capturing Unhandled Exceptions in ASP.NET Web API's with ELMAH walks you through what the Elmah.Contrib.WebApi is doing for you.

Additionally, I had to change the way that the error response is thrown for it to be picked by Elmah to:

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

I would also recommend the use of the Elmah.MVC NuGet Package.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top