This post is part of a series about WCF extensibility points. For a list of all previous posts and planned future ones, go to the index page.
In the last post about QueryStringConverter I mentioned that the way to replace the converter used by some operations in WCF WebHttp endpoints (they’re more widely known as WCF REST endpoints, although the purists may disagree with this name) is to create a class derived from WebHttpBehavior and override a virtual method from that base class. In this post I’ll go over the other virtual methods in that class, which makes extending WCF WebHttp endpoints (a little) easier than the general WCF endpoints.
The WebHttpBehavior class is concrete, so it can be used directly. There is one public subclass in WCF, WebScriptEnablingBehavior, which is the behavior used in endpoints compatible with the ASP.NET AJAX library--LINK. That behavior modifies the base with the following changes:
So that class changed a lot of the behavior. And the good thing is that all those changes were made via public virtual method, so it’s possible to recreate something similar with “normal” user code as well.
Those are the virtual members which can be overridden to change the behavior of the base type:
IEndpointBehavior methods: the class implements this interface, so those methods can also be overridden.
Specific WebHttpBehavior properties: all the great virtual properties which are tailored to specific extensions and can be overridden by derived classes.
Specific WebHttpBehavior methods: same as the previous list, this time with methods instead of properties. All of those methods are protected (i.e., can be overridden, but not called directly unless calling the base implementation by the derived type)
Now, this class is fairly extensible, but it has a lot of room for improvement (which is coming in the new WCF Web APIs). The biggest problem is that some of the features, such as URI templates and the help page aren’t aware of the changes done in derived classes. And once you start overriding some of the methods (such as the request formatters, you actually have to implement the URI template yourself – we had to do that in the jQuery Support in the WCF Codeplex page, and it was a lot of work). Some derived classes (such as WebScriptEnablingBehavior simply disallow UriTemplates and the help pages to avoid dealing with this problem.
This is fairly trivial – you just derive from that class, and instead of adding a WebHttpBehavior to your endpoint, you add your own class.
The JsonValue type (and its derived classes) was originally introduced in Silverlight 2 and was a great way to deal with untyped JSON data – but it was limited to Silverlight. When we started the WCF Codeplex project, the first component we released (called “jQuery Support”, since it also added support for forms/url-encoded data, which is the default format for jQuery ajax calls) was a beefed up version of those classes, along with the integration with the WCF service model, by means of a new behavior (we called it WebHttpBehavior3, since WebHttpBehavior2 had been used in the REST Starter Kit – which by the way is deprecated, all of its features either made it to the framework or are being maintained at the WCF Codeplex site). The WebHttpBehavior3 class used many of the extensibility points of WebHttpBehavior to provide a server-side support for untyped JSON data.
But I’ve seen some people trying to do the same in the client side, so I decided to use this scenario as the sample for this post. To avoid problems with UriTemplates, I’ll limit the code to deal with either responses or simple GET requests without templates. Some of the code is “borrowed” from the Codeplex site, some of it is new (I simplified some of it a little to fit in this series format).
And before starting with the code, the usual disclaimer: this is a sample for illustrating the topic of this post, this is not production-ready code. I tested it for a few contracts and it worked, but I cannot guarantee that it will work for all scenarios (please let me know if you find a bug or something missing). A more complete implementation would also include UriTemplate support (or even better, it would be built on top of the new WCF Web APIs), it would have more error handling, and it may also include client-side support for GET operations with a single JsonValue parameter. It could also support complex objects in the query string (right now it only supports record-like data).
Let’s start with the services which I’ll use to test this. The first service implements two contracts, the first with one operation which returns a JsonValue (and takes an optional boolean parameter indicating whether the operation should throw), and the second takes a JsonValue argument which will be bound to the query string, and returns the JSON (as a Stream) back to the client. The second service doesn’t use JsonValue at all, but it will be used to validate that we can use a contract with JsonValue on the client side.
Now we can go to the behavior. First we’ll override the default outgoing formats from Xml to Json. Notice that we can’t simply set those values in the base class from the constructor, since those are virtual properties which shouldn’t be set while the object is being created.
Next we’ll just add a little more validation to the base type. Since our reply formatters can’t handle out / ref parameters, we want to have an error when the service / client is being opened (and not when a request is being made).
Now for the reply formatters. If the operation has a single parameter of type JsonValue (or its subclasses), and the request is not wrapped, we’ll use our own formatter (coming up later). Otherwise, we’ll just return the formatter from the base class.
This is a new “feature” on this sample (which doesn’t exist on Codeplex) which lets a [WebGet] operation have a single JsonValue (or JsonObject, since the query string is a key/value pair collection which maps well to a JsonObejct) parameter – and all of the query string will be bound to it.
Finally, we’ll add an error handler which knows how to deal with WebFaultException<T> exceptions where the detail type is a JsonValue type. This time we’ll use the same trick as we did on the Codeplex project (since the base type doesn’t return the error handlers it added), which is to let the base add first, then find which ones it did, then remove them and pass to our error handler implementation to delegate as necessary.
And the behavior class is done. Now let’s go to the classes which implement the extensions which we overrode. First is the request formatter for GET requests. It uses the HttpUtility class to parse the query string, and creates a simple JsonObject based on the query string values.
Now the reply formatter for the server side. The implementation is fairly simple, as it delegates creating the message to a body writer which is JsonValue-aware, and setting the appropriate message properties to have the response serialized as JSON.
The body writer is also simple, since it uses one of the methods defined in the JsonValueExtension class (from the Codeplex project) which knows how to write the JsonValue in a XmlWriter using the JSON to XML mapping rules.
Next is the client reply dispatcher. Again, using a function from the JsonValueExtensions class to handle the JSON to XML mapping, its implementation is quite trivial.
Finally the error handler, which is the biggest of the classes, although it’s also not that complex. On ProvideFault, if it turns out that the exception which caused the handler to be invoked is a WebFaultException<T>, where the T is a JsonValue type, then we create a JSON message with the detail as given in the exception, and the status code from there as well.
Now that everything is set up we can start testing the program. To test the server part I’ll use a HttpWebRequest-based client, and to test the client part we’ll talk to the service which doesn’t use JsonValue (the one with a single operation with no JsonValue in its signature).
And that’s it. Extending WebHttpBehavior is a little easier than the normal extensibility in WCF, but it still has some ways to go to become easier. Hopefully the WCF Web APIs will solve them to consolidate WCF as a top player in the services world for REST.
[Code in this post]
[Back to the index]