Download Sample Projects (114.64 kb)
Quite some time back I wrote about
How to use HttpWebRequest to send POST request to another web server in ASP.Net
applications. Now that a lot of silverlight applications are being developed, we have
similar need to post data from silverlight applications. The concept is exactly the
same that I used in ASP.Net. But there is little difference. Silverlight does not allow
blocking synchronous requests. All web requests are asynchronous. That means we have
little bit more book keeping to do and manage the thread contexts etc. to make sure
that then requests complete and we need to do any UI work in call back methods, we
stay in correct thread context.
To keep example simple, I am going to post two values to destination URL using POST
method. In the attached project, on Slots.xaml page, I have added a simple
button Submit Data. And in the event handler of this button, I initiated request
to submit data to Default page of sample web application. The event handler for the
click looks as below.
private void SubmitData_Click(object sender, RoutedEventArgs e)
{
// Prepare web request...
HttpWebRequest myRequest =
(HttpWebRequest)WebRequest.Create("http://localhost/SilverGridWeb/Default.aspx");
myRequest.Method = "POST";
myRequest.ContentType = "application/x-www-form-urlencoded";
myRequest.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), myRequest);
}
This code is pretty much same as what I had in ASP.Net application. There is one difference.
Instead of calling GetRequestStream method, you will need to call
BeginGetRequestStream. This will initiate an asynchronous request. There
are two parameters that are passed to this method. First is the callback method that
needs to be called when this method completes. Second parameter is any object that you want
to pass to callback method. So here I have passed my HttpWebRequest object itself.
Now lets look at what is this callback method GetRequestStreamCallback. I copied
the function from Microsoft documentation itself and made some changes to fit my needs.
private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
System.IO.Stream postStream = request.EndGetRequestStream(asynchronousResult);
string strId = "My Id";
string strName = "My Name";
string postData = "userid=" + strId;
postData += ("&username=" + strName);
byte[] byteArray = System.Text.Encoding.UTF8.GetBytes(postData);
// Write to the request stream.
postStream.Write(byteArray, 0, postData.Length);
postStream.Close();
// Start the asynchronous operation to get the response
request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
}
You can see that in callback method, I grabbed the request object that was passed as
state to the method call. Then I constructed data that I need to pass with the request
that needed to be sent as POST. And then made another async call BeginGetResponse
to submit the data and get the response.
Now lets look at what is this callback method for response.
private void GetResponseCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
string responseString = streamRead.ReadToEnd();
// Close the stream object
streamResponse.Close();
streamRead.Close();
// Release the HttpWebResponse
response.Close();
Action<string> act = new Action<string>(DisplayResponse);
this.Dispatcher.BeginInvoke(act, responseString);
}
void DisplayResponse(string msg)
{
GainersText.Text = msg;
}
Again I passed original HttpWebRequest object to the call method as state. In
the call back method, you can grab the request object and then call EndGetResponse
to get the resonse stream.
Thread Context and Invalid cross-thread access error
You will notice that in GetResponseCallback method I have called BeginInvoke
method on Dispatcher thread. This is very important if you are planning on doing
any UI manipulation in the call back methods. The call back methods are invoked on a thread
that are separate from the UI thread that initiated the request. So if you try to
access any UI element in this callback you will get the following exception thrown.
An unhandled exception ('Unhandled Error in Silverlight Application
Code: 4004
Category: ManagedRuntimeError
Message: System.UnauthorizedAccessException: Invalid cross-thread access.
at MS.Internal.XcpImports.CheckThread()
at System.Windows.DependencyObject.SetValueInternal(DependencyProperty dp, Object value,
Boolean allowReadOnlySet, Boolean isSetByStyle, Boolean isSetByBuiltInStyle,
PropertyInvalidationReason reason)
at System.Windows.DependencyObject.SetValueInternal(DependencyProperty dp, Object value)
at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
at System.Windows.Controls.TextBlock.set_Text(String value)
at SilverGrid.Views.Stocks.GetResponseCallback(IAsyncResult asynchronousResult)
at System.Net.Browser.BrowserHttpWebRequest.<>c__DisplayClassd.
<InvokeGetResponseCallback>b__b(Object state2)
at System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext,
ContextCallback callback, Object state)
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal
(_ThreadPoolWaitCallback tpWaitCallBack)
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state)
') occurred in iexplore.exe [7532].
Therefore it is important that from the callback method, you invoke an action request
on Dispatcher thread of UI.