At the end of my last post about adding connectedness to our web service I had added support for XHTML and XML representations. This allowed us to inject URIs into the representation without changing our Model classes. This was relatively simple because, for those representations, we had stuck to the principle introduced in Multiple Representations of having a transformation layer between internal and external representations of our entities. In this post I want to tackle JSON representations.
- SupportedTypes – returns a list of Types for which the class can provide custom serialisation and deserialisation functionality;
StringBuilder builder = new StringBuilder();
Exception ex = controllerContext.HttpContext.Server.GetLastError();
if (controllerContext.HttpContext.AllErrors == null)
throw new InvalidOperationException(string.Format("…"));
Aside: If an error is returned by the engine, the above code only throws an exception if we are not already handling an exception (that is why it checks the AllErrors property). Otherwise we could hit an infinite loop... something I managed to do as I experimented.
- Reuse the jsonSerialization element in the web.config file
Option 1 would make the developer’s job easier. They would just create the class and magically it would be used… in the same way that the ASPX and XSLT files are executed without any configuration. However, the developer would have to take care to keep the namespace of the class consistent with its location. A bigger issue with this approach is deciding how the engine should construct the namespace to search? The last part of the namespace should be the path to the folder containing the view… but what about the root? I thought perhaps the root could be based on the namespace of the HttpApplication or perhaps it could be stored in the web.config file.
string controllerName = controllerContext.RouteData.GetRequiredString("controller");
string cacheKey = _cacheKeyPrefix_JsonConverter + controllerName;
// If the converters are not cached, we need to create a
if (result == null)
// Load the appropriate Web.Config for the current controller.
System.Configuration.Configuration configuration = WebConfigurationManager.OpenWebConfiguration(String.Format(CultureInfo.InvariantCulture, WebConfigLocationFormat, controllerName));
// Get the JSON section.
ScriptingJsonSerializationSection jsonSection = (ScriptingJsonSerializationSection)configuration.GetSection("system.web.extensions/scripting/webServices/jsonSerialization");
// Load the configuration details into the result object
result.MaxJsonLength = jsonSection.MaxJsonLength;
result.RecursionLimit = jsonSection.RecursionLimit;
foreach (Converter converterDefinition in jsonSection.Converters)
Type converterType = BuildManager.GetType(converterDefinition.Type, false);
// Add the new result to the cache
With this framework in place, I created an ItemJsonConverter and an ListJsonConverter. Just to prove it worked, I registered the ListJsonConverter in the root web.config file and the ItemJsonConverter in a web.config file in the /View/Products folder.
Finally, it is possible to inject extra properties into the JSON being created for external consumption. This Serialize method adds an Href property to the JSON… allowing us to add connectedness to the representation:
Dictionary<string, object> result = new Dictionary<string, object>();
Product product = obj as Product;
if (product != null)
// Create the basic representation
result["Id"] = product.Id.ToString("N");
result["Name"] = product.Name;
result["Category"] = product.Category;
result["Details"] = product.Details;
// Add an extra Href property to the representation.
RouteValueDictionary routeValueDictionary = new RouteValueDictionary();
RequestContext requestContext = new RequestContext(new HttpContextWrapper(HttpContext.Current), new RouteData());
result["Href"] = new UrlHelper(requestContext).Action("Item", "Products", routeValueDictionary, HttpContext.Current.Request.Url.Scheme, null);
The code for this version of the web service can be downloaded here: RESTfulMVCWebService15.zip (93 kb)
UPDATE: I have recompiled this code against ASP.Net MVC RC1. This download now includes minor changes required for RC1. I have also removed some code behind files.
UPDATE: I have changed the CSS in the download. In Firefox, the DIV containing the Help menu overlaid the main menu, so you couldn’t actually click on the menu items. Thank you to Jim Solderitsch for reporting the problem.
Another Aside: I’ve also fixed an issue with this version that prevented the site from working when hosted within the Visual Studio Development Server (thank you to Adam Storr for pointing it out). In my last post I mentioned using one of ActionLink’s more heavily parameterised overloads to ensure an absolute URI was returned. I was passing in Context.Request.Url.Authority. Unfortunately, this meant the URI created would include two Port numbers. I could have changed to passing in Context.Request.Url.Host to remove the Port number. However I decided to pass an empty string instead, allowing the framework to extract the host itself. There isn’t an overload that allows you to force the Port number, which is a shame as it means ActionLink cannot be used to redirect a resource to another server if really necessary.