2
votes

Since a few days I am trying to figure out how to return from my web api generic response - a wrapper class where one property will have dynamically pointed type.

Below code fragment shows what I want to achieve:

[RoutePrefix("api")]
public class TestController : ApiController
{
    [HttpGet]
    [Route("test")]
    public HttpResponseMessage Test3()
    {
        Smth smth = new Smth()
        {
            Something = "dsfdsfdsfs"
        };

        object apiResponse = this.GetResponse(true, smth);

        return base.Request.CreateResponse(HttpStatusCode.OK, apiResponse);
    }

    public object GetResponse(bool isSuccess, dynamic responseObject, string[] messages = null)
    {
        return new
        {
            is_success = isSuccess,
            response_object = responseObject,
            messages = messages
        };
    }
}

Unfortunately this apporach does not work - I still get:

ExceptionMessage

Cannot perform serailization of type <>f__AnonymousType0`3[System.Boolean,System.Object,System.String[]] ...

ExceptionType

System.Runtime.Serialization.InvalidDataContractException

StackTrace

in

System.Runtime.Serialization.DataContract.DataContractCriticalHelper.ThrowInvalidDataContractException(String message, Type type) w System.Runtime.Serialization.DataContract.DataContractCriticalHelper.CreateDataContract(Int32 id, RuntimeTypeHandle typeHandle, Type type) w System.Runtime.Serialization.DataContract.DataContractCriticalHelper.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle, Type type) w System.Runtime.Serialization.DataContractSerializer.GetDataContract(DataContract declaredTypeContract, Type declaredType, Type objectType) w System.Runtime.Serialization.DataContractSerializer.InternalWriteObjectContent(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) w System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) w System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph, DataContractResolver dataContractResolver) w System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph) w System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content) w System.Net.Http.Formatting.XmlMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) --- Koniec śladu stosu z poprzedniej lokalizacji, w której wystąpił wyjątek --- w System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) w System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) w System.Net.Http.HttpContent.d__49.MoveNext() --- Koniec śladu stosu z poprzedniej lokalizacji, w której wystąpił wyjątek --- w System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) w System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) w System.Web.Http.Owin.HttpMessageHandlerAdapter.d__13.MoveNext()

During my research I found on forum some examples which work well:

[RoutePrefix("api")]
public class TestController : ApiController
{
    [HttpGet]
    [Route("test")]
    public HttpResponseMessage Test3()
    {
        Smth smth = new Smth()
        {
            Something = "dsfdsfdsfs"
        };

        var apiReponse = new
        {
            is_success = true,
            response_object = smth,
            messages = new string[] { "dsfsfdds" }
        };

        return base.Request.CreateResponse(HttpStatusCode.OK, apiReponse);
    }
}

Above example works and returns correctly formatted data, but such approach will cause bugs related to naming (in this way I have to specify response structure each time I return it).

From my point of view there is no difference in this two approaches except fact that in first case we get anonymous type and in second case we work with object.

So the question is:

Is it possible to make my first approach work?

2
what error are you getting?Daniel Manta
I added exception details but I cut off fragment of exception message - it was not in english :)Piotr Kwiatkowski
try serializing the dynamic object to a string using json. If that fails, serialize the whole darn thingKell

2 Answers

1
votes

Basically serialize object as a string json representation and return content type "application/json" worked.

Smth smth = new Smth()
{
    Something = "dsfdsfdsfs"
};
var serializer = new JavaScriptSerializer();
string json = serializer.Serialize(this.GetResponse(true, smth));
var response = this.Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(json, Encoding.UTF8, "application/json");
return response;

Returns

{"is_success":true,"response_object":{"Something":"dsfdsfdsfs"},"messages":null}
2
votes

Note: While this is not the answer to your question I would like to make a comment so that other people would not be misguided.

I would strongly suggest that using a 200 (OK) response for every scenario is not appropriate and RESTful.

The client would have to check is_success = true, for each and every response if the response code would be always 200 (OK) even in case of error.

As MDN quotes: (LINK)

The HTTP response codes should be used properly for e.g. HTTP response status codes indicate whether a specific HTTP request has been successfully completed. Responses are grouped into five classes:

  • Informational responses (100–199)
  • Successful responses (200–299)
  • Redirects (300–399)
  • Client errors (400–499)
  • Server errors (500–599)

For e.g,

  • If the request contains invalid data or does not contain the required field you should return 400 (Bad Request).
  • If there is unexpected error on the server then use 500 (Internal Server Error)
  • If a new resource is created on the server then use 201 (Created)

I would also suggest reading about using HTTP methods properly. (LINK)

As for a generic response you could return something like this in Successful response: (This is totally my structure, it may not be best practice)

{
    ResponseCode:1,
    Message:"User created",
    Data:{//Any complex object
         purchases:[
            {data 1},
            {data 2}
          ]
       },
    Exception:null
}

In case of server error (use this only for development):

{
    ResponseCode:2,
    Message:"Caught in Global exception filter",
    Data:null,
    Exception: {//Do not send this in production
        Message: "An error has occurred.",
        ExceptionMessage:,
        ExceptionType":,
        StackTrace: ,
        InnerException: {
     }
   }
}

Here ResponseCode is your custom code that can be used to describe the problem in more detail for e.g. in the above example ResponseCode:2 indicates that the error is caught in the global exception handler.