Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing exceptions between two C# programs using JSON

I have a Web API which makes HTTP Requests to a windows service that executes certain tasks/commands.

If my 'service' throws an exception I then want to pass that exception back up the pipe to the Web API using JSON. I then want to de-serialize the exception back to an exception object and throw it.

my code:

Shared exception between Web API and Service:

public class ConnectionErrorException : Exception
{
    public ConnectionErrorException()
    {
    }
    public ConnectionErrorException(String message)
        : base(message)
    {
    }
}

Now in my Service I have the following code:

       ... 
       try
        {
            result = await ExecuteCommand(userId);
            //If reached here nothing went wrong, so can return an OK result
            await p.WriteSuccessAsync();
        }
        catch (Exception e)
        {
            //Some thing went wrong. Return the error so they know what the issue is
            result = e;
            p.WriteFailure();
        }
        //Write the body of the response:

        //If the result is null there is no need to send any body, the 200 or 400 header is sufficient
        if (result != null)
        {
            var resultOutput = JsonConvert.SerializeObject(result);
            await p.OutputStream.WriteAsync(resultOutput);
        }
        ...

So here I return a JSON object. Either the actual response object, or the Exception which happened to occour.

Then here is the code in the Web API which makes the request to the Service:

  // Make request
            HttpResponseMessage response = await client.PostAsJsonAsync(((int)(command.CommandId)).ToString(), command);
            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsStringAsync();
            }
            else
            {
                var exception = HandleErrorResponse(await response.Content.ReadAsStringAsync());
                var type = exception.GetType();
                //TODO: try and determine which exact exception it is.
                throw exception;
            }

Now here, if the response was successful I just return the string content. If the request fails, I try and pass the json response to an exception. However I have to pass it to the base exception as I do-not know what type it is yet. However when I debug and add a watchdog on the exception. There is a parameter _className which says 'Domain.Model.Exceptions.API.ConnectionErrorException`.

Question: How can I determine which exception was returned and de-serialize it back to the correct exception so that I can throw it again. I need to know the exact type of exception because I handle all the different exceptions further up my services layer in the Web API.

Here is an example of the json which is returned for the ConnectionErrorException:

{
    "ClassName": "Domain.Model.Exceptions.API.ConnectionErrorException",
    "Message": null,
    "Data": null,
    "InnerException": null,
    "HelpURL": null,
    "StackTraceString": "",
    "HResult": -2146233088,
    "Source": "LinkProvider.Logic",
    "WatsonBuckets": null
}
like image 626
Zapnologica Avatar asked May 13 '15 09:05

Zapnologica


2 Answers

Replace your exception handling with following code block.

else
{
    var response = await response.Content.ReadAsStringAsync();
    var exception = JsonConvert.DeserializeObject<Exception>(response);
    // your handling logic here
    Console.WriteLine(exception);
}

So if service threw new NotImplementedException("I haz error!"), above would print out System.NotImplementedException: I haz error!.


Here's a quick, stand-alone example using MVVMLight and JSON.net. Say you have sender as

public class Sender
{
    public Sender()
    {
        Messenger.Default.Register<NotificationMessage>(this, message =>
            {
                if ((Type)message.Target == typeof(Sender))
                   GotResponse(message.Notification);
            });    
    }

    public void SendRequest(string request)
    {
        Console.WriteLine("sending:{0}", request);
        Messenger.Default.Send(
            new NotificationMessage(this, typeof(Receiver), request));
    }

    private void GotResponse(string response)
    {
        Console.WriteLine("received:{0}", response);
        if (response.Equals("ok"))
            return;

        Exception exception = JsonConvert.DeserializeObject<Exception>(response);
        Console.WriteLine("exception:{0}", exception);

        try
        {
            throw exception;
        }
        catch (Exception e)
        {
            Console.WriteLine("Indeed, it was {0}", e);
        }
    }
}

and receiver as

public class Receiver
{
    public Receiver()
    {
        Messenger.Default.Register<NotificationMessage>(this, message =>
            {
                if ((Type)message.Target == typeof(Receiver))
                    GotRequest(message.Notification);
            }); 
    }

    public void SendResponse(string response)
    {
        Messenger.Default.Send(new NotificationMessage(this, typeof(Sender), response));
    }

    public void GotRequest(string request)
    {
        string response = !string.IsNullOrWhiteSpace(request) ? 
                          "ok" : 
                          JsonConvert.SerializeObject(new NotImplementedException("I haz error!"));

        SendResponse(response);
    }
}

then following "activation"

var sender = new Sender();
var receiver = new Receiver();
sender.SendRequest("my request");
sender.SendRequest(null);

would print out

sending:my request
received:ok

sending:
received: {"ClassName":"System.NotImplementedException", "Message":"...","WatsonBuckets":null}

exception:System.NotImplementedException: I haz error!

Indeed, it was System.NotImplementedException: I haz error! at WpfApplication1.View.Sender.GotResponse(String response) in...

like image 51
Mikko Viitala Avatar answered Sep 19 '22 13:09

Mikko Viitala


First of all to be able to deserialize exception JSON I was forced to add an extra constructor to ConnectionErrorException class:

public class ConnectionErrorException : Exception
{
    // ... rest of the code

    protected ConnectionErrorException(SerializationInfo info, StreamingContext context) 
        : base(info, context)
    {
    }
}

This is a known issue. Check this question for example.

Next I would read the value of ClassName property first and then according to the value would deserialize it to desired type. I think it will be a good idea to create some helper class for that:

public static class JsonHelper
{
    public static bool IsInstanceOf<T>(this JsonObject jsonObject)
    {
        if (jsonObject == null || !jsonObject.ContainsKey("ClassName"))
        {
            return false;
        }

        return jsonObject["ClassName"] == typeof(T).FullName;
    }
}

And then your code might look like that:

var jsonObject = JsonObject.Parse(json);
if(jsonObject.IsInstanceOf<ConnectionErrorException>())
{
    var connectionErrorException = 
        JsonConvert.DeserializeObject<ConnectionErrorException>(json);
}
like image 30
Aleksandr Ivanov Avatar answered Sep 20 '22 13:09

Aleksandr Ivanov