Introduction
A couple of months ago I started to play with some of the features of JSON objects when being consumed from a webservice. I decided to implement
WCF to test the good integration with JSON data.
The outstanding article
of Gary Stafford helped to configure my first JSON cross domain application consuming
a REST/JSONP WCF service. That included the settings of a cross domain "JSONP endpoint" in the service.
The example covered part of the needs but
it took me a week of research to configure the same WCF service for being consumed from a usual Service Reference SOAP proxy. At that stage, I required a second
"endpoint" configuration.
Background
Remember first of all that JSONP or "JSON with padding" complements the JSON data format. It is used to request data from a server that is deployed
in a different domain, something forbidden by many web browsers.
WCF services are used in enterprise application development. Among many advantages,
WCF can offer several endpoints on the same service (HTTP, TCP etc).
By configuring a WCF service with JSONP and SOAP endpoints, you can make it accessible
from different types of client. This is useful for the clients who can consume the WCF proxy and for the ones who run HTTP communication with JSONP data.
The Representational State Transfer (REST) provides a direct response from the service in several plain formats. If the WCF service is using REST/JSONP, then it is not necessary
for the client to consume the WCF service proxy.
The REST/JSONP performance is relevant in Ajax applications that require JSON data to handle the front end presentation. Now it's possible to get that data with a simple HTTP communication.
The example
This example is basically a server application MyService
that runs a WCF service with two different enpoints to be consumed by a web client application MyClient
:
- The SOAP endpoint is used from a client method
loadDropDownSOAP()
that populates an ASP dropdown list server control. - The second REST/JSONP endpoint is also used by a
JavaScript method
loadDropDownJSONP()
that attempts to populate another dropdown list. This time, the method only requires a HTTP call to get the data from the service.
The client application look and feel is quite simple. Just press over "Load with JSONP call" and "Load with SOAP call" to launch both operations.
Using the code
The zip folder contains both website applications implemented on Visual Studio 2010 (framework 4.0).
MyService
and MyClient
are ready to be compiled and tested in local environment:
MyClient
. You can build and run the application. The client application will be executed on http://localhost:7726/
MyService
. Also ready to be executed. The service will be running over http://localhost:6573/Service.svc
Below you can read further implementation details of the most relevant files.
MyService - Web.config
Take a look at both endpoint and binding configuration. The SOAP request will have to handle with
the address="soap"
endpoint setting.
Important to use binding="webHttpBinding"
for the REST/JSONP endpoint and binding="basicHttpBinding"
on the SOAP endpoint
="1.0"
<configuration>
<appSettings>
</appSettings>
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="false" />
<services>
<service behaviorConfiguration="WebServiceBehavior" name="MyService.Service">
<endpoint address="" behaviorConfiguration="jsonBehavior" binding="webHttpBinding"
bindingConfiguration="webHttpBindingWithJsonP" contract="MyService.IService" />
<endpoint address="soap" binding="basicHttpBinding" contract="MyService.IService" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="jsonBehavior">
<webHttp helpEnabled="true"/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="WebServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="webHttpBindingWithJsonP"
crossDomainScriptAccessEnabled="true" />
</webHttpBinding>
</bindings>
</system.serviceModel>
<system.web>
<compilation debug="false" targetFramework="4.0" />
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
MyService - Iservice.cs (Service Contract)
The format of the response is set in both operation contract. Either WebMessageFormat.Json
or WebMessageFormat.Xml
.
[ServiceContract]
public interface IService
{
[OperationContract]
[WebGet(BodyStyle = WebMessageBodyStyle.Bare,
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "/GetTourListJSONP/")]
cTourList GetTourListJSONP();
[OperationContract]
[WebGet(BodyStyle = WebMessageBodyStyle.Bare,
RequestFormat = WebMessageFormat.Xml,
ResponseFormat = WebMessageFormat.Xml,
UriTemplate = "/GetTourListXML/")]
cTourList GetTourListXML();
}
MyService - Service.scv.cs (implementation of IService interface)
Both service methods return a list of tours implemented in a class cTourList
based in a collection of objects cTour
.
public class Service : IService
{
public cTourList GetTourListJSONP()
{
return CreateTourList();
}
public cTourList GetTourListXML()
{
return CreateTourList();
}
private cTourList CreateTourList()
{
cTourList oTourList = new cTourList();
oTourList.Add(new cTour() { ID = 1, description = "Barcelona" });
oTourList.Add(new cTour() { ID = 2, description = "Paris" });
oTourList.Add(new cTour() { ID = 3, description = "Rome" });
oTourList.Add(new cTour() { ID = 4, description = "London" });
oTourList.Add(new cTour() { ID = 5, description = "Moscow" });
return oTourList;
}
}
Once the service is running in Visual Studio, it's possible to test both endpoints with the WCF Test Client. Recommended to do so before using the final client application.
MyClient - Web.config
SOAP calls from the client application will require Service Reference settings on the project. You can handle that opening Visual Studio 2010 -> Project -> Add Service Reference
After adding the Service Reference, make sure that the text "soap" is included in the "address"
of the endpoint: address="http://localhost:6573/Service.svc/soap". Otherwise, the SOAP call won't be successful.
="1.0"
<configuration>
<system.web>
<compilation debug="false" targetFramework="4.0" />
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:6573/Service.svc/soap" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IService" contract="WCFReference.IService"
name="BasicHttpBinding_IService" />
</client>
</system.serviceModel>
</configuration>
MyClient - Default.aspx.cs
The method loadDropDownSOAP()
uses the endpoint SOAP that we commented above. The call returns an structure implemented in WCFReference.cTourList
.
public partial class _Default : System.Web.UI.Page
{
protected void Button1_Click(object sender, EventArgs e)
{
DropDownListSOAP.Items.Clear();
loadDropDownSOAP();
}
private void loadDropDownSOAP()
{
WCFReference.ServiceClient oWebservice = new WCFReference.ServiceClient();
WCFReference.cTourList oTourList = oWebservice.GetTourListXML();
foreach (WCFReference.cTour oTour in oTourList)
{
DropDownListSOAP.Items.Add(new ListItem(oTour.description, Convert.ToString(oTour.ID)));
}
}
}
MyClient - Scripts/base.js
This file contains the $.ajax
call that will retrieve data from the WCF service in REST/JSONP mode using one specific endpoint configured on the WCF service.
The var wcfServiceUrl
contains the default HTTP address of the service.
Note that the JavaScript method is populating the dropdown list using jQuery.
The $.ajax
call is returning a list of tours contained in the structure jsonpCallback: "tourList"
var wcfServiceUrl, populateDropdown;
wcfServiceUrl = "http://localhost:6573/Service.svc/";
populateDropdown = function () {
var ID, description;
ID = this.ID;
description = this.description;
$("#selectJSON")
.append($("<option></option>")
.val(ID)
.html(description));
};
function loadDropDownJSONP(idDropdownJson, idDropdownSoap) {
$("#" + idDropdownSoap + "").html("");
$("#" + idDropdownJson + "").html("");
$.ajax({
cache: false,
url: wcfServiceUrl + "GetTourListJSONP",
data: "{}",
type: "GET",
jsonpCallback: "tourList",
contentType: "application/javascript",
dataType: "jsonp",
error: function () {
alert("list failed!");
},
success: function (list) {
$.each(list, populateDropdown);
}
});
}
History
- 26/04/2012: First release of the article.