0

In my applications Web API, I have an exception filter that suppose to catch any exception and transmit it to the client within a response wrapper class. My goal is to make that exception transmit with the correct type, so the client can reconstruct and re-throw it.

Following is the code for exception catcher:

public override void OnException(HttpActionExecutedContext actionExecutedContext)
    {

        HttpStatusCode OutputHttpCode = HttpStatusCode.InternalServerError;
        var exceptionType = actionExecutedContext.Exception.GetType();

        if (exceptionType == typeof(InvalidIDException))
        {
            OutputHttpCode = HttpStatusCode.Unauthorized;
        }

        //this way of getting type didnt work for me either
        //var exceptionType = typeof(RestErrorResponse<>).MakeGenericType(actionExecutedContext.Exception.GetType());

        actionExecutedContext.Response = new HttpResponseMessage()
        {
            Content = new StringContent(JsonConvert.SerializeObject(
                //this will not compile here, saying t is a variable but used like a type.
                //If i use generic "Exception" instead everything is working fine,
                //but it will be interpreted as generic exception on client side and
                //could not be handled properly if i rethrow it directly
                new RestErrorResponse<exceptionType> () //Exception will compile
                {
                    Content = null,
                    Status = RestStatus.Error,
                    Exception = actionExecutedContext.Exception
                }
                ),
                    System.Text.Encoding.UTF8, "application/json"),

            StatusCode = OutputHttpCode
        };

        base.OnException(actionExecutedContext);
    }

This is a class with generics I'm trying to put my exception into:

public class RestErrorResponse<E> :RestResponse<Object> {
    public E myException { get; set; }
}

If i use a generic Exception in my "RestErrorResponse" class this is a JSON that gets created:

{
  "Exception": {
    "ClassName": "InvalidLoginException",
    "Message": "Invalid User Name",
    "Data": {},
    "InnerException": null,
    "HelpURL": null,
    "StackTraceString": "....",
    "RemoteStackTraceString": null,
    "RemoteStackIndex": 0,
    "ExceptionMethod": "....",
    "HResult": -2147024809,
    "Source": "DB",
    "WatsonBuckets": null,
    "ParamName": null
  },
  "Status": {
    "Verbal": "Error",
    "Code": 1074
  },
  "Content": null
}

My goal will be to get:

{
  "InvalidLoginException": {
    "ClassName": "InvalidLoginException",
    "Message": "Invalid User Name",
    "Data": {},
    "InnerException": null,
    "HelpURL": null,
    "StackTraceString": "....",
    "RemoteStackTraceString": null,
    "RemoteStackIndex": 0,
    "ExceptionMethod": "....",
    "HResult": -2147024809,
    "Source": "DB",
    "WatsonBuckets": null,
    "ParamName": null
  },
  "Status": {
    "Verbal": "Error",
    "Code": 1074
  },
  "Content": null
}
Derek Pollard
  • 6,953
  • 6
  • 39
  • 59
AnKing
  • 1,994
  • 6
  • 31
  • 54
  • 1
    Why does it have to be generic. All exceptions will cast to `Exception`. Also you cannot make the object name variable like that without generating the JSON dynamically. – juharr Aug 13 '18 at 13:52
  • I think you could use `GenericTypeNameContractResolver` and `JsonPropertyGenericTypeNameAttribute` from [How to get the name of `` from generic type and pass it into JsonProperty()?](https://stackoverflow.com/q/39128650/3744182). You'll need to add the attribute `[JsonPropertyGenericTypeName(0)] public E myException { get; set; }`. See https://stackoverflow.com/q/51804489/3744182 and https://dotnetfiddle.net/PeWAYp for a recent example with a working fiddle. – dbc Aug 14 '18 at 00:07
  • Alternatively, you could mark `public E myException { get; set; }` with `[JsonProperty(TypeNameHandling = TypeNameHandling.Auto)]`. For security you should also constrain `T` to be of type `Exception`. – dbc Aug 14 '18 at 00:07

1 Answers1

1

Is RestErrorResponse a class under your control? If so, does it really have to be generic? Consider this:

public class RestErrorResponse : RestResponse<Object> {
    public object Exception { get; set; }
}

Now you can simply instantiate it:

var response = new RestErrorResponse 
{
     // other properties
     Exception = actionExecutedContext.Exception
}

If you can't change it, you will need reflection to create the instance of RestErrorResponse. For example:

 var responseType = typeof(RestErrorResponse<>).MakeGenericType(exceptionType);
 dynamic response = Activator.CreateIntance(type);
 response.Content = null;
 response.Status = RestStatus.Error;
 response.Exception = actionExecutedContext.Exception;
jeroenh
  • 26,362
  • 10
  • 73
  • 104
  • RestErrorResponse is under my control, i can change it if its better to use it without generics, but if i use just "Exception" JSON will deserialize it as a generic Exception and I will not be able to rethrow it – AnKing Aug 13 '18 at 13:57
  • Then I would just change it (see first part of updated answer) – jeroenh Aug 13 '18 at 13:59
  • but what about recreating the same exception type on the client side? Doing it this way just deserializes it as "Exception", not as "InvalidLoginException" << the type I need to rethrow – AnKing Aug 13 '18 at 14:04
  • 1
    Please ask another question if you have issues on deserialization. See for example https://stackoverflow.com/questions/4441466/how-to-deserialize-an-jobject-to-net-object. But be warned that there are documented issues with serializing/deserializing exceptions, mainly because not all exceptions are implemented correctly (especially with 3rd party libraries). YMMV. – jeroenh Aug 13 '18 at 14:50