A GetResponse Extension for Synchronized Web Requests in Silverlight
Unlike its CLR counter part, Silverlight’s HttpWebRequest does not provide a synchronous GetResponse method that allows to receive an HTTP response in a blocking operation. As a result, you are forced to use the asynchronous BeginGetResponse method, and handle the results on a callback method.
This makes sense if the response is requested on the UI thread (otherwise, it would freeze the UI), but it might be a problem in certain scenarios (e.g. if you want to submit multiple requests in an orderly fashion).
However, you can get around the issue by using wait handles, which allow you to easily block a running operation. I encapsulated the required functionality in a simple extension method that brings GetResponse method back into Silverlight.
The usage is simple – you just invoke the GetResponse() extension method with an optional timeout. Here’s a sample that submits five synchronous HTTP requests:
private void RunRequests() { for (int i = 0; i < 5; i++) { Uri uri = new Uri("http://localhost/users?user=" + i); HttpWebRequest request = (HttpWebRequest)WebRequestCreator.ClientHttp.Create(uri); //get response as blocking operation which times out after 10 secs HttpWebResponse response = request.GetResponse(10000); } }
You will run into timeout issues if you run execute this method on the UI thread because internally, the requested response accesses the UI thread (for whatever reasons). Only invoke this extension method on background threads!
Here’s the implementation:
public static class RequestHelper { /// <summary> /// A blocking operation that does not continue until a response has been /// received for a given <see cref="HttpWebRequest"/>, or the request /// timed out. /// </summary> /// <param name="request">The request to be sent.</param> /// <param name="timeout">An optional timeout.</param> /// <returns>The response that was received for the request.</returns> /// <exception cref="TimeoutException">If the <paramref name="timeout"/> /// parameter was set, and no response was received within the specified /// time.</exception> public static HttpWebResponse GetResponse(this HttpWebRequest request, int? timeout) { if (request == null) throw new ArgumentNullException("request"); if (System.Windows.Deployment.Current.Dispatcher.CheckAccess()) { const string msg = "Invoking this method on the UI thread is forbidden."; throw new InvalidOperationException(msg); } AutoResetEvent waitHandle = new AutoResetEvent(false); HttpWebResponse response = null; Exception exception = null; AsyncCallback callback = ar => { try { //get the response response = (HttpWebResponse)request.EndGetResponse(ar); } catch(Exception e) { exception = e; } finally { //setting the handle unblocks the loop below waitHandle.Set(); } }; //request response async var asyncResult = request.BeginGetResponse(callback, null); if (asyncResult.CompletedSynchronously) return response; bool hasSignal = waitHandle.WaitOne(timeout ?? Timeout.Infinite); if (!hasSignal) { throw new TimeoutException("No response received in time."); } //bubble exception that occurred on worker thread if (exception != null) throw exception; return response; } }