15

I need specific JSON settings per controller in my ASP.NET MVC 6 webApi. I found this sample that works (I hope !) for MVC 5 : Force CamelCase on ASP.NET WebAPI Per Controller

using System;
using System.Linq;
using System.Web.Http.Controllers;
using System.Net.Http.Formatting;
using Newtonsoft.Json.Serialization;

public class CamelCaseControllerConfigAttribute : Attribute, IControllerConfiguration 
{
    public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
    {
        var formatter = controllerSettings.Formatters.OfType<JsonMediaTypeFormatter>().Single();
        controllerSettings.Formatters.Remove(formatter);

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

        controllerSettings.Formatters.Add(formatter);

    }
}
2
  • You don't want to receive your API(s) with the same name as your Model classes? Instead, you want them in pasalCase for each model? Commented May 2, 2016 at 14:48
  • It's just a sample for accessing the JsonSerializerSettings object. In my case, I need to change the binder for some controller used by a mobile application Commented May 2, 2016 at 15:02

2 Answers 2

9

This class works fine :

using System;
using System.Linq;
using Newtonsoft.Json.Serialization;
using Microsoft.AspNet.Mvc.Filters;
using Newtonsoft.Json;
using Microsoft.AspNet.Mvc.Formatters;

namespace Teedl.Web.Infrastructure
{
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]

    public class MobileControllerConfiguratorAttribute : Attribute, IResourceFilter
    {
        private readonly JsonSerializerSettings serializerSettings;

        public MobileControllerConfiguratorAttribute()
        {
            serializerSettings = new JsonSerializerSettings()
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver(),
                TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Objects,
                TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple,
                Binder = new TypeNameSerializationBinder("Teedl.Model.Mobile.{0}, Teedl.Model.ClientMobile")
        };
        }


        public void OnResourceExecuted(ResourceExecutedContext context)
        {
        }

        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            var mobileInputFormatter = new JsonInputFormatter(serializerSettings);
            var inputFormatter = context.InputFormatters.FirstOrDefault(frmtr => frmtr is JsonInputFormatter);
            if (inputFormatter != null)
            {
                context.InputFormatters.Remove(inputFormatter);
            }
            context.InputFormatters.Add(mobileInputFormatter);

            var mobileOutputFormatter = new JsonOutputFormatter(serializerSettings);
            var outputFormatter = context.OutputFormatters.FirstOrDefault(frmtr => frmtr is JsonOutputFormatter);
            if (outputFormatter != null)
            {
                context.OutputFormatters.Remove(outputFormatter);
            }
            context.OutputFormatters.Add(mobileOutputFormatter);
        }
    }
}

Use :

[Route("api/mobile/businessrequest")]
[Authorize]
[MobileControllerConfigurator]
public class MobileBusinessRequestController : BaseController
{
Sign up to request clarification or add additional context in comments.

5 Comments

Input/OutputFormatters do not exist anymore on the Resource context. Does anyone know how they can be accessed now?
Want the solution for "Microsoft.AspNetCore.Mvc.Filters" too.
maybe for different framework versions you can use stackoverflow.com/a/44499722/782022
Also see stackoverflow.com/a/52193035/10391 for a way to customize input formatters in the latest Core MVC
Does this work with open api spec?
8

You can use a return type of JsonResult on your controller action methods. In my case, I needed specific actions to return Pascal Case for certain legacy scenarios. The JsonResult object allows you to pass an optional parameter as the JsonSerializerSettings.

public JsonResult Get(string id)
{
    var data = _service.getData(id);

    return Json(data, new JsonSerializerSettings
    {
        ContractResolver = new DefaultContractResolver()
    });

}

To have more consistent controller method signatures I ended up creating an extension method:

public static JsonResult ToPascalCase(this Controller controller, object model)
{
    return controller.Json(model, new JsonSerializerSettings
    {
        ContractResolver = new DefaultContractResolver()
    });
}

Now I can simply call the extension method inside my controller like below:

public IActionResult Get(string id)
{
    var data = _service.getData(id);
    return this.ToPascalCase(data);
}

3 Comments

Yup that's what I'm doing as well - similar scenario: Legacy library that has JS dependencies that were built with older code depending on default case. Can't control app settings client gets the right case. Wish there was a different way to do this to preserve the simpler contract interface.
@RickStrahl Edited answer to include an extension method for handling this. A little cleaner.
Does this work with OpenApi specs?

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.