In a standard Web API implementation, if you want to change the ContractResolver
that is used by the Web API framework to serialize your models into JSON, it’s as easy as changing the JsonFormatter
settings in the GlobalConfiguration
.
public static class WebApiConfig { public static void Register(HttpConfiguration config) { var jsonFormatter = config.Formatters.JsonFormatter; jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); } }
In this example, I’m swapping out the ContractResolver
so that Web API will return JSON with camel case property names, which it doesn’t do by default.
In Sitecore, it’s a little more work than just changing the ContractResolver
of the JsonFormatter
in the GlobalConfiguration
. First, Sitecore adds a second JsonFormatter
into Web API’s set of formatters so that JSON is returned instead of XML for browser requests (side note: Sitecore makes a lot of small changes to Web API behind the scenes, which Andreas Gehrke covers nicely here). Second, Sitecore runs its own Web API services that are best not interfered with.
Override the ContractResolver Per Controller
Web API makes it really simple to override the ContractResolver
at the controller level. Create a ControllerConfiguration
Attribute
as follows:
[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; } } }
This will replace all of the JsonMediaTypeFormatters
in the controllerSettings.Formatters
collection with a new instance that has a CamelCasePropertyNamesContractResolver
. Do not modify the JsonMediaTypeFormatters
in the controllerSettings.Formatters
collection directly as any changes you make will affect the Global Configuration as well.
Now add the attribute to the controllers that you want to use this ContractResolver
:
[UseCamelCasePropertyNamesContractResolver] public class ContentController : ServicesApiController { public IHttpActionResult Get() { return Ok(new { Content = "Hello world!" }); } }
Override All Controllers
You can override the ContractResolver
for all controllers in your project by applying an IControllerConfig
Attribute
to a base ApiController
class that your controllers inherit from.
Add the IControllerConfig
Attribute
to your base controller:
[UseCamelCasePropertyNamesContractResolver] public class BaseServicesApiController : ServicesApiController { }
And inherit from your base controller:
public class ContentController : BaseServicesApiController { public IHttpActionResult Get() { return Ok(new { Content = "Hello world!" }); } }
Voila, all controllers in your project that inherit from your base controller will use the new ContractResolver
.
Override ContractResolver Globally
It’s possible to override ContractResolvers
globally, and I include this method for completeness sake, but I do not advocate using it within a Sitecore install. Although I’ve observed that Sitecore’s Item Service API seems to be resilient to global ContractResolver
changes (through some black magic), there’s always the possibility of modifying Sitecore’s API implementation(s) that could lead to unintended consequences elsewhere; don’t do this unless you really know what you’re doing.
To override the ContractResolver
globally, create a new pipeline processor:
public class UseCamelCasePropertyNamesContractResolver { public void Process(PipelineArgs args) { var config = GlobalConfiguration.Configuration; foreach (var jsonFormatter in config.Formatters.OfType<JsonMediaTypeFormatter>()) { jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); } } }
Patch the processor into the Sitecore pipeline (take care to put it after Sitecore’s ServicesWebApiInitializer
, as Martin English points out on his blog):
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"> <sitecore> <pipelines> <initialize> <processor type="SitecoreDemo.UseCamelCasePropertyNamesContractResolver, SitecoreDemo" patch:after="processor[@type='Sitecore.Services.Infrastructure.Sitecore.Pipelines.ServicesWebApiInitializer, Sitecore.Services.Infrastructure.Sitecore']" /> </initialize> </pipelines> </sitecore> </configuration>
Now requests to your all of your Web API controllers (and possibly Sitecore’s Web API controllers) will use your ContractResolver
when serializing response to JSON.
Let me know your thoughts in the comments.
Hello, this is a good blog, but I still have some question.
My WebApi controllers return custom and sometimes 3-Party defined models for example List or Movie and not IHttpActionResult . How can I change my controllers to do that?
How does it work with async controllers. for example I have controllers that are public async Task<List>
Thanks for your interesting post by the way!
Thank you! This worked!