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
}
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...
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With