2

I'm trying to create an attribute that will serialize data return from an action differently

public override void OnActionExecuted(HttpActionExecutedContext filterContext)
{
    var content = (filterContext.Response.Content as ObjectContent);

    if (content == null)
    {
        return;
    }

    if (content.ObjectType.IsGenericType 
        && content.ObjectType.GetGenericTypeDefinition() == typeof (Page<>))
    {
        var pageObject = (content.Value as IPage);
        var jsonFormatterRule = new JsonFormatterRule();
        var pageJson = JsonConvert.SerializeObject(pageObject.ItemsArray, 
                                                jsonFormatterRule.GetPascalCasedSettings());

       //How do I set the content that \/ doesn't compile?
       //filterContext.Response.Content = pageJson;
   }
}

This is the JsonFormatterRules incase anyone wanted to see them.

public JsonSerializerSettings GetDefaultSettings()
{
    var settings = new JsonSerializerSettings()
    {
        Formatting = Formatting.Indented,
        ContractResolver = new CamelCasePropertyNamesContractResolver(),
        DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind,
    };

    settings.Converters.AddRange(defaultConfiguredConverters);
    return settings;
}

public JsonSerializerSettings GetPascalCasedSettings()
{
    var settings = this.GetDefaultSettings();
    settings.ContractResolver = new DefaultContractResolver();

    return settings;
}

How can I Set the Content From On Action Executed? I cannot change the default serializer to the DefaultContract Globally because it could threading issues.

Also I'd prefer not to have to create a new response and copy over the Headers from the old one that seems like over kill.

johnny 5
  • 16,589
  • 46
  • 82
  • 156

2 Answers2

1

One way to do this would be to define a custom formatter.

First, define your attribute:

[AttributeUsage(AttributeTargets.Class)]
public sealed class SpecialSerializeAttribute : Attribute
{
}

Now create a formatter that will find the attribute:

public class SpecialSerializeFormatter : MediaTypeFormatter
{
    public SpecialSerializeFormatter()
    {
        //You can add any other supported types here.
        this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
    }

    public override bool CanReadType(Type type)
    {
        //you can just return false if you don't want to read any differently than your default way
        //if you return true here, you should override the ReadFromStreamAsync method to do custom deserialize
        return type.IsDefined(typeof(SpecialSerializeAttribute), true));
    }

    public override bool CanWriteType(Type type)
    {
        return type.IsDefined(typeof(SpecialSerializeAttribute), true));
    }

    public override async Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content,
        TransportContext transportContext)
    {

        //value will be your object that you want to serialize

        //add any custom serialize settings here
        var json = JsonConvert.SerializeObject(value);

        //Use the right encoding for your application here
        var byteArray = Encoding.UTF8.GetBytes(json);
        await writeStream.WriteAsync(byteArray, 0, byteArray.Length);
    }
}

Register the formatter in you WebApiConfig.cs

You can also build a formatter for each type directly and then you don't have to do the Attribute. Just change your CanRead and CanWrite methods. I find basing these of the direct Type's gives better results since it's not such a generic formatter and you may need to apply custom logic based on the type, but the above answer should get you what you need.

ManOVision
  • 1,783
  • 1
  • 11
  • 14
0

Incase anyone was wondering, Response Content is an HTTPContent, which inherits from ByteArrayContent. So if you have your JSON Serialized already, all you have to do is put it into a byte array.

filterContext.ActionContext.Response.Content = new ByteArrayContent(Encoding.ASCII.GetBytes(pageJson));
johnny 5
  • 16,589
  • 46
  • 82
  • 156
  • 1
    Good to know. Make sure ASCII is what you want. If you have any user generated content that gets serialized, you'll want UTF8 so any special characters can be serialized. – ManOVision May 05 '16 at 17:15