John
John

Reputation: 1764

WebAPI how do I deserialize exceptions on the (.NET) client side

I have a webAPI 2 controller, Currently it is throwing an exception, and this is being sent back to the client in a response message. 500 internal server error, the exception it's self is serialised and I can see the json representation of it in fiddler. (json pasted below)

I have been trying to find a way to de-serialize this exception back to a an .NET Exception object, but keep getting the error:

"Member 'ClassName' was not found."

currently in my client I am trying to deserialise the exception via the following code.

if (apiResponse.ResponseCode.Equals(500)) // Unhandled exception on server
{
    var exceptionObject = await response.Content.ReadAsAsync<Exception>();
}

How do I properly de-serialize this exception object on my client side application? ideally I should be able to get back the original System.InvalidOperationException` object.

Below is the Response I am trying to de-serialize as captured in fiddler:

HTTP/1.1 500 Internal Server Error Cache-Control: no-cache Pragma: no-cache Content-Type: application/json; charset=utf-8 Expires: -1 Server: Microsoft-IIS/10.0 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET Date: Wed, 30 Sep 2015 13:43:42 GMT Content-Length: 1556

{"Message":"An error has occurred.","ExceptionMessage":"UrlHelper.Link must not return null.","ExceptionType":"System.InvalidOperationException","StackTrace":" at System.Web.Http.Results.CreatedAtRouteNegotiatedContentResult1.Execute()\r\n at System.Web.Http.Results.CreatedAtRouteNegotiatedContentResult1.ExecuteAsync(CancellationToken cancellationToken)\r\n at System.Web.Http.Controllers.ApiControllerActionInvoker.d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Filters.AuthorizationFilterAttribute.d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()"}

Upvotes: 8

Views: 4065

Answers (2)

germi
germi

Reputation: 4658

Thanks to the comment from Serg Rogovtsev, I now know that what you have is not an Exception but a HttpError. Therefore, you'll have to handle it as such:

var exceptionObject = await response.Content.ReadAsAsync<System.Web.Http.HttpError>();

I'm still not convinced that's a good idea, though. A 500 status code generally means that something went unexpectedly wrong and there was nothing there to catch it on the server side. If you have control over the server, I'd strongly suggest to handle such errors there. The client is unable to do anything about it anyway, so all it needs is some kind of error notification.

Old answer (before the comment about HttpError):

A serialized Exception looks something like this:

{
    "ClassName":"System.Exception",
    "Message":"some exception",
    "Data":null,
    "InnerException":null,
    "HelpURL":null,
    "StackTraceString":null,
    "RemoteStackTraceString":null,
    "RemoteStackIndex":0,
    "ExceptionMethod":null,
    "HResult":-2146233088,
    "Source":null,
    "WatsonBuckets":null
}

What you have is just a formatted error message that contains information about the exception.

By the way: I wouldn't try to catch server-side exceptions on the client. You need to handle errors on the server and provide the client with enough information to inform the user about what happened.

Upvotes: 6

John
John

Reputation: 1764

I found a good way to get the json exception I want. I got this while looking through the samples on the ASPNET git repository.

In the ASPNET Startup.cs Configure method add the following (for dev environment only since as noted in other answers a 500 is probably not useful to the client)

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    if (env.IsDevelopment())
    {
        app.Use(async (context, next) =>
        {
             try
             {
                  await next();
             }
             catch (Exception ex)
             {
                  if (context.Response.HasStarted)
                  {
                      throw;
                  }
                  context.Response.StatusCode = 500;
                  await context.Response.WriteJsonExcpetionAsync(ex);
              }
        });
    }

    // Other config logic...
}

Upvotes: 0

Related Questions