WCF POX, JSON and SOAP Coexist

By admin - Last updated: Monday, August 16, 2010 - Save & Share - 30 Comments

Sometimes, we want to make a service available in different protocols so that clients could have an option to choose one of their favorite methods to consume the web services.

Here we are going to talk about how to make one WCF service available in POX(Plain Old XML as XML for short), JSON and SOAP from different endpoints within same service host.

A bit background. A WCF service can have multiple endpoints configured within the same host.
For example, TestService can be exposed at the following endpoints.

Each endpoint could have its own binding and endpointBehavior. The binding can specify the protocol that the endpoint agreed with. The endpointBehavior can be utilized to extend the service/client run-time, in this case, it specifies the serialization (more details in later section)

In this section, we will use basicHttpBinding and webHttpBinding that pre-configured by the WCF framework.

Let’s do some coding, first we create a simple service with only one operation contract.

// Service Contract
    [ServiceContract(Namespace = "http://www.example.com/service")]
    public interface ITestService
    {
        [OperationContract]
        [WebGet]
        FooDataContract[] GetFoo(string name, int age, int numberOfFoo);
    }

There is WebGet attribute, mocked up on GetFoo operation contract, specifies this operation can
understand HTTP GET verb in the request, the goal is to make the operation RESTful. WebGet attribute is only understood by WCF WebHttpBehavior, so it does not affect the SOAP request since the SOAP endpoint will not use WebHttpBehavior (This will be explained in endpoint configuration section)

You may notice the FooDataContract array is the return type of the method, we will defined a simple class for it.

// DataContract
    [DataContract(Name = "Foo",
Namespace = "http://www.example.com/data")]
    public class FooDataContract
    {
        [DataMember(Order = 0)]
        public string Name { get; set; }
        [DataMember(Order = 1)]
        public int Age { get; set; }
    }

There is nothing much to explain about the datacontract, it simply tells the serializer the name and namespace that the object should be serialized on wire and the ordering of the properties at message level. However the auto property i.e. {get;set;} is a neat feature of .NET 3+

The final step of coding the service is the service implementation.

// ITestService Implementation,
    // Each request will initialize its own instance of the TestService
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class TestService : ITestService
    {
        public FooDataContract[] GetFoo(string name, int age, int numberOfFoo)
        {
            List<FooDataContract> result = null;
            for (int i = 0; i < numberOfFoo; i++)
            {
                if (result == null)
                    result = new List<FooDataContract>();

                result.Add(new FooDataContract()
                {
                    // default to "null"
                    Name = (name ?? "null") + "_" + i,
                    Age = age
                });
            }
            // return null or array
            return result == null ? null : result.ToArray();
        }
    }

The service returns a list of FooDataContract objects to the clients according to the parameters.

We have completed the WCF service implementation, but we need to make it configured and hosted for the main purposes of this post, the most interesting part is the configuration part

There are three key areas of configuration i.e. service endpoints, binding and endpointBehavior.
Let’s start with endpoint configuration, it is completed server app.config below.

<configuration>
  <system.serviceModel>
    <!-- bindings -->
    <bindings>
      <basicHttpBinding>
        <binding name ="soapBinding">
          <security mode="None">
          </security>
        </binding>
      </basicHttpBinding>
      <webHttpBinding>
        <binding name="webBinding">
        </binding>
      </webHttpBinding>
    </bindings>
    <!-- behaviors -->
    <behaviors>
      <endpointBehaviors>
        <!-- plain old XML -->
        <behavior name="poxBehavior">
          <webHttp/>
        </behavior>
        <!-- JSON -->
        <behavior name="jsonBehavior">
          <enableWebScript  />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="defaultBehavior">
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="WcfService.TestService" behaviorConfiguration="defaultBehavior">
        <host>
          <baseAddresses>
            <!-- note, choose an available port-->
            <add baseAddress="http://localhost:81/TestService" />
          </baseAddresses>
        </host>
        <endpoint address="soap"
                  binding="basicHttpBinding"
                  bindingConfiguration="soapBinding"
                  contract="WcfService.ITestService" />

        <endpoint address="pox" 
                  binding="webHttpBinding"
                  bindingConfiguration="webBinding" 
                  behaviorConfiguration="poxBehavior"
                  contract="WcfService.ITestService" />

        <endpoint address="json"
                  binding="webHttpBinding"
                  bindingConfiguration="webBinding" 
                  behaviorConfiguration="jsonBehavior"
                  contract="WcfService.ITestService" />

      </service>
    </services>
  </system.serviceModel>
</configuration>

As mentioned earlier, there will be three endpoints available, pox, json as REST service and soap as SOAP service.

The SOAP endpoint uses basicHttpBinding which comes from WCF, it specifies the endpoint uses SOAP as service (You can specify the version of the message, security etc in “soapBinding” section, but will not be explained here in details).

The POX endpoint uses webHttpBinding with “webBinding” configuration section and the endpointBehavior is poxBehavior.

This tells the POX endpoint it can receive generic HTTP requests and the poxBehavior with webHttp element tells the current endpoint will provide web style services and also at run time, it looks for WebGet attribute on the operation contract in order to determine if the parameters can be parsed from the URI/URL in the GET request. The result is in XML (Plain Old way that you normally see how XML looks like, not SOAP).

The JSON endpoint is specified by webHttpBinding again because it will surely understand generic HTTP request. The special part in this endpoint is the endpoint behavior configuration which is jsonBehavior. It contains enableWebScript element which is derived type of webHttp used in POX endpoint behavior, this means on top of POX REST service, it enables the JavaScript proxy endpoint which allows AJAX calls from Browser. In current context, the purpose is to make the response data to the clients in JSON format.

The code below is the console host for the service.

class Program
    {
        static void Main(string[] args)
        {
            // per call instancing
            ServiceHost host = new ServiceHost(typeof(TestService));
            // start listening
            host.Open();
            Console.WriteLine("Service started \nPress [Enter] to Close");
            Console.ReadLine();
            host.Close();
        }
    }

We have done most of the service, it is time to write a client to test against it. It is a bit tricky to get the client going by calling each of the endpoint.

Firstly, create a new console project and add the service reference to http://localhost:81/TestService
Now you have automatically generated proxy with configuration in the app.config
The generated content within app.config file is useless, we need to manually configure it.
Here is a completed configured client app.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
      <!-- bindings -->
      <bindings>
        <basicHttpBinding>
          <binding name ="soapBinding">
            <security mode="None">
            </security>
          </binding>
        </basicHttpBinding>
        <webHttpBinding>
          <binding name="webBinding">
          </binding>
        </webHttpBinding>
      </bindings>
      <!-- behaviors -->
      <behaviors>
        <endpointBehaviors>
          <!-- plain old XML -->
          <behavior name="poxBehavior">
            <webHttp/>
          </behavior>
          <!-- JSON -->
          <behavior name="jsonBehavior">
            <enableWebScript  />
          </behavior>
        </endpointBehaviors>
        <serviceBehaviors>
          <behavior name="defaultBehavior">
            <serviceDebug includeExceptionDetailInFaults="true" />
            <serviceMetadata httpGetEnabled="true" />
          </behavior>
        </serviceBehaviors>
      </behaviors>
        <client>
          <endpoint address="http://localhost:81/TestService/soap"
                    binding="basicHttpBinding"
                    bindingConfiguration="soapBinding"
                    contract="TestServiceReference.ITestService" />

<!-- only one endpoint can be configured for client  at a time
          <endpoint address="http://localhost:81/TestService/pox" 
                    binding="webHttpBinding"
                    bindingConfiguration="webBinding" 
                    behaviorConfiguration="poxBehavior"
                    contract="TestServiceReference.ITestService" />
          <endpoint address="http://localhost:81/TestService/json"
                    binding="webHttpBinding"
                    bindingConfiguration="webBinding" 
                    behaviorConfiguration="jsonBehavior"
                    contract="TestServiceReference.ITestService" />
-->

        </client>
    </system.serviceModel>
</configuration>

In client Program.cs file, calling the method GetFoo with SOAP,
Remember, only one endpoint can be configured at a time, let’s try the SOAP one first by commenting out the other two endpoints,

<!--
***http headers***
POST /TestService/soap HTTP/1.1
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://www.example.com/service/ITestService/GetFoo"
Host: localhost:81
Content-Length: 211
Expect: 100-continue
-->

<!-- SOAP -->
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <GetFoo xmlns="http://www.example.com/service">
      <name>wall-e</name>
      <age>256</age>
      <numberOfFoo>2</numberOfFoo>
    </GetFoo>
  </s:Body>
</s:Envelope>

The request is HTTP POST with SOAP content to the service.
It is the corresponding SOAP response below.
Yes, we have successfully made a SOAP request and obtained a SOAP response.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <GetFooResponse xmlns="http://www.example.com/service">
      <GetFooResult xmlns:a="http://www.example.com/data" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <a:Foo>
          <a:Name>wall-e_0</a:Name>
          <a:Age>256</a:Age>
        </a:Foo>
        <a:Foo>
          <a:Name> wall-e_1 </a:Name>
          <a:Age>256</a:Age>
        </a:Foo>
      </GetFooResult>
    </GetFooResponse>
  </s:Body>
</s:Envelope>

Let’s try POX endpoint, comment out SOAP and JSON endpoint and leave pox endpoint uncommented. Run the client again. Here we come to a tricky part, we get an exception!

Operation ‘GetFoo’ of contract ‘ITestService’ specifies multiple request body parameters to be serialized without any wrapper elements. At most one body parameter can be serialized without wrapper elements. Either remove the extra body parameters or set the BodyStyle property on the WebGetAttribute/WebInvokeAttribute to Wrapped.

This means by default, the client tries to request the service with POST, therefore client tries to serialize the parameters in the http body. Without SOAP, there is no way to serialize
GetFoo(“wall-e”, 256, 2) into XML

<!–  this is not XML –>
      <name>wall-e</name>
      <age>256</age>
      <numberOfFoo>2</numberOfFoo>

unless you specify the body style should be wrapped automatically.

<!–  this is wrapped XML –>
      <request>
        <name>wall-e</name>
        <age>256</age>
        <numberOfFoo>2</numberOfFoo>
      </request>

However, we want to call the service by GET, not POSTing the data to the server for a “GET” actions. There is an important extra step to do in order to let the client dispatch the HTTP request with parameters in the URL which conforms to the REST service call. It is the WebGet attribute on the client side, while generating the proxy by adding service reference, WebGet attribute does not add to the service contract automatically. WebGet attribute is also required on the client side for consuming the service in the REST way. (Remember, webHttp and enableWebScript behavior look for this attribute to design the HTTP request in the earlier discussion). What we need to do is simply locate the file “reference.cs” generated after adding service reference.

Search keyword: “public interface ITestService” within the client project, you will find the generated service contract, add [System.ServiceModel.Web.WebGet] on top of the OperationContract.

[System.ServiceModel.Web.WebGet]
WcfServiceClient.TestServiceReference.Foo[] GetFoo(string name, int age, int numberOfFoo);

OK, it is done, run the client again,

// REST request with parameters in the URL
GET /TestService/pox/GetFoo?name=wall-e&age=256&numberOfFoo=2 HTTP/1.1
Content-Type: application/xml; charset=utf-8
Host: localhost:81

The request no more contains any HTTP body as all the information is expressed in the URL.
The following replied XML (Plain Old XML) is really just the serialized form of array of FooDataContract.

<ArrayOfFoo xmlns="http://www.example.com/data"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">

  <Foo>
    <Name>wall-e_0</Name>
    <Age>256</Age>
  </Foo>
  <Foo>
    <Name>wall-e_1</Name>
    <Age>256</Age>
  </Foo>
</ArrayOfFoo>

The JSON request, this is the final test in this session,
The request is exactly the same as the POX request,

// REST request with parameters in the URL
GET /TestService/json/GetFoo?name=wall-e&age=256&numberOfFoo=2 HTTP/1.1
Content-Type: application/xml; charset=utf-8
Host: localhost:81

As expected the response is in JSON format.

{"d":[
      {
        "__type":"Foo:http:\/\/www.example.com\/data",
        "Name":"wall-e_0",
        "Age":256
      },
      {
        "__type":"Foo:http:\/\/www.example.com\/data",
        "Name":"wall-e_0",
        "Age":256
      }
    ]
}

This example uses windows console as service self-host, in IIS6 or 7, it will be slightly different.
The client utilizes Visual Studio 2008 service proxy generation, in real world, programmers might choose generic WebClient to consume the REST service. A must-have tool during web service development is a http proxy analyzer, not only it can show you how things have been done at the wire level, also it helps a lot to debug the services and clients during the development. All the raw JSON, XML, SOAP or even HTTP headers are exposed to programmers. There are free and commercial tools available, Google will find one of the best HTTP proxy analyzers for you.

Download WCF Soap POX JSON REST Service Example

UPDATE:
ASP.NET MVC is a very good framework for building 100% RESTful web services.




Posted in WCF • Tags: , , , , , , , , , Top Of Page

30 Responses to “WCF POX, JSON and SOAP Coexist”

Comment from Steve
Time October 25, 2008 at 4:25 am

Great post, thanks!

I was able to setup my own service to do these three formats all at once based on this. Noticed a typo in your post though, when you’re talking about the JSON request, the GET request should be:

// REST request with parameters in the URL
GET /TestService/json/GetFoo?name=wall-e&age=256&numberOfFoo=2 HTTP/1.1
Content-Type: application/xml; charset=utf-8
Host: localhost:81

Comment from admin
Time October 26, 2008 at 10:15 pm

Thanks for pointing out the typo. I have changed the ‘pox’ to ‘json’

Comment from Steve
Time December 6, 2008 at 5:25 am

Great post and examples. Really appreciate it!

-Steve

Comment from Nestor
Time January 21, 2009 at 5:15 am

Very nice post, self explanatory and easy to follow and understand.
Exactly what I was looking for about this possibility within WCF.

Thanks heaps, much appreciated.

Keep up your good posting.

Nestor.

Comment from Cheeso
Time April 1, 2009 at 10:43 am

Very interesting and useful.

one problem? The client program.cs seems to be wrong – you actually show the server program.cs.

Also, one question: what about content negotiation? Rather than exposing different content types for the same resource at different URIs, why not have a single URI, and then allow the content type (json, pox, more?) to be negotiated at request time. HTTP allows this.

Of course if you are using WsTcpBinding then you don’t have the content type negotiation of HTTP, but I suppose you could default to SOAP in that case.

Comment from admin
Time April 1, 2009 at 3:23 pm

Thanks Cheeso and your reply, I copied the server’s program.cs code onto the blog. My bad.

Content type negotiation is a very good idea, I’ve seen an example of it from Codeplex. However, one important issue with it is users are not able to modify http header field easily in the web browsers. In contrast, by expressing it at the endpoint level, you can easily control what kind of message you expect in the response by jsut altering the URL.

Comment from AllanC
Time July 9, 2009 at 12:17 pm

Thanks! This gave me enough pieces to get my SOAP and REST working together. Too bad that you cannot use UriTemplate and JSON (web scripting) together (it violates some WCF constraint).

Comment from Dave
Time July 22, 2009 at 4:28 am

You shouldn’t need UriTemplate. I was trying to use it to format a query string but all i need to do was this:

[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest,
Method = "GET",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
Result RecordMetric(int learnerId, string metricId, decimal value);

I can then call my service like this:

http://myserver:someport/service/RecordMetric?learnerId=1&metricId=123&value=40

Comment from Dave
Time July 22, 2009 at 4:32 am

Wow it really killed my URL… let me try that again:

mysever:someportnumber/service/RecordMetric?learnerId=1&metricId=123&value=40

Comment from admin
Time July 22, 2009 at 12:44 pm

Hi Dave, I think what AllanC wants to do is to make the url a lot more nicer. In your case, if UriTemplated, it could look like

../RecordMetric/1/123/40

Comment from Joan
Time November 7, 2009 at 1:58 am

Sorry but I am stucked in trying to get the info out of this data:

{“d”:[{"__type":"ComboItem:http:\/\/schemas.---------------\/2009\/07\/30\/ComboItem","Id":"ECL","Name":"Es-----ro"}]}

what should I use in the javascript to get my ComboItem.id and .Name fullfiled with values.

Thanks a lot!

Comment from admin
Time November 7, 2009 at 9:00 am

@Joan,
The “d” is an array with one element
try this

var result = {ā€dā€:[{"__type":"ComboItem","Id":"ECL","Name":"Es-----ro"}]};
alert(result.d[0].Id);

Comment from Joan
Time November 9, 2009 at 11:53 pm

Thanks that helped to understand a bit more.

Finally I got the array doing this eventhough I don’t really get why should work this way . . .

var jsonstring = xmlHttp.responseText;
var comboitem = eval(‘ (‘ + jsonstring + ‘ ) ‘);

$(‘#resp_DGO’).empty();

$.each(comboitem.d, function(i) { $(‘#resp_DGO’).append(‘ ‘ + comboitem.d[i].Name + ”);
})

Thanks for the great post!

Comment from Database Consultant
Time January 1, 2010 at 12:15 am

Very nice post. WCF is pretty versatile. I’ve been able to get SOAP and REST working together nicely now.

Comment from Web Design, Manchester
Time February 28, 2010 at 10:14 pm

Great article – keep them coming please

Comment from Zaro
Time March 12, 2010 at 9:48 pm

Great article, helps me with error:
HTTP Error 405 Method not allowed – I’ve changed the client proxy interface decorated methods with [System.ServiceModel.Web.WebGet].
To make example work for me, I’ve changed the config paths:
/I’ve received error like: has zero application (non-infrastructure) endpoints./
from: /service config/

to

and: /client config/
<endpoint address="http://localhost:81/TestService/soap&quot;
to
<endpoint address="http://localhost:81/TestService.svc/soap&quot;

Thanks for the post!

Comment from Bunty
Time July 10, 2010 at 9:02 am

What would be difference if I am looking to host this service with multiple endpoints in IIS? A quick answer to this would be really helpful.

Thanks in advance.

Comment from admin
Time July 10, 2010 at 11:33 am

you will need TestService.svc file for your service implementation and the baseAddress=”http://localhost:81/TestService.svc”
So the endpoints are
http://localhost:81/TestService.svc/soap
http://localhost:81/TestService.svc/pox
http://localhost:81/TestService.svc/json

Comment from Bunty
Time July 15, 2010 at 7:42 am

What is the best way to handle exceptions for both SOAP and REST together?

How do you differentiate between SOAP call and REST call to a specific operation? so that we can handle them appropriately and send proper response back such as custom fault for SOAP and some standard Http response back for REST type of request.

This is something very urgent and quick help would be really appreciated.

Thanks in advance.

Comment from Admin
Time July 15, 2010 at 10:27 am

It should be the same way to handle the exceptions.
There is no difference at the code level( c# ), the difference is at the configuration level. e.g. which endpoint, binding do you use for talking to the webservice.

Comment from Chris.A
Time October 2, 2010 at 3:10 am

Hi,
I have a question about this example. I tested it and it works great, but now, if I want to add a certificate to one of the end point, how Can I manage to do it ?

Comment from Ray
Time October 2, 2010 at 8:09 pm

Hi Chris, you will need to configure the endpoint with the binding that support https security, and the service behavior should specify the certificate. more details check out this msdn link. http://msdn.microsoft.com/en-us/library/ms729700.aspx

Comment from Thanh
Time December 8, 2010 at 2:07 am

I’ve spent two days on WCF, JSON, and SOAP and never got it working until I found this article. Thanks for sharing this!

Pingback from Bad Request with SOAP endpoint WCF Webservice | Software development support, software risk,bugs for bugs, risk analysis,
Time October 6, 2011 at 1:02 am

[...] tried using the configuration indicated by this tutorial, to no [...]

Comment from Bob
Time October 28, 2011 at 9:48 am

You say it’s only possible to have one end point configured. Is it not possible to have them run in parallel? I.e. One user wants to use json another pox is this possible? Have I missed something?

Comment from Nimish
Time November 4, 2011 at 4:04 am

Hi,

I am trying to run this solution on VS 2010, and it is not working as expected. Is there something that needs to be done make it run on VS 2010. I have requirement to have fucntions in a web service, one returning soap and another json.

Comment from Mark Casey
Time February 9, 2012 at 2:48 am

Thanks a millioin for this post great post!

Comment from Tom
Time June 21, 2012 at 6:18 am

Please note that this solution does not actually work now. WCF no longer truly supports 2 REST endpoints with different configurations (one for XML, one for JSON for example). This breaks the WSDL for the service, thus breaking the SOAP endpoint. You can see more about that here http://stackoverflow.com/questions/10022358/cant-expose-rest-xml-rest-json-and-soap-properly-via-configuration-only-for-wc

Comment from Faisal Noman
Time August 23, 2012 at 6:46 am

Really great post and example. appreciate

Comment from Bhaskar
Time April 8, 2013 at 6:41 pm

Hello,
it’s great Post & nice to understand.

i m developing WCF-REST comunicate with C# winform App but facing few problems.i m trying to send multiple use defined complex Strong Typed class objects on POST of WCF-REST with Bare Body style but service picking them as null?if yes can you mail me the example of that of VS 2010.
Also i want help on Provide REST Service To Sybase Unwired Platform.

Thanx in advance

Write a comment


Captcha: 7 × six =