WebClient vs. WebRequest in Silverlight 2

Silverlight Logo

When making web requests in Silverlight 2, its easy to start with the simple WebClient class. In fact, it supports making simple requests (DownloadStringAsync and OpenReadAsync) as well as uploading through the class. On the other hand there are some that swear by the WebRequest route. What's the big difference?

Let's start with WebRequest. The pattern for WebRequest is to call WebRequest.Create:

Uri url = new Uri("http://localhost:8889/Silverlight.js", UriKind.Absolute);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);;

Notice that we are actually using a HttpWebRequest object and casting the return value of the Create call. In reality this object is a BrowserHttpWebRequest object. This class represents a web request in Silverlight 2 (since all requests are actually routed through the browser's networking stack). The reason for this is to make sure that any requests are part of the page.  This means that when you make a request in Silverlight 2 it is bringing with it the same Session ID and cookies.  This also means that for large sized requests, the user may cancel the request.

To execute the request, we need to get a WebResponse object that contains our results of the request. To do this, ordinarily you would call the HttpWebRequest's BeginGetResponse method:

req.BeginGetResponse(new AsyncCallback(WebComplete), req);

This method specifies that you create an AsyncCallback object that describes the callback once the request is complete.  The WebComplete object in the AsyncCallback is actually the method to call (though you could use a delegate or lambda here):

void WebComplete(IAsyncResult a)
{
  HttpWebRequest req = (HttpWebRequest)a.AsyncState;
  HttpWebResponse res = (HttpWebResponse)req.EndGetResponse(a);
  Dispatcher.BeginInvoke(() => status.Text = "Downloading...Done.");
}

Note that we're retrieving the request from the AsyncState and then calling EndGetResponse to complete the operation. The most important thing to understand inside this callback is that it does not happen on the UI thread so any calls to update the UI will result in InvalidThreadAccessExceptions. Typically this means using the Dispatcher to call the UI thread as necessary (as shown). In addition, the response contains a stream that contains the results of the request. To retrieve the data, you need to write code to dig it out of the stream as necessary...not hard, but another step.

In contrast, the WebClient class performs this operation very differently. First, it supports two different request styles: DownloadString and OpenReadDownloadString support the ability to retrieve a string with the contents of the request. The OpenRead style returns a Stream instead. This way you can decide if you need a simple way to retrieve just a string, otherwise you can use the tried and true way of consuming a Stream.

Another change is the WebClient class uses events not AsyncCallbacks to complete the asynchronous model.  The events and execution start of each of the styles are paired up with Completed events and Async execution methods. For example, to make a request with the WebClient with the DownloadString style: 

WebClient client = new WebClient();
client.DownloadStringCompleted += 
  new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
client.DownloadStringAsync(new Uri("/Silverlight.js", UriKind.Relative));

The other big difference is that the WebClient class handles the context switch for the user. What this means is that when the event is fired, it is always called on the UI thread. So your event handling code can update the UI directly as so:

void client_DownloadStringCompleted(object sender, 
                                    DownloadStringCompletedEventArgs e)
{
  status.Text = "Downloading...Done.";
}

So what's the verdict?  There is none. They both are fine to use.  In fact, under the covers the WebClient class uses the BrowserHttpWebRequest class so that it does not matter. If you find the WebClient class easier to use, go ahead. But if you are already familiar with the HttpWebRequest-style classes and AsyncCallback objects, continue to do what you're comfortable with. Just understand the differences between these workhorses of Silverlight 2.

What do you use?

 

Comments

Gravatar

Cameron Saturday, September 27, 2008

I actually am using both for different purposes. I wrote a wrapper around the WebRequest and WebResponse methods to do normal posts to the server and I use the WebClient to download resources from the server because it is already setup to report the progress of the download.

Gravatar

James Saturday, September 27, 2008

I love the simplicity of Webclient but can't use it due to its limitations. Webclient can post binary data to a site, but it can't get a response back (such as posting a zip file to a php backend that will return a string once completed). I know I can use a string upload instead of binary (having to translate both ends), but then the package size doubles or more.

Webclient has no progresschanged event on binary upload/post.

Are these things just not possible with Webclient, or is it just not important to add right now since they have more important bugs to work out? Doesn't look like it will be added in RTW, but hopefully sometime in the future.

Gravatar

Shawn Wildermuth Saturday, September 27, 2008

James,

I would guess that what you're doing is more advanced so you'd want to use the WebRequest instead of the WebClient.

Gravatar

Vladimir Monday, September 29, 2008

I think this is might be important:

SL 2 RC0
"http://silverlight.net/blogs/msnow/default.aspx

Breaking Change #20. Request stream

You must close the request stream on an HttpWebRequest before calling BeginGetResponse(). Previously BeginGetResponse() would close an open request stream."

Gravatar

calm Friday, November 7, 2008

Dispatcher.BeginInvoke(() => status.Text = "Downloading...Done.");

I can't understand the "()=>"
Can you tell me the usage of "()=>"
Waiting your answer...Thank you!

Gravatar

Greg Thursday, December 10, 2009

Shawn,
Thank you for this great explanation!

Cheers,

Greg

Gravatar

stephen patten Thursday, December 30, 2010

Thanks Shawn!

And please accept my condolences.

Stephen

Gravatar

JD Wednesday, February 9, 2011

Hi Shawn, maybe you can hint me about this.
I am presenting a WebBrowser control to the user with a login form so they login. After that I want to request pages via WebClient but I want the user session to remain active (WebClient requests are all redirected to the login page). Is there a way out?

Thanks!

Gravatar

Shawn Wildermuth Wednesday, February 9, 2011

JD,

The fix is to *not* use the webbrowser to login, but to use the Forms Authentication Service and login via a web service call:

http://shawnw.me/ijC9nK


Leave a Comment

*
*
*