Consume JSON REST Service via WCF Message Class
Since WCF was designed and envisioned by Microsoft the world has changed and the use of RESTful JSON based web services has increased at the expense of SOAP based services. WCF was updated to reflect this change and for several years has supported RESTful services through webHTTPBinding etc (more on MSDN), and there are many resources on the web for how to consume or host a REST service with WCF, but many of these assume you are not using a generic channel factory approach with the low-level message class. Usually in WCF you would consume a service via a proxy, or perhaps by directly creating a Channel Factory, however these require explicit knowledge of the service contract being consumed and sometimes a more generic solution is required. If, for example, you wanted to create a generic WCF helper class for your application which would build a message directly from passed in data and call a service generically then you could use the Message class directly. This advanced approach is documented for SOAP messaging, but what about if you need to send JSON?
Below are some notes on how you would use the Message class to send JSON in a generic way (i.e. without needing intimate knowledge of the service contract you’re calling).
In the code below we need to pass the Person object named “bob” as JSON so we create a WebChannelFactory and use the “Endpoint1” config (which is very generic in nature). The special WebChannelFactory is a ChannelFactory that automatically adds WebHttpBinding and WebHttpBehavior to the endpoint config if its missing. Then we create a proxy and directly build a Channels.Message class using a SOAP version of “None” (as we’re not using SOAP here but JSON) and the DataContractJsonSerializer .
1
2
3
4
5
6
7
8
9
10
11
12
13
Person bob = new Person() {age = 89, name="Bob"};
WebChannelFactory factory = new WebChannelFactory("Endpoint1");
IRequestChannel proxy = factory.CreateChannel(
new EndpointAddress("http://localhost:8080/Test"));
System.ServiceModel.Channels.Message requestMsg =
System.ServiceModel.Channels.Message.CreateMessage(
MessageVersion.None, "", bob, new DataContractJsonSerializer(typeof(Person)));
requestMsg.Headers.To = new Uri(factory.Endpoint.Address.ToString());
requestMsg.Properties[WebBodyFormatMessageProperty.Name] = new WebBodyFormatMessageProperty(WebContentFormat.Json);
You will notice above we also need to set the message header URI too and also set the WebBodyFormatMessageProperty format to JSON. If we forget to do this step then the message will be sent in XML format despite the previous web config we have set (for more info on this issue see here and here). This is what is sent without setting the WebBodyFormatMessageProperty to JSON:
1
<root type="object"><age type="number">89</age><name>Bob</name></root>
and with the WebBodyFormatMessageProperty set to “WebContentFormat.Json”:
1
{ "age": 89, "name": "Bob" }
Next we call the nice and generic “Request()” method on the proxy and handle the response, picking out the body and de-serialising it into a Person object via the DataContractJsonSerializer.
1
2
System.ServiceModel.Channels.Message responseMsg = proxy.Request(requestMsg);
Person BobResponse = responseMsg.GetBody(new DataContractJsonSerializer(typeof(Person)));
Endpoint Config:
1
2
3
4
5
6
7
8
9
<system.serviceModel>
<client>
<endpoint name="Endpoint1"
address="http://localhost:8080/Test"
binding="webHttpBinding"
contract="System.ServiceModel.Channels.IRequestChannel"
/>
</client>
</system.serviceModel>
In this snippet the only thing that is specific to the service being called is the Person object which the DataContractJsonSerializer needs to know about in order to be able to serialise it into JSON correctly. The actual service call is generic. To make this a completely generic helper we can instead pass in a type for the DataContractJsonSerializer to use instead of a real object, leaving the calling component to pass the right type in when it calls this generic helper method.
If you are already using this message class approach for SOAP services and need to now call some JSON REST services then hopefully this will help.