Reputation: 422
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? :/
Upvotes: 1
Views: 412
Reputation: 422
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.
Upvotes: 1
Reputation: 12405
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.
Upvotes: 0