1

Using Asp.net WebApi (RC), how can I catch errors that are not caught by Exception Filters or Application_Error() in global.asax?

With both of these in place it seems that there is a class of exceptions still not covered. For example: ApiControllerActionSelector_AmbiguousMatch error (Multiple actions were found that match the request: {0}).

I'm not specifically concerned about the above error, this error just pointed out that there is a class of errors that aren't being caught by either my Exception Filter or Application_Error method.

So how can I cover all my bases?

EBarr
  • 11,826
  • 7
  • 63
  • 85

2 Answers2

3

You're right, there are several classes of exception not trapped by either Application_Error or ExceptionFilter. The Web API request pipeline is processed separately from the ASP.NET MVC pipeline (at least through MVC 4) so the MVC Application_Error doesn't kick-in. Also, if your application throws HttpResponseException type exceptions, they will not be caught by an ExceptionFilter by design (see the ExceptionFilter paragraph). To access all exceptions thrown by your code, you'll need to create a DelegatingHandler along the lines of this code:

public class ResponseExceptionTrapper : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        return base
            .SendAsync(request, cancellationToken)
            .ContinueWith(response =>
                 {
                     var result = response.Result;
                     if (!result.IsSuccessStatusCode)
                     {
                          var exceptionResult = string.Format(
                               "Response exception: Path({0}) Status({1}) ",
                               request.RequestUri,
                               result.StatusCode);

                          if (result.Content != null)
                          {
                               var exceptionReadTask =
                                      result.Content.ReadAsStringAsync();

                               exceptionReadTask.Wait();
                               exceptionResult += "Message:\n\r" +
                                                 exceptionReadTask.Result;
                           }

                           // Do something appropriate with exceptionResult
                      }

                      return result;
                 }, cancellationToken);
    }
}

You can wire up the handler with this line in your global config logic:

GlobalConfiguration.Configuration.MessageHandlers.Add(
     new ResponseExceptionTrapper());
Sixto Saez
  • 12,610
  • 5
  • 43
  • 51
2

I believe that Exception Filters only get called once the action is invoked (in which case there is a try/catch around it). The Ambiguous match error would pop up before that in the pipeline and there could be other errors that pop up after that (e.g. a formatter error) as you mention.

I'm not sure you can have one solution to address all of the aspects (since the hosting implementation can vary), but you could try overriding the HttpControllerDispatcher. This class is one of the "root" classes used in the pipeline. Specifically, you could override SendAsync to do your try/catch and handle accordingly.

Chris
  • 772
  • 3
  • 8
  • I agree with the assessment about Exception Filter's location within the pipeline. I'm just surprised that Application_Error didn't kick in. I'm not so worried about a single solution, just making sure that my app behaves with grace regardless of the error. I hadn't thought about overriding the dispatcher...It will be a while till I can circle back to this ...but I'll report back. – EBarr Aug 14 '12 at 12:40
  • It might be easier to try and address that with a series of unit tests. It is pretty easy to search through and identify all of the controllers and actions available and then invoke each of those to see if there are any exceptions returned. This is even easier with WebApi since you can do Self-Hosting for testing. – Chris Aug 14 '12 at 14:20
  • Thanks for the pointer --I agree on the methodology. Unit test, however, are just that -- tests. Even if you have 100% code coverage, you can still miss all sorts of things -- cluster fail-over, out of memory, high volume locking contention, app domain cycling, whatever... – EBarr Aug 14 '12 at 14:29
  • This post describes usage of `HttpControllerDispatcher`: http://aspnetwebstack.codeplex.com/workitem/574 – Zaid Masud Aug 01 '13 at 10:45