When we developed our OData web service we came accross a situation in which we needed our webservice to return JSON in stead of XML. According to the specification OData supports both JSON and XML by including the $format query option. Unfortunately WCF Data Service does not support the $format query option and will return the following error when this query option is provided:
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"> <code /> <message xml:lang="en-US"> The query parameter '$format' begins with a system-reserved '$' character but is not recognized. </message> </error>
The service tells you that it indeed saw the $format query option but that it didn't recognize it as a valid query option. In this blog post I'm going to show you the different options you have for resolving this little problem.
Enable support for the $format query option
Although WCF Data Service does not support the $format query option it actually supports JSON as an output format. All you need to do is specify "application/json" in the Accept header of your request, but sometimes it is not easy to modify the request headers and sometimes you just want to built an OData compliant data service that supports the $format query option. The basic solution to this problem comes down to removing the $format query option and changing the Accept header to application/json.
You don''t have to do this all by yourself. There are two pieces of code out there that do the job for you:
Both of these pieces of code comes with the nice advantage that they also add support for JSONP callbacks. JSONP is a technique commonly used to circumvent the same origin policy of modern browsers. The same origin policy prevents scripts running on domain1.com to communicate with domain2.com.
Using the DataServicesJSONP library is very simple. You just need to add the JSONPSupportBehaviour to your data service definition, just like in the picture below and you have full support for JSON and JSONP in your OData web service.
Using the WCF Data Services Toolkit not only gives you support for JSON and JSONP, but also adds caching capabilities and offers solutions for a lot different data sources. To use the toolkit in your data service, simply inherit your service from ODataService instead of DataService, just like in the picture below and you have full support for JSON and JSONP in your OData web service.
I created a solution that contains examples of both techniques. You can download it here. The solution contains two wcf services, one that uses DataServicesJSONP and one that uses the toolkit. To compile the code you need to have SQL Compact Edition 4.0 installed. You can install it via the Web Platform Installer or directly via the Microsoft Download Center.
Today I was working on some kind of WCF Data Service, when suddenly while I was testing my data service this error came up:
When I see this kind of errors, I'm always wondering if there is a way that my code can give me some more information on this error. Sure it can! All you need to do is add the following attribute to your service class:
[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)]
I compiled my projects again and ran it. This time I was given some more information on the error.
The additional information the exception gave me, immediately revealed the problem. I changed the constructor of my 'ExampleContext' from an empty constructor to a constructor that required one argument and since WCF Data Service still wants to call my empty constructor it throws an exception because it can't find the empty constructor.
I was wondering why this constraint wasn't enforced at compile time instead of runtime. The WCF people could have defined the DataService class as follows,
public class DataService<T> : IRequestHandler, IDataService where T : new()
which enforces every T to have an empty constructor, but they defined it as
public class DataService<T> : IRequestHandler, IDataService
without the empty constructor requirement. Why would they have done that? Of course this could be an issue with the DataService class, but as Scott Hanselman said at the Dav Days 2011: "Don't blame Microsoft for your bad design." Why would you enforce every T to have an empty constructor? It is just removing flexibility. It was more likely that I did something wrong, so I googled on "dataservice context needs empty constructor". The second hit took my attention, because it talked about an "IDataContextProvider in a constructor", so I clicked on it.
The blog post talked about the usage of Inversion of Control in a webservice and the code in the blog post showed a method called "CreateDataSource()" that was overriden from its base class "DataService" and returned the type T. This was a great solution because know I could call my custom constructor myself. While writing the code to override that method, another thought came up in my head. Of course removing the empty constructor an adding a custom one leads to problems for data services, because it doesn't know about all parameters of the custom constructor and their constraints.