Wednesday, July 4, 2007

WCF interop with complex data types

Sometimes you may want to allow services you have written in WCF to be callable from non-WCF aware clients. Ideally, you should allow anyone writing in JavaScript, PHP, Java and so on to be able to make requests to your services. This idea is fairly simple and well known when using simple value types such as Integers and Strings. You can build a request (in JavaScript) to make to your WCF service as follows:



function SimpleRequest()
{
var xmlhttp = new XMLHttpRequest( );
xmlhttp.open ('POST', 'http://localhost:2407/TestInterop/Service.svc/', true);


xmlhttp.setRequestHeader('SOAPAction', 'http://tempuri.org/IMyService/MyOperation1');
xmlhttp.setRequestHeader('Content-Type', 'text/xml');

var data = '';
data += '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">';
data += ' <s:Body>';
data += ' <MyOperation1 xmlns="http://tempuri.org/">';
data += ' <myValue1>Steven</myValue1>';
data += ' </MyOperation1>';
data += ' </s:Body>';
data += '</s:Envelope>';

xmlhttp.onreadystatechange = function ( ) {
if(xmlhttp.readyState == 4) {
alert("status = " + xmlhttp.status);
alert("status text = " + xmlhttp.statusText);
alert(xmlhttp.responseText);

document.form1.result.value = xmlhttp.responseText;
}
};


However when using complex types, things are not necessarily more difficult, but there are some things to watch out for. Specifically, you need to ensure that the SOAP request you build correctly sets the namespace of the complex type you are sending to the WCF service. So if you have defined a DataContract as follows in the service:



[DataContract(Name = "DataContract", Namespace = "http://livz.org/wcf/name")]
public class DataContract
{
string firstName;
string lastName;

[DataMember]
public string FirstName
{
get { return firstName;}
set { firstName = value;}
}
[DataMember]
public string LastName
{
get { return lastName;}
set { lastName = value;}
}
}


Then you must ensure to set this namespace in the request. In fact, if you do not, a default namespace ("http://schemas.datacontract.org/2004/07/") is applied to the complex type and simply passing the serialized data will not work (well, it does not fail, but the values of your type are not set!). So, to correctly make the request for the above data type, you can use the following JavaScript:



function ComplexRequest()
{
var xmlhttp = new XMLHttpRequest( );
xmlhttp.open ('POST', 'http://localhost:2407/TestInterop/Service.svc/', true);

xmlhttp.setRequestHeader('SOAPAction', 'http://tempuri.org/IMyService/MyOperation2');
xmlhttp.setRequestHeader('Content-Type', 'text/xml');

var data = '';
data += '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">';
data += ' <s:Body>';
data += ' <MyOperation2 xmlns="http://tempuri.org/">';
data += ' <dataContractValue xmlns:a="http://livz.org/wcf/name"><a:FirstName>Francisca</a:FirstName>

<a:LastName>livingstone</a:LastName></dataContractValue>';
data += ' </MyOperation2>';
data += ' </s:Body>';
data += '</s:Envelope>';

xmlhttp.onreadystatechange = function ( ) {
if(xmlhttp.readyState == 4) {
document.form1.result.value = xmlhttp.responseText;
}
};


xmlhttp.send(data);
}

2 comments:

Anonymous said...

Hi, I have been searching from past 2 days on accessing WCF services using scriptmanager. I am very much disappointed for not finding enough answers for this question. I just want to know whether it is possible to access WCF services using scriptmanager in .net 2.0

I have seen some samples for .net framework 3.5

your help needed, thanks in advance
-Vikram

Anonymous said...

What you have written is very interesting. I am interested in using XMLHttpRequest instead of the ASP Script Manager because the Script Manager doesn't seem to support any kind of security. However, in ASP, the framework automatically handles serializing and deserializing objects (i.e. for your example you would just have access to the object you defined - no need to worry about the XML).

My question is is there a JavaScript library out there that will serialize and deserialize Xml in to objects so that it is easier to deal with the Xml response?