(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Windows Phone 7 Data Access Strategies: HttpWebRequest

(3 votes)
Andrea Boschin
>
Andrea Boschin
Joined Nov 17, 2009
Articles:   91
Comments:   9
More Articles
1 comments   /   posted on Aug 23, 2011
Categories:   Data Access , Windows Phone

Download source code

In the previous part of this series I've spoken about WebClient, the simplest class that allows the access to remote resources and I've explained that it is built on top of the HttpWebRequest class. Since WebClient is a class that is made to simplify the access to the HTTP resources, by hiding the details of the call, the HttpWebRequest makes evident the duality of Request/Response that is natural when you deal with HTTP. As we will see, this implies that the HttpWebRequest class gives you a more fine grained control of what is happening but, it also requires more work to accomplish the various tasks required to complete the call.

Request/Response

As I've said when you use the HttpWebRequest class it becomes evident that the HTTP call you are doing is made of two separated parts. Indeed you have to deal with a couple of class instead of one: WebRequest and WebResponse. The WebRequest (HttpWebRequest) represents the very first part of the call, the one that goes from the client to the server, transporting the request header, cookies, credentials, and obviously posted data. After the server received the request, you have to deal with the WebResponse that contains data returned by the server, the response headers, and so on. When you place a call with HttpWebRequest you have to follow the following path:

  1. Create an instance of the HttpWebRequest
  2. If you have to post something get the request stream
  3. Then write posted data to the request stream
  4. Get the response (that is actually what starts the call)
  5. Get the response stream
  6. Read returned data from response stream
  7. Close all thing

What makes things hard is the process of getting the request stream and the response which are both asynchronous, so, in the case you have also to upload something, you have to deal with two asynchronous calls instead of one. Here is the how the HttpWebRequest can be used to retrieve a rss feed; in this case I don't send anything to the server so the asynchronous call is one:

public void GetFeed()
{
    HttpWebRequest request = HttpWebRequest.CreateHttp("http://www.slpg.org/syndication.axd");
    request.BeginGetResponse(new AsyncCallback(HandleResponse), request);
}
 
public void HandleResponse(IAsyncResult result)
{
    HttpWebRequest request = result.AsyncState as HttpWebRequest;
 
    if (request != null)
    {
        using(WebResponse response = request.EndGetResponse(result))
        {
            using (StreamReader reader = new StreamReader(response.GetResponseStream()))
            {
                string feed = reader.ReadToEnd();
                // do something with the feed here
            }
        }
    }
}

Please take immediately note of the "using" blocks that are intentionally used every time it is found a class that implements IDisposable like WebResponse and StreamReader. These blocks are useful to release resources that are not used anymore. But in this sample, what is immediately clear, is that the asynchronicity model of this class is callback-based (using the Begin-End pattern) instead of event-based. This implies you have to write lot of code to retrieve the response but for the sake of the application layering, it is better because it does not requires to attach and detach events. Again, the best thing is to follow a pattern to encapsulate the network calls in a isolated layer:

public void GetFeed(Action<string> result)
{
    HttpWebRequest request = HttpWebRequest.CreateHttp("http://www.slpg.org/syndication.axd");
 
    request.BeginGetResponse(
        a =>
        {
            string data = null;
 
            using (WebResponse response = request.EndGetResponse(a))
            {
                using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                {
                    data = reader.ReadToEnd();
                }
            }
 
            Deployment.Current.Dispatcher.BeginInvoke(() => result(data));
        }
        , null);
}

If you carefully read the article on WebClient you have for sure noticed a big difference between this last example and the corresponding example in the other article. You are to be aware that once you return the result, you have to make the thread marshaling to the user interface. This is the very biggest difference between WebClient, where the thread marshaling is made by the class and HttpWebRequest, where you have to make the marshaling by your own.

Since this may appear annoying you have to know that it is probably the best reason to use this class instead of the WebClient, specifically if you have to perform some operations on retrieved data before returning them to the UI thread. As an example if you need to perform a deserialization, with the WebClient this operation is performed on the UI thread, impacting directly the performances of the user interface, but using the HttpWebRequest you can perform deserialization in the network call thread and then move the result to the UI leaving it free to run at the maximum performances during the operation.

Errors handling and Aborting a call

Exceptions handling with HttpWebRequest have the same limitation as the WebClient: you can only intercept exceptions coming from network issues but for application errors you have to rely on your own protocol built on top of the HTTP. This may be an error code returned in the body of the response, a special http header, but the HttpWebRequest will not understand it for you. For the other cases, like timeouts, network faults and so on, you have to rely on exceptions using one or more try-catch blocks.

You can get a number of exceptions by the methods but the most important is the WebException. With this exception you can read the exact reason that caused the fault in the communication. As an example, you can receive a value of RequestCanceled stating that at some point someone has called the Abort() method in the HttpWebRequest. This method causes the immediate cancellation of the request and the situation is handled raising a WebException. Now that we are aware of the various aspects of error handling and cancellation here is the complete code for a call:

public void GetFeed(Action<string> result, Action<Exception> fail)
{
    HttpWebRequest request = HttpWebRequest.CreateHttp("http://localhost:8000/HttpHandler.ashx");
 
    try
    {
        request.BeginGetResponse(
            a =>
            {
                try
                {
                    string data = this.ReadResponse(request, a);
                    Deployment.Current.Dispatcher.BeginInvoke(() => result(data));
                }
                catch (Exception ex)
                {
                    this.HandleErrors(ex, fail);
                }
            }
            , null);
    }
    catch (Exception ex)
    {
        this.HandleErrors(ex, fail);
    }
}
 
private string ReadResponse(HttpWebRequest request, IAsyncResult a)
{
    using (WebResponse response = request.EndGetResponse(a))
        using (StreamReader reader = new StreamReader(response.GetResponseStream()))
            return reader.ReadToEnd();
}
 
private void HandleErrors(Exception ex, Action<Exception> fail)
{
    WebException we = ex as WebException;
 
    if (we != null && we.Status == WebExceptionStatus.RequestCanceled)
        return;
 
    Deployment.Current.Dispatcher.BeginInvoke(() => fail(ex));
}

In this code you can see two try-catch blocks apparently nested each other. The outer block is responsible of catching the exceptions raised by the BeginGetResponse method, exceptions that are raised in the UI thread before to start the network call. Once the method spawned a new thread to begin the call, the outer block becomes inefficient so I have to put another try-catch inside the lambda expression to catch errors raised in the separated thread. In the HandleErrors method I check if the exception is a WebException and the status is RequestCanceled. In this case the exception is swallowed and it does not return to the caller, due to a user cancellation. In all the other cases the exception is presented to the fail lambda.

Download and Upload progress

Again, following the path of the previous article, there may be the need to display the progress of the network operation. While the WebClient notifies you an event with the global progress of the operation, the HttpWebRequest is not so smart but it requires you to handle directly the situation. The bad news is that you cannot handle the progress of upload because it is not implemented in Silverlight for Windows Phone.

First of all, to notify the progress you have to be aware of the size of the file you are moving. The operation will be cut into little pieces and, on the completion of each chunk, you can update the progress calculating the percentage with the bytes moved and the expected size. To accomplish this task you have to set the AllowReadStreamBuffering to false. Setting this property disables the normal behavior of the HttpWebRequest that usually uses a buffer in memory to optimize the network transfer. Disabling this buffer allows to directly read bytes from the network in small chunks and at each one calculate the remaining download size; something like this:

public void GetHugeFile(Action<string> result, Action<Exception> fail, Action<double> progress)
{
    HttpWebRequest request = HttpWebRequest.CreateHttp("http://www.slpg.org/syndication.axd");
 
    try
    {
        request.AllowReadStreamBuffering = false;
        request.BeginGetResponse(
            a =>
            {
                try
                {
                    using (WebResponse response = request.EndGetResponse(a))
                    {
                        int expected = (int)response.ContentLength;
 
                        using(StreamReader reader = new StreamReader(response.GetResponseStream()))
                        {
                            int read = 0;
                            StringBuilder data = new StringBuilder(expected);
                            char[] buffer = new char[1024];
 
                            while ((read = reader.Read(buffer, 0, buffer.Length)) != 0)
                            {
                                data.Append(new string(buffer, 0, read));
 
                                Deployment.Current.Dispatcher.BeginInvoke(() => progress(data.Length * 100.0 / expected));
                            }
 
                            Deployment.Current.Dispatcher.BeginInvoke(() => progress(100.0));
                            Deployment.Current.Dispatcher.BeginInvoke(() => result(data.ToString()));
                        }
                    }
                }
                catch (Exception ex)
                {
                    this.HandleErrors(ex, fail);
                }
            }
            , null);
    }
    catch (Exception ex)
    {
        this.HandleErrors(ex, fail);
    }
}

Again on Headers, Verbs, Credentials and Cookies

To complete the exploration of the HttpWebRequest class, it remain to discuss the ones I called "advanced topics". Speaking about headers, you have to be aware of the strict separation between WebRequest and WebResponse. If you need to access the headers you are sending to the server you have to use the Headers property on the HttpWebRequest instance, also with custom headers.

request.Headers["x-source-uri"] = http://www.silverlightplayground.org/;
 

HttpWebRequest has the same limitation on headers you can manipulate, as the WebClient has. As already said you can read about these limits in this page: http://msdn.microsoft.com/en-us/library/system.net.webheadercollection(v=VS.95).aspx. To access the response headers you have the same Headers collection on the WebResponse class.

In the example attached to this article, that I've extended adding the support for HttpWebRequest, you can see that the requests are made setting the Method property. This let you decide the Verb to send to the page and apply the same RESTful paradigm as I did with the WebClient.

public override void AddNames(IEnumerable<string> names, Action success, Action<Exception> fail)
{
    this.UploadNames(names, "POST", success, fail);
}
 
public override void DeleteNames(IEnumerable<string> names, Action success, Action<Exception> fail)
{
    this.UploadNames(names, "DELETE", success, fail);
}
 
private void UploadNames(IEnumerable<string> names, string method, Action success, Action<Exception> fail)
{
    HttpWebRequest request = HttpWebRequest.CreateHttp("http://localhost:8000/HttpHandler.ashx");
    request.CookieContainer = this.Cookies;
    request.Method = method;
 
    try
    {
        request.BeginGetRequestStream(
            a =>
            {
                try
                {
                    using (StreamWriter writer = new StreamWriter(request.EndGetRequestStream(a)))
                        writer.Write(this.Serialize(names));
 
                    request.BeginGetResponse(
                        b =>
                        {
                            try
                            {
                                string result = this.ReadResponse(request, b);
                                // handle here the result
                                Deployment.Current.Dispatcher.BeginInvoke(() => success());
                            }
                            catch (Exception ex)
                            {
                                this.HandleErrors(ex, fail);
                            }
                        }
                        , null);
                }
                catch (Exception ex)
                {
                    this.HandleErrors(ex, fail);
                }
            }, null);
    }
    catch (Exception ex)
    {
        this.HandleErrors(ex, fail);
    }
}

In the above sample I also demonstrated the use of the CookieContainer. This property is directly exposed by the HttpWebRequest class. Remember that differently from WebClient you can use an instance of HttpWebRequest only once, so it is important you share the same instance of the CookieContainer across different calls if you need to ensure all of them share the same cookies.

And finally if you need to present your credentials to the web server you can again rely on the Credentials property:

request.Credentials = new NetworkCredential("username", "p@ssw0rd");

Making a choice

Speaking for real, I will say that there are very few things that you can do with HttpWebRequest that you cannot do with WebClient. The different features are so subtle that to me the sole reason to decide to use one instead of the other is to deal with performances troubles that you can resolve using the manual thread marshaling in the HttpWebRequest. On the other side if you are not hit by performances problems, probably the WebClient is the better solution for its simplicity.


Subscribe

Comments

  • OasisLiveForever

    Re: Windows Phone 7 Data Access Strategies: HttpWebRequest


    posted by OasisLiveForever on Aug 26, 2011 08:02

    Hi,

    great post!

    Which is the default timeout for HttpWebRequest and is there any way to set a custom value for the timeout?

Add Comment

Login to comment:
  *      *       

From this series