The "Tech. Arch."

Architecting Forward ::>

Silverlight 2 Beta 2 now supports Web Services Interoperability

This week, the Silverlight team released beta 2. This is a critical new beta since apart from the many UI-oriented features, the networking stack now offers web services interoperability (WSI).

Why is this important?
Well if you are a Microsoft-only shop and all your web services are .asmx-based then you are using a document-style web service model (as opposed to an RPC-style).
But if your enterprise environment has mixed technologies (e.g. MS, Java, Ruby, etc.) then you will find that many older (and sometime not so old) web services follow the SOAP/RPC style.
E.g.
– if you are Ruby On Rails developer and expose web services using the Action Web Service plugin then your services are RPC-style
– if you are using Apache Axis 1.x (often 3rd party software embeds older version of Axis, not Axis 2 which does offer document-style SOAP) – same thing

In beta 1, Silverlight could only call .asmx web services or services using a document-style SOAP implementation. Unfortunately for me, I had built a simplistic web service using the Rails Action Web Service plugin. So I could not get my web service implementation to even build let alone work.

Here is what my test scenario looked like.

In Rails I declared my web service API like so:

class WsTstApi < ActionWebService::API::Base
api_method :get_time,
:returns => [:string]
end

Then I created an implementation for the API in a Rails controller like so:

require 'date'
class WsTstController < ApplicationController
wsdl_service_name 'WsTst'
web_service_api WsTstApi
web_service_scaffold :invoke

def get_time
return DateTime.now.strftime("%H:%M:%S")
end

end

This allowed Rails to generate the following WSDL (abbreviated for clarity) when calling the when requested through http://***mywebservicedomain***/ws_tst/service.wsdl:

<definitions name="WsTst" xmlns:typens="urn:ActionWebService" ...>

<message name="GetTime"/>
<message name="GetTimeResponse">
<part name="return" type="xsd:string"/>
</message>

<portType name="WsTstWsTstPort">

<operation name="GetTime">
<input message="typens:GetTime"/>
<output message="typens:GetTimeResponse"/>
</operation>

</portType>

<binding name="WsTstWsTstBinding" type="typens:WsTstWsTstPort">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/>

<operation name="GetTime">
<soap:operation soapAction="/ws_tst/api/GetTime"/>

<input>
<soap:body namespace="urn:ActionWebService" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>

<output>
<soap:body namespace="urn:ActionWebService" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>

</operation>

</binding>

<service name="WsTstService">
<port name="WsTstWsTstPort" binding="typens:WsTstWsTstBinding">
<soap:address location="http://***mywebservicedomain***/ws_tst/api"/>
</port>
</service>
</definitions>

I then created a Silverlight project and added a service reference to http://***mywebservicedomain***/ws_tst/service.wsdl to generate the web service proxy.
However, it seemed like Visual Studio only knew how to generate a proxy assuming a WCF scenario as opposed to a Silverlight-specific WCF stack. This unfortunately generated an serialization attribute (below the statement with the OperationContractAttribute) such as:

[System.ServiceModel.XmlSerializerFormatAttribute(Style=System.ServiceModel.OperationFormatStyle.Rpc, Use=System.ServiceModel.OperationFormatUse.Encoded)]

The Silverlight ServiceModel library did not implement the XML serializer feature or offer any alternatives.
I tried to comment those out and play around with a different set of attributes but at the end of the day I could not set the serialization to be XML RPC-based.
Microsoft indicated at the time in the Silverlight forums that WSI compatibility would be implemented in beta 2.

Well now the wait is over. So this morning I started a brand new Silverlight 2 project. I added my service reference and looked at the generated proxy. Same problem! The XmlSerializerFormatAttribute is not implemented. So I went back to MSDN to look at the new beta 2 documentation for ServiceModel. And oh surprise, there is now an equivalent feature called DataContractFormatAttribute. I replaced the name and rebuilt. Well it seems that the Use property is not implemented so I removed it. So now my full web service proxy decoration looks like this:

[System.ServiceModel.OperationContractAttribute(AsyncPattern=true, Action="/ws_tst/api/GetTime", ReplyAction="*")]
[System.ServiceModel.DataContractFormatAttribute (Style=System.ServiceModel.OperationFormatStyle.Rpc)]
System.IAsyncResult BeginGetTime(System.AsyncCallback callback, object asyncState);

[return: System.ServiceModel.MessageParameterAttribute(Name="return")]
string EndGetTime(System.IAsyncResult result);

The solution now built successfully. I modified added a button and a text box to my Page.xaml, then implemented the button click handler to do the following:
1. Instantiate the generated SOAP port client
2. Subscribe to the asynchronous web service call response event
3. Call the web service asynchronously

The code looks like this:

private void btnTest_Click(object sender, RoutedEventArgs e)
{
btnTest.IsEnabled = false; // Disable the button while the web service call is in progress
WsTstWsTstPortClient client = new WsTstWsTstPortClient();
try
{
client.GetTimeCompleted += new EventHandler<GetTimeCompletedEventArgs>(client_GetTimeCompleted);
client.GetTimeAsync();
txtResults.Text = "Calling web svc ...";
}
catch (Exception ex)
{
txtResults.Text = string.Format("Error calling web svc: {0}", ex.ToString());
}
}

I then implemented the web service response event handler so that it gets the Result value from the web service response as follows:

void client_GetTimeCompleted(object sender, GetTimeCompletedEventArgs e)
{
string results = string.Empty;

try
{
results = (e == null)
? "Received null response!"
: ((e.Result == null)
? "Received some data but Result is null!"
: e.Result);
}
catch (Exception ex)
{
results = string.Format("Error: {0}", ex.ToString());
}

txtResults.Text = string.Format("Web svc call completed at: {0}nResults: [{1}]",
DateTime.Now.ToString(),
results);
btnTest.IsEnabled = true;
}

I built my solution, deployed the .xapp file to my web site and navigated to my test page. The moment of truth was there as I clicked the Test button.
...
Silverlight calling a RoR web service
... and YES the web service call was successful and the result was displayed in my Silverlight client.

Very cool. This opens up a lot of opportunities for Silverlight client to be used in internal web applications interacting with a broad range of WSI profile web services.

June 8th, 2008 Posted by | Ruby, Silverlight | no comments

No Comments

No comments yet.

Sorry, the comment form is closed at this time.