HTTPError Exception Message not displaying when webapi is run on Server vs being run locally

cookiekitty picture cookiekitty · Apr 24, 2014 · Viewed 15.3k times · Source

I have a webapi that runs on an IIS7.5 server. It has 3 controllers, and all 3 can be used to access the webapi from calls within my application.

I had a error where my base class for my controller was exposing it's functions as public, rather than protected. This caused the server to throw an Internal Server Error 500 (because of an invalid exception thrown "Multiple actions were found that match the request"). It took me a while to drill down on this, because it never triggered the logging from my webapi. From this dicussion here, I discovered that the error that was happening was happening before an Application_Error function would catch it to log it. So I added the below code to my global.asax of my webapi, and I can now log errors like this.

BUT, my problem now, when I cause an Internal Server Error 500 exactly like above, on my local machine running my webapi, I get a log exactly how I wish to see it with the "ExceptionMessage" of "Multiple actions were found that match the request" spelled out as the reason for the Internal Server Error. But when deploy this exact code to the server, and use the webapi from there, my log only displays "Message" : "An error has occurred" and does not show me the "ExceptionMessage", even though I can see that the exception is being thrown using PerfView. I just need to be able to get my server logs to show the same information as what my local log displays.

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: \r\n Path({0}) \r\n Status({1}) \r\n",
                         request.RequestUri,
                         result.StatusCode);

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

                        exceptionReadTask.Wait();
                        exceptionResult += "Message:" +
                                          exceptionReadTask.Result;

                    }

                    // Do something appropriate with exceptionResult
                    exceptionResult.Log();
                }

                return result;
            }, cancellationToken);
    }
}

Server Log Example:

Timestamp: 4/24/2014 12:24:40 PM
Message: Timestamp: 4/24/2014 4:24:40 PM
Message: Response exception: 
Path(http://webserver/CreditReporting/Api/RetrieveQueuedPullCreditReport) 
Status(InternalServerError) 
Message:{"Message":"An error has occurred."}

Local Log Example:

Timestamp: 4/24/2014 12:03:16 PM
Message: Timestamp: 4/24/2014 4:03:16 PM
Message: Response exception: 
 Path(http://localhost:XXXXX/Api/RetrieveQueuedPullCreditReport) 
 Status(InternalServerError)
 Message:
  {"Message":"An error has occurred.",
  "ExceptionMessage":"Multiple actions were found that match the request:
    \r\nSystem.Threading.Tasks.Task`1[
 Our.WebServices.CreditReporting.Contracts.RetrieveQueuedPullCreditReportResponse] Post

Answer

cookiekitty picture cookiekitty · Apr 29, 2014

I found that these needed to be turned on in the GlobalConfiguration for the webapi itself:

1: config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.LocalOnly;
2: config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
3: config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Never;

The server actually determines how much detail to display, the default is LocalOnly.

Our logging method is not considered local, I guess because it is not actually built into the API itself, but because it is in a shared dll between multiple APIs.

I found this article to be very helpful.