2

In the code below I am manually creating a json object that adheres to our standard json response object format, which is the same for both errors and actual responses:

{ data : { /*something*/ }, status : { httpCode : 123, message: "error message" } }

How can I configure ASP.NET to do this formatting for me? It can obviously do so using the standard JSON and XML formatter when doing content negotiation, but the linked article only points to a page on Custom Formatters that is blank (and also for ASP.NET MVC which I am not using)...

I would also like for it to be able to set the returned http code in the response object (as shown below).

Current manual formatting of json

[StandardizedRestServiceExceptionFilter]
public class FooController : ApiController
{
    /// <summary>
    /// 
    /// The return format for both real results and errors
    /// is { data : { }, status : { httpCode : 123, message: "error message" }
    ///
    /// We have moved the error serialization to StandardizedRestServiceExceptionFilter,
    /// but was unable to generalize the processing of the output format for normal responses
    /// That could be improved, possibly using a IMessageFormatter ?
    /// </summary>
    /// <param name="code"></param>
    /// <returns></returns
    [HttpGet]
    public JObject Coverage(string code)
    {
        dynamic returnObject = new JObject();
        dynamic statusObject = new JObject();
        dynamic dataObject = new JObject();

        JArray stores = StoresWithCoverage(code);
        var hasCoverage = stores.Count > 0;
        dataObject.coverage = hasCoverage;

        returnObject.data = dataObject;
        returnObject.status = statusObject;

        statusObject.message = "";
        statusObject.httpCode = 200;

        return returnObject;
        }
    }
}

So in the above example I would like to be able to just return an Object of some kind with a coverage property and have ASP.NET do the actual formatting and serialization to JSON (if requested in the content negotiation).

oligofren
  • 20,744
  • 16
  • 93
  • 180

1 Answers1

2

One can either use prebuilt or custom Media Formatters using the ASP.NET Web API.

In this case, one could take the custom formatter from the link above as an example, and modify the JSON content wrapper to one's needs (here: using JSON.NET):

First you need to specify which content type you are supporting:

public CustomJsonFormatter()
{
    // Add the supported media type.
    SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
}

Then you you need to implement your override of the WriteToStream method:

public override void WriteToStream(Type type, object value, Stream  writeStream, HttpContent content)
{
    using (var writer = new StreamWriter(writeStream, effectiveEncoding))
    {
        using (JsonWriter jw = new JsonTextWriter(writer))
        {
            dynamic returnObject = new JObject();
            dynamic status = new JObject();
            JObject data = (JObject)JToken.FromObject(value);

            status.httpCode = HttpContext.Current.HttpResponse.StatusCode;
            status.message = null;

            returnObject.data = data;
            returnObject.status = status;

            jw.Formatting = Formatting.Indented;

            JsonSerializer serializer = new JsonSerializer();
            // for customizing settings, like lower casing
            // attribute names, see http://stackoverflow.com/a/6288726/200987
            serializer.ContractResolver = new CamelCasePropertyNamesContractResolver()

            serializer.Serialize(jw, returnObject);
        }
    }
}

And finally one needs to say which type one support transforming, register this formatter with ASP.NET, and some other minor (and usually non-required) modifications. Refer to the articles below for how to do this, as well as how serialization, http status codes and content negotiation works and can be customized.

oligofren
  • 20,744
  • 16
  • 93
  • 180
Benjamin Soulier
  • 2,223
  • 1
  • 18
  • 30
  • Great tips, and the link to the Media Formatters article brought me on to this article on how the various response types are being processed Wep Api 2, including how the HTTP response codes are being set: https://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/action-results Super interesting and useful. I might elaborate a bit on your answer to make it more complete. – oligofren Oct 06 '16 at 12:45
  • I was mentioning the fact of finding out a way to get HTTP Context value, as accessing HttpContext.Current when using Web API [is not a good practice for a stateless service](http://stackoverflow.com/a/19884776/2952074). – Benjamin Soulier Oct 06 '16 at 13:48
  • I think you may have misunderstood the answer you linked to :-) They talk in the context of sessions in relation to stateless services. Accessing the context to retrieve the http status does in no way relate to this. I do not affect (change) the state of the context in any way, and there is no session object that is shared between requests. This is still a purely stateless service if I fetch data from the HttpContext. Do elaborate if you disagree. – oligofren Oct 06 '16 at 14:11
  • 1
    Good point; I have been trying to avoid any reference to it in any cases, especially when using `async/await` (where HttpContext.Current is linked to current thread), because [you need to pass it of as a variable](http://stackoverflow.com/questions/23790292/web-api-service-how-to-use-httpcontext-current-inside-async-task), which is not good in my opinion. – Benjamin Soulier Oct 06 '16 at 14:22