Someone should do their own formatter picking in their code and not touch the global stuff but I guess it's complex. There are similar issues with routing, authorization and ApiExplorer things.
For now I try to do as many things as possible closer to the code and relying as little as possible on on GlobalConfiguration.
Hopefully Core gives some better support for isolating these things. I'm not so sure though.
The same thing happend to us after upgrading EPiServer 10.10.0.0 to 11.5.1.0. But it's only present in our test environment, can't reproduce it while in debug. Did you manage to find a solution?
We added a custom attribute to the API controllers we created. The attribute enforces our serializer settings.
public class JsonConfigurationAttribute : 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); } }
Found a similar attribute, which I guess is more specific where as yours is more general;
[AttributeUsage(AttributeTargets.Class)] public class UseCamelCasePropertyNamesContractResolverAttribute : Attribute, IControllerConfiguration { public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor) { var formatters = controllerSettings.Formatters; foreach (var jsonFormatter in formatters.OfType<JsonMediaTypeFormatter>().ToArray()) { var newFormatter = (JsonMediaTypeFormatter)Activator.CreateInstance(jsonFormatter.GetType()); newFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); var jsonFormatterIndex = formatters.IndexOf(jsonFormatter); formatters[jsonFormatterIndex] = newFormatter; } } }
Erik, I think you can get this stuff done via global action filters - meaning that you don't fall off the track if someone forgot to decorate controller.
Michael, I would use the version from Erik in case you don't absolutely definetily need the same Type for the formatter that was originally used as the version you pasted here uses reflection to create the new instance (which performance wise is not as good as using new for the known Type). Just mentioning the performance aspect as usually APIs are performance orientated so depending on how "stessed" your API is it can make a difference to use new vs Activator.CreateInstance.
What about the edge cases, the "unknown" formatter requires properties that you are now not passing in the createinstance call? So should you actually clone the existing formatter (you could serialize the instance with Newtonsoft JSON and then deresialize it back and set the wanted ContractResolver ;D).
So, we're doing this when the site starts up:
var settings = GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings;
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
Json that we return from different Web APIs is camel cased, as expected.
Then, if an editor enters "Find" -> "Configure" the Json formatting is set to Pascal Casing for the entire server!
That's not very nice :-)
(Episerver 11.5.1.0)