5

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
}
0

3 Answers 3

3

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...

Sign up to request clarification or add additional context in comments.

2 Comments

So what you are saying is that I could just do a .ToString() on my object and the check the content of the string?
Need to rephrase my (now deleted) comment. I'm saying you need to serialize the exception using e.g. JSON.net and then at the other end just deserialize as Exception.
1

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);
}

2 Comments

Interesting comment about the additional constructor. Mine didn't give me any errors. Thanks for you helpers I will give it a shot, Looks like a nice clean way to implement it.
You need to add that extra constructor if you are wrapping the actual exception.
1

You can keep you exceptions to a C# dynamic Object then serialize it to JSON and then return it from your windows service. Again on the Web API you deserialize that JSON and keep into a dynamic object. In this way you don't have to bother about the actual Type of the exception. On any exception you can just throw it away. If you want to know the actual type of the exception then you can write you code like this where tempData is the dynamic object after deserializiation:

Type exceptionType = ((ObjectHandle)tempData).Unwrap().GetType();

And then handle the exception accordingly

Hope this helps :)

2 Comments

The whole idea is that that I want to know what type of exception is being thrown. Further up in my services layer I deal with all the specific errors. So I need to get the exact type of error.
@Zapnologica edited my answer ...this should solve your issue

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.