1

In our application, we have a fairly straightforward set of logging hooks (IExceptionFilters on MVC and API controllers and an additional catch-all in Application_Error()), but there's a whole category of errors that doesn't trigger any of them. If an exception is thrown from within WebAPI itself, or from something used internally (like the type initializer of a class being created by a dependency resolver), all I get is a 500 response being sent to the client.

The only way I've found to capture the error details is to use HttpConfiguration.IncludeErrorDetailPolicy to configure the application to emit error details - however, it's a noted bad practice to broadcast your error details to the world, so I'd prefer to turn this off entirely, or set it to something conditional (say, local-only.) ..But that means remoting into the server where the application is running and invoking the APIs locally with a tool that can inspect the responses (like IE or Google Chrome) in order to figure out what's going on.

I've seen another question on here in a similar vein (here), but the solution presented (using a DelegatingHandler to inspect responses) doesn't meet our needs, our think. Is there really no event I can hook, extension point I can use, or anything like that to capture the actual exception that occurred?

(As an aside, I suppose I could change my IncludeErrorDetailPolicy to Always and use the solution presented in the other thread to capture the error details in a MessageHandler, log them, and manually scrub them from the response being sent to the client, but that would be a nasty hack.)

Thoughts? :/

Community
  • 1
  • 1
William Scalf
  • 422
  • 2
  • 13
  • Why doesn't the MessageHandler approach work for you? – Youssef Moussaoui Jan 18 '13 at 18:44
  • I might be misunderstanding it, but from how I'm reading it, the only way to get an exception object that I can log would be to follow the hack mentioned at the end of my question - to enable including error detail in all responses, then parsing that error detail on error to attempt to reconstruct the exception that was originally thrown, then manually scrub those details from the response before sending it to the client. ..And while that should work, it sounds crude, brittle, and prone to losing diagnostic information and/or unintentionally leaking that information to clients. – William Scalf Jan 18 '13 at 18:50
  • You want to preserve diagnostic information *and* avoid leaking information to the client? What kind of responses are you trying to send back? Why don't the defaults work for you? You would get full diagnostic information on the local machine for debugging purposes, and no information gets leaked to remote clients. – Youssef Moussaoui Jan 18 '13 at 19:31
  • Well, yeah - my intention is for as much information as possible to be captured on the server, and a minimal response to be sent to the client. The problem is that, for most errors, we use built-in mechanisms in the framework to capture fault details for logging (MVC and HTTP IExceptionFilters and the Application_Error event), and that's all well and good, but there appears to be a category of errors inside and around web api where no such mechanism exists - or, at least, not that I can find. – William Scalf Jan 18 '13 at 19:55

2 Answers2

1

We opened a support request with the Microsoft Partner Network and they came back with what I feel is a better answer.

The idea is to replace the platform's default IHttpControllerActivator implementation with one that wraps the default controller creation behavior with any additional desired behavior.

In our case, that means wrapping the DefaultHttpControllerActivator's Create method with a try/catch/throw construct and a call to our logging service. That might not give 100% coverage, but most of the exceptions we're missing deal with the creation of controllers, so it should help a lot.

I'd really love to be able to hook the HandleException method inside HttpControllerDispatcher, but it's both private and static, so meh.

William Scalf
  • 422
  • 2
  • 13
  • +1, and just a remark: replace also `IHttpControllerSelector`, `IHttpActionSelector`and `IHttActionInvoker` for full control on these internal errors. – Konamiman Nov 05 '14 at 09:08
0

Ok, I understand what you're trying to do. Thanks for clarifying. WebAPI has a model where error responses aren't necessarily caused by an exception. Anyone can return an HttpResponseMessage with a 400 for example without actually throwing an exception. And in many cases, the built-in framework errors work the same way without throwing an exception.

Now, I think what you suggested sounds fine to me. You could set the ErrorDetailPolicy to Always, and implement a message handler that logs the error and uses a different HttpError that only includes the Message. Here's what it might look like:

public class ErrorHandlingMessageHandler : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
        HttpError error;
        if (response.TryGetContentValue(out error))
        {
            LogError(error)
            // Use an HttpError that doesn't leak internal information
            (response.Content as ObjectContent).Value = new HttpError(error.Message);
        }

        return response;
    }
}

Notice that we're not scrubbing the existing error. We're creating a new one to reduce the risk of leaking information. The Message should always be safe to send back. This doesn't cover sending back the Model State, but if you need that you can always copy it over to the new error.

One way of thinking about this is that you can record the response you would have sent to a local client, and then still send the remote client a safe message that doesn't include the error details.

Youssef Moussaoui
  • 12,187
  • 2
  • 41
  • 37