2
votes

I have an issue with my MediaTypeFormatter. When I make a request with the Accept header set to "application/vnd.siren+json", it correctly sets the response to have Content Type header set to "application/vnd.siren+json".

What I am trying to do is even when I do not state explicitly that I want "application/vnd.siren+json", I would like to set the response content type to be "application/vnd.siren+json".

For example, a bog-standard call will have this Accept header set:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

When I do a GET using that Accept header, my response type will be application/xml and not application/vnd.siren+json.

WebApiConfig.cs has been configured as:

SirenMediaTypeFormatter sirenMediaTypeFormatter = new SirenMediaTypeFormatter();
sirenMediaTypeFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml"));
sirenMediaTypeFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/vnd.siren+json"));
config.Formatters.Insert(0, sirenMediaTypeFormatter);

I have set up my MediaTypeFormatter as:

public class SirenMediaTypeFormatter : JsonMediaTypeFormatter 
{
    private static Type _supportedType = typeof(Entity);
    private const string _mediaType = "application/vnd.siren+json";

    public SirenMediaTypeFormatter()
    {
        SupportedMediaTypes.Add(new MediaTypeHeaderValue(_mediaType));
    }

    public override bool CanReadType(Type type)
    {
        return type == _supportedType;
    }

    public override bool CanWriteType(Type type)
    {
        bool blnRetval = (typeof(Entity).IsAssignableFrom(type));
        return blnRetval;
    }


    public override Task WriteToStreamAsync(Type type, object value,
   Stream stream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
    {
        return Task.Factory.StartNew(() =>
        {
            if (typeof(Entity).IsAssignableFrom(type))
            {
                content.Headers.ContentType = new MediaTypeHeaderValue(_mediaType);

                var objectToSerialize = BuildSirenDocument(value, stream, content.Headers.ContentType.MediaType);


                var jsonSerializerSettings = new JsonSerializerSettings
                {
                    ContractResolver = new CamelCasePropertyNamesContractResolver()
                };

                string jsonResult = Newtonsoft.Json.JsonConvert.SerializeObject(objectToSerialize, jsonSerializerSettings);

                StreamWriter writer = new StreamWriter(stream);
                writer.Write(jsonResult);
                writer.Flush();

            }
        });
    }

I have tried to update the value of the Context using content.Headers.ContentType = new MediaTypeHeaderValue(_mediaType); however 1). it does not work and the content type remains set to application/xml and 2). I worry about playing around with Context in a WriteAsynch method like this.

How do I go about forcing my Response header type (without setting it explicitly in the controller).

1

1 Answers

4
votes

It's too late to write the headers by the time WriteToStreamAsync is called as you allude to in your question. Instead you need to override SetDefaultContentHeaders.

From the documentation, this:

Sets the default headers for content that will be formatted using this formatter.

To change the Content Type you can pass through your own MediaTypeHeaderValue to the base method:

public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType)
{
    base.SetDefaultContentHeaders(type, headers, new MediaTypeHeaderValue(_mediaType));
}