Step by Step Guide to Silverlight POX Proxy Sample.

Technorati Tags: Silverlight,POX,XmlSerializer,JSON

Scott Guthrie laid out all the details regarding Silverlight 2.0 Beta1 this morning and if you haven't had a chance to see the keynote then you can view it from VisitMix site. Scott briefly mentioned the support for networking stack in Silverlight and I would like to quote him.

" We have a very robust networking stack in SL2. We have support for REST, for SOAP, for WS-* - we support calling out to anything. We support calling external services, not just back to the server-of-origin. We even have support for sockets, if you want to program at that level. "

Our PM, Yavor has already kicked off the information flow by posting bunch of posts on SilverlightWS blog. Lets quickly recap all the information that has been announced so far.

  1. Install Silverlight Beta1 Runtime- https://www.microsoft.com/silverlight/resources/installationFiles.aspx?v=2.0
  2. Install Silverlight tools (Runtime, SDK, VS Tools) - https://www.microsoft.com/downloads/details.aspx?FamilyId=E0BAE58E-9C0B-4090-A1DB-F134D9F095FD&displaylang=en
  3. Silverlight WebServices MSDN Samples site - https://code.msdn.microsoft.com/silverlightws/Release/ProjectReleases.aspx?ReleaseId=601

We have shipped a prototype for a generic POX Proxy client that we are hoping to get feedback on. We want to evaluate how many people care about a generic POX proxy and what specific features they look for in such a proxy. We are hoping to use all the feedback to decide whether we need to formally add it to the produce or not. This blog post is to give a step by step guide on the sample app we wrote that uses the POX proxy to invoke EBay POX services. We will use the POX proxy to invoke the FindItems call of the EBay POX service via XML and JSON payloads and display the returned results on a DataGrid. Here is the final look of the sample.

PoxSample

Step1: Understanding the requirements of the remote POX Service.

EBay have exposed a POX service endpoint for its shopping services and information on it can be found at https://developer.ebay.com/products/shopping/. We would be using the POX proxy API to invoke the FindItems call. FindItems takes a FindItemsRequest and returns a FindItemsResponse. To have a strongly typed programming model, we need to first create CLR types for FindItemsRequest and FindItemsResponse. EBay doesn't support a Metadata Endpoint and so I manually mapped the types information from Ebay shopping WSDL (You could do the same from the XSD as well). Silverlight's System.Xml.dll doesn't support XmlElement or XElement in Beta1 yet so all xs:any elements from the WSDL have been mapped to object. Since we want the same type to be serialized via XmlSerializer and DataContractJsonSerializer, we will decorate the types with XmlElement and DataContract attributes. Here is how the FindItemsRequest types looks (You can refer the Shopping.cs file from the code to refer the complete set of types).

System.Xml.Serialization.XmlRoot(Namespace="urn:ebay:apis:eBLBaseComponents", ElementName="FindItemsRequest")]

[DataContract]

public partial class FindItemsRequestType : AbstractRequestType

{

private string queryKeywordsField;

private SimpleItemSortCodeType itemSortField;

private bool itemSortFieldSpecified;

private SortOrderCodeType sortOrderField;

private bool sortOrderFieldSpecified;

private int maxEntriesField;

private bool maxEntriesFieldSpecified;

private string postalCodeField;

private string[] sellerIDField;

private string[] sellerIDExcludeField;

/// <remarks/>

[System.Xml.Serialization.XmlElementAttribute(Order=0)]

[DataMember(EmitDefaultValue=false)]

public string QueryKeywords

{

get

{

return this.queryKeywordsField;

}

set

{

this.queryKeywordsField = value;

}

}

/// <remarks/>

[System.Xml.Serialization.XmlElementAttribute(Order=1)]

[DataMember(EmitDefaultValue = false)]

public SimpleItemSortCodeType ItemSort

{

get

{

return this.itemSortField;

}

set

{

this.itemSortField = value;

}

}

........

Step 2: Prepare and Issue FindItemsRequestType

Next step is to use the POX proxy API to issue a call to the FindItems request. We pulled all the POX related code to EbayUtility.cs file. All requests to the Ebay service have some common fields such as appid and version and we will populate the PoxProxyRequest with those common entries. You can also subclass EbayPOXProxyRequest from POXProxyRequest and do the common stuff within that class.

//Add custom Stuff

POXProxyRequest ebayRequest = new POXProxyRequest(new Uri("https://open.api.ebay.com/shopping"));

ebayRequest.WebMethod =

POXUtils.HttpVerbs.Post;

ebayRequest.QueryParameters[

"callName"] = "findItems";

ebayRequest.QueryParameters[

"appid"] = "YOUR_EBAY_APP_ID";

ebayRequest.QueryParameters[

"version"] = "523";

We then specify the request and response encoding (JSON or XML) as a query and also modify the content type to match the encoding.

//Set content type to either JSON or XML

ebayRequest.QueryParameters[

"requestencoding"] = this.parent.ebayPoxEncodingType;

ebayRequest.QueryParameters[

"responseencoding"] = this.parent.ebayPoxEncodingType;

ebayRequest.ContentType =

this.parent.contentType;

We then create an instance of FindItemsRequestType and populate it will all required fields. The only required filed in FindItemsRequest is QueryKeywords and we will populate that field in addition to the couple of optional fields, MaxEntires and PostalCode field. Refer the EbayControl.Xaml file for the input fields.

//Prepare the request

FindItemsRequestType fir =

new FindItemsRequestType();

fir.QueryKeywords =

this.parent.ebayControl.searchQueryTxtBox.Text;

fir.MaxEntries =

Int32.Parse(this.parent.ebayControl.maxEntriesTxtBox.Text);

fir.MaxEntriesSpecified =

true;

if (!string.IsNullOrEmpty(this.parent.ebayControl.postalCodeTxtBox.Text))

{

fir.PostalCode =

this.parent.ebayControl.postalCodeTxtBox.Text;

}

Subscribe to the PoxProxyResponse's GetResponseCompleted event so that we can populate our DataGrid with the search results. Also, we will subscribe to the RequestSerialized and ResponseSerialized event so we can use that to show the actual request/response payload content to the user.

//Callback to call when the POX response is received

ebayRequest.GetResponseCompleted +=

new EventHandler<GetResponseCompletedEventArgs>(this.parent.ebayClient_GetResponseCompleted);

//Display the Actual serialized/deserialized contents on UI

ebayRequest.RequestSerialized =

this.parent.PrintStreamContents;

ebayRequest.ResponseDeSerialized =

this.parent.PrintStreamContents;

Finally issue the request.

//Issue the Async request.

ebayRequest.GetResponseAsync<FindItemsRequestType>(fir);

Step 3: Receive Response and Populate DataGrid

When the response is received from the service, the POX Proxy will invoke the callback provided in the previous step. We then retrieve POXProxyResponse from the GetCompletedEventArgs object. We will use this POXProxyResponse to retrieve the FindItemsResponseType.

POXProxyResponse resp = e.Response;

FindItemsResponseType fir = resp.GetResponse<FindItemsResponseType>();

resultsListBox.ItemsSource = fir.Item;

FindItemsResponseType contains an array of SimpleItemType objects that we will assign to the DataGrid. Since we have a strongly typed object of the response we can use data binding to pull values from the object.

<ListBox x:Name="resultsListBox" Visibility="Collapsed" Width="Auto" SelectionChanged="resultsListBox_SelectionChanged">

    <ListBox.ItemTemplate>

        <DataTemplate>

            <StackPanel Orientation="Vertical">

                <Grid>

                    <Grid.ColumnDefinitions>

                        <ColumnDefinition Width="100"/>

                        <ColumnDefinition Width="60"/>

                        <ColumnDefinition Width="*"/>

                    </Grid.ColumnDefinitions>

                    <StackPanel Orientation="Horizontal" Grid.Column="0">

                        <TextBlock Text="{Binding ConvertedCurrentPrice.currencyID}" Margin="5" Foreground="Blue" VerticalAlignment="Center"/>

                        <TextBlock Text="{Binding ConvertedCurrentPrice.Value}" Foreground="Blue" VerticalAlignment="Center"/>

                    </StackPanel>

                    <Image Source="{Binding GalleryURL}" Height="55" Margin="7,7,5,5" Grid.Column="1"/>

                    <TextBlock Text="{Binding Title}" Margin="5" Foreground="Black" VerticalAlignment="Center" Grid.Column="2"/>

                </Grid>

            </StackPanel>

        </DataTemplate>

    </ListBox.ItemTemplate>

</ListBox>

Step 4: Display Request/Response Stream Data

We had subscribed to the RequestSerialized and ResponseSerialized events and on invocation we will display the Stream contents to the  user. The Stream contents will contain the actual serialized/deserialized data.

//Print the serialized/Deserialized stream content.

StringBuilder builder = new StringBuilder(this.payLoadTextBox.Content.ToString());

builder.Append(

Environment.NewLine);

if (s != null)

{

StreamReader reader = new StreamReader(s);

builder.Append(

"=====================Stream Contents=======================").Append(Environment.NewLine);

builder.Append(reader.ReadToEnd()).Append(

Environment.NewLine);

builder.Append(

"===========================================================");

}

else

{

builder.Append(

"Stream was null");

}

builder.Append(

Environment.NewLine);

this.payLoadTextBox.Content = builder.ToString();

You can switch between XML and JSON payload to use either XmlSerializer or DataContractJsonSerializer. As you can see we have made the process of invoking a POX service very straight forward. If you refer the Digg sample that Scott posted last week, you can see that the sample use WebClient to do the heavy lifting and had to use LINQ to get data from the response. The WebClient class in Silverlight is a subset of the desktop version and support for headers, non Get verbs and ability to get response streams is not available. Once you want to use WebClient/HttpWebRequest to call in to various POX requests you can see that it can get cumbersome. Users can easily create a type for the Digg response and use the POX Proxy to achieve the same effect.

Please download and use the Proxy and provide us with feedback on how we can improve this.

Maheshwar Jayaraman