44

Overriding the default JSON serializer settings for web API on application level has been covered in a lot of SO threads. But how can I configure its settings on action level? For example, I might want to serialize using camelcase properties in one of my actions, but not in the others.

9
  • Could you not just craft the HttpResponseMessage manually setting the content to your desired json output? It's not as tidy as dealing with a global formatter but you can still create DRY helpers for dealing with these one off situations. Commented Jun 12, 2017 at 12:12
  • Have you considered ActionFilterAttributes for the task? Commented Jun 12, 2017 at 12:19
  • The best approach would be to build an HTTP module and intercept each request and process as you need to for each different action. Commented Jun 12, 2017 at 12:20
  • Would you consider an override per controller instead of per action? Commented Jun 12, 2017 at 12:24
  • @FedericoDipuma Sure, that would be fine Commented Jun 12, 2017 at 12:32

3 Answers 3

79

Option 1 (quickest)

At action level you may always use a custom JsonSerializerSettings instance while using Json method:

public class MyController : ApiController
{
    public IHttpActionResult Get()
    {
        var settings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver()
        };
        var model = new MyModel();
        return Json(model, settings);
    }
}

Option 2 (controller level)

You may create a new IControllerConfiguration attribute which customizes the JsonFormatter:

public class CustomJsonAttribute : Attribute, IControllerConfiguration 
{
    public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
    {
        var formatter = controllerSettings.Formatters.JsonFormatter;

        controllerSettings.Formatters.Remove(formatter);

        formatter = new JsonMediaTypeFormatter
        {
            SerializerSettings =
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver()
            }
        };

        controllerSettings.Formatters.Insert(0, formatter);
    }
}

[CustomJson]
public class MyController : ApiController
{
    public IHttpActionResult Get()
    {
        var model = new MyModel();
        return Ok(model);
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

do we need to instantiate a new formmater? can't we tweak the current instance or the change will be applied everywhere?
@JobaDiniz as you stated, if you change properties of the original formatter, those changes will affect every other controller
Nice initialization of SerializerSettings! I didn't know about that C# feature.
15

Here's an implementation of the above as Action Attribute:

public class CustomActionJsonFormatAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        if (actionExecutedContext?.Response == null) return;

        var content = actionExecutedContext.Response.Content as ObjectContent;

        if (content?.Formatter is JsonMediaTypeFormatter)
        {
            var formatter = new JsonMediaTypeFormatter
            {
                SerializerSettings =
                {
                    ContractResolver = new CamelCasePropertyNamesContractResolver()
                }
            };

            actionExecutedContext.Response.Content = new ObjectContent(content.ObjectType, content.Value, formatter);
        }
    }
}

public class MyController : ApiController
{
    [CustomActionJsonFormat]
    public IHttpActionResult Get()
    {
        var model = new MyModel();
        return Ok(model);
    }
}

1 Comment

I used this code but I gave it a Type ContractResolverType property and then did ContractResolver = (IContractResolver)Activator.CreateInstance(ContractResolverType) so I don't have to create one of these for each contract resolver type. In use it looks like this: [ActionContractResolver(ContractResolverType = typeof(DefaultContractResolver))]
0

I needed to return a 404 status error code alongside a json object with error details. I solved it using WebApi.Content with a new new JsonMediaTypeFormatter.

public class MyController : ApiController
{
    public IHttpActionResult Get()
    {
        // Configure new Json formatter
        var formatter = new JsonMediaTypeFormatter
        {
            SerializerSettings =
            {
                TypeNameHandling = TypeNameHandling.None,
                PreserveReferencesHandling = PreserveReferencesHandling.None,
                Culture = CultureInfo.InvariantCulture,
                Formatting = Formatting.Indented,
                NullValueHandling = NullValueHandling.Ignore
            }
        };

        try
        {
            var model = new MyModel();
            return Content(HttpStatusCode.OK, model, formatter);
        }
        catch (Exception err)
        {
            var errorDto = GetErrorDto(HttpStatusCode.NotFound, $"{err.Message}");
            return Content(HttpStatusCode.NotFound, errorDto, formatter);
        }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.