Vishful thinking…

Making a HTTP Post in Silverlight a.k.a thread hopping

Posted in C#, Silverlight by viswaug on September 17, 2009

Making a HTTP GET request is as easy as pie using the WebClient class that is available in the Silverlight library. The DownloadStringCompleted event callback of the WebClient class is invoked on the main UI thread thus allowing the developer to access UI elements in the callback method itself. If the web service call resulted in a failure, then that condition can be easily determined in the DownloadStringCompleted event callback method by checking if the Error property on the DownloadStringCompletedEventArgs argument passed into the callback method is NULL. The code snippet below illustrates one way to use the WebClient class.

    WebClient webClient = new WebClient();

    webClient.DownloadStringCompleted += (sender, e) =>

    {   

        if (e.Error == null)   

        {       

            //Process web service result here       

            txtBox.Text = e.Result;   

        }    else   

        {       

            //Process web service failure here       

            txtBox.Text = e.Error.Message;   

        }    callback();

    };

    webClient.DownloadStringAsync( new Uri( “URL address that needs to be fetched” ) );

 

But when it comes to making a HTTP POST in Silverlight, things aren’t so simple. Silverlight doesn’t come with any helper classes like the WebClient (for making HTTP GET requests) to help take the hassle out of making a HTTP POST. So, to make a HTTP POST request, we must jump through two threads before we get back on the main UI thread where we will be able to access the UI elements and update them as needed. Here is a simple illustration of the process that we have to go through to make a HTTP POST.

SilverlightHTTPPost

Before we get into code snippets for actually accomplishing the above, let’s get into a little bit of why the above needs to happen. When the user initiates an UI event (button click etc) upon which we must make the HTTP request, we need to ask Silverlight/browser to open a HTTP request pipe to the specified URL. We get this request stream in a different non-UI thread. The reason for this being that Silverlight makes HTTP requests through the browser stack (Silverlight 3 can use its own HTTP stack if needed, but uses the browser stack by default). As you might know, browsers limit the number of outgoing HTTP requests to a given domain name. This limit used to be 2 but modern day browsers have increased this limit (8 in IE8 etc). So, given this limit on the outgoing HTTP requests, there is no guarantee that the browser can open a HTTP request pipe to the URL when demanded by Silverlight. So, we end up getting the request stream in a non-UI thread. We can write/post the necessary data to this request pipe and wait to get a response from the web service. The results from the web service is received on another different non-UI thread. Once we read all the information from the response stream, we can’t start updating the UI elements since we are still on a non-UI thread. So, we need to pass this response from the web service back to the UI thread before we can start updating the UI elements.

Since, we need to pass data between threads as explained above, I had written these tiny little classes to help us through the process of making the HTTP POST request.

 

    public class HttpWebResponseData<T>

    {

        public HttpWebResponse Response { get; set; }

        public T Data { get; set; }

    }

 

    public class HttpWebRequestData<T>

    {

        public HttpWebRequest Request { get; set; }

        public T Data { get; set; }

    }

 

    public class ExceptionEventArgs : EventArgs

    {

        public Exception ExceptionInfo { get; set; }

 

        public ExceptionEventArgs(Exception ex)

        {

            ExceptionInfo = ex;

        }

    }

 

    public class EventArgs<T> : EventArgs

    {

        private T eventData;

 

        public EventArgs(T eventData)

        {

            this.eventData = eventData;

        }

 

        public T EventData

        {

            get

            {

                return eventData;

            }

        }

    }

 

We need to store away the SynchronizationContext of the UI thread before we begin making the HTTP POST request and start jumping threads. This is so that we can get back on the UI thread when the HTTP POST web service call has returned. So, create a variable to hold the in the SynchronizationContext Silverlight UserControl.

        SynchronizationContext syncContext;

 

The following code snippet illustrates how to use the HTTPWebRequest and the HTTPWebResponse classes in Silverlight to perform the HTTP POST.

    //This method will called on user action (button click etc); We are on the UI thread

    private void DoHTTPPost(List<string> stuffToPost)

    {

        //Obtain and store away the SynchronizationContext of the UI thread in the private variable declared above

        syncContext = SynchronizationContext.Current;

        HttpWebRequest request = ( HttpWebRequest ) WebRequest.Create( “URL address to POST to” ) );

        request.Method = “POST”;

        request.ContentType = “text/xml”;

        request.BeginGetRequestStream( new AsyncCallback( DoHTTPPostRequestReady ), new HttpWebRequestData<List<string>>()

        {

            Request = request,

            Data = stuffToPost

        } );

    }

 

    //This method will be called on the non-UI request thread

    private void DoHTTPPostRequestReady(IAsyncResult asyncResult)

    {

        HttpWebRequestData<List<string>> requestData = asyncResult.AsyncState as HttpWebRequestData<List<string>>;

        HttpWebRequest request = requestData.Request;

 

        Stream requestStream = request.EndGetRequestStream(asyncResult);

        DataContractSerializer serializer = new DataContractSerializer(typeof(List<string>));

 

        //Write the string list as XML to the request stream

        StreamWriter sw = new StreamWriter(requestStream);

        serializer.WriteObject(requestStream, requestData.Data);

        sw.Flush();

        requestStream.Close();

 

        request.BeginGetResponse(new AsyncCallback(DoHTTPPostResponseCallback), request);

    }

 

    //This methods will be called on the non-UI response thread

    private void DoHTTPPostResponseCallback(IAsyncResult ar)

    {

        //Ignoring error handling here to keep things simple

        HttpWebRequest request = ar.AsyncState as HttpWebRequest;

        HttpWebResponse response = request.EndGetResponse(ar) as HttpWebResponse;

        Stream responseStream = response.GetResponseStream();

        DataContractSerializer serializer = new DataContractSerializer(typeof(string));

        string result = serializer.ReadObject(responseStream) as string;

 

        //Post the response from the web service back to the UI thread using the SynchronizationContext obtained when initiating the HTTP POST request

        syncContext.Post(DoHTTPPostExtractResponse, new HttpWebResponseData<string>()

        {

            Response = response,

            Data = result

        });

    }

 

    //We are back on the UI thread when this method is called

    private void DoHTTPPostExtractResponse(object state)

    {

        string response = (state as HttpWebResponseData<string>).Data;

        //We can update the UI elements with the response here since we are back on the UI thread

        txtBox.Text = response;

    }

 

I will be posting more helper methods for the above pretty soon. Also, I will delve more into making HTTP POST requests to WCF REST endpoints in future posts.

About these ads

6 Responses

Subscribe to comments with RSS.

  1. […] via web services. Until today, we had to handle HttpWebRequest and HttpWebResponse classes ourselves in order to aquire the desired data.Not any more!PostClientI have just released PostClient, an […]

  2. Kerek egy ég alatt said, on July 11, 2011 at 7:16 am

    […] A fejlesztés során rájöttem, hogy a GET helyett a POST kérést szeretnék indítani Silverlight-ból. Találtam is egy cikket, de a határidő vége felé már nem szeretném átírni a működő programot, majd azután kisérletezek vele. http://viswaug.wordpress.com/2009/09/17/making-a-http-post-in-silverlight-a-k-a-thread-hopping/ […]

  3. […] Yes, you are right. In this post, we see how to insert records into the SQL Azure DB table through OData by simply doing native HTTP Posts at the correct URL. Now, I will do this from a Windows Phone app, that is, Silverlight. This brings in the asynchronous factor since we cannot lock up the UI thread. Essentially, we need to open a Request channel at the right URL, write some byte content into the request stream & then expect a response back. In Silverlight, this means jumping through two different threads before coming back to the main UI thread. Check out this wonderful post on why we need to do this (here). […]

  4. […] Yes, you are right. In this post, we see how to insert records into the SQL Azure DB table through OData by simply doing native HTTP Posts at the correct URL. Now, I will do this from a Windows Phone app, that is, Silverlight. This brings in the asynchronous factor since we cannot lock up the UI thread. Essentially, we need to open a Request channel at the right URL, write some byte content into the request stream & then expect a response back. In Silverlight, this means jumping through two different threads before coming back to the main UI thread. Check out this wonderful post on why we need to do this (here). […]

  5. […] via web services. Until today, we had to handle HttpWebRequest and HttpWebResponse classes ourselves in order to aquire the desired […]

  6. Max said, on January 20, 2012 at 5:53 pm

    In Silverlight 4, where is the DataContractSerializer class? When I do the following it doesn’t see it…

    using System.Runtime.Serialization;


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: