HttpWebRequest Fundamentals - Windows Phone Services Consumption - Part 2
This is the second part of our "Consuming Services with Windows Phone" series. Previously we covered using WebClient to pull raw HTML from the web. WebClient isn't the most optimal solution for all things webbie, so let's explore the daddy of WebClient, HttpWebRequest.
HttpWebRequest is a lower-level abstraction of the http protocol than WebClient. WebClient actually wraps HttpWebRequest. HttpWebRequest allows for more direct access of HTTP aspects which WebClient does not. Things such as the Accept header, Content Length and Type, direct access to Cookies and the User Agent.
WebRequest also returns on a background thread, allowing a program to parse and process results as they are returned without crunching down the main UI. When a program wishes to update the main UI in a callback from a HttpWebRequest, the program must use the Dispatcher to process user interface updates, since the WebRequest callback is on the background thread.
If you wish to explore the basics of a HttpWebRequest for scraping a website, walk through the steps below.
Step1: Create a new Windows Phone Portrait Project
Step2: Modify MainPage.xaml, change the ContentPanel from a Grid to StackPanel, and make it match what's below.
<StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Button Content="Call HttpWebRequest" Width="250" Click="Button_Click" />
<TextBlock Text="HttpWebRequest Target:" Style="{StaticResource PhoneTextNormalStyle}" Foreground="{StaticResource PhoneAccentBrush}" />
<TextBlock Text="https://msdn.microsoft.com" TextWrapping="Wrap" Margin="20,0,0,0" x:Name="TextBlockTargetUri" />
<TextBlock Text="Elapsed Time:" Style="{StaticResource PhoneTextNormalStyle}" Foreground="{StaticResource PhoneAccentBrush}" />
<TextBlock Text="tbd" TextWrapping="Wrap" Margin="20,0,0,0" x:Name="TextBlockElapsedTime" />
<TextBlock Text="HttpWebRequest Call Results:" Style="{StaticResource PhoneTextNormalStyle}" Foreground="{StaticResource PhoneAccentBrush}" />
<TextBlock x:Name="TextBlockResults" Text="results go here" Margin="20,0,0,0" TextWrapping="Wrap"/>
</StackPanel>
Next the code. Match up the steps given to the code block below.
Step3: Wire up the Button_Click handler. In the handler we setup a HttpWebRequest against our target Uri, then initiate the request with a callback targeted towards our "ReadRequestCallback" handler.
Step4: Code up our handler for the WebRequestCallback. First we'll create a new request instance. Then initialize an HttpWebResponse we can use to pull the results down from the web. After that we'll use a StreamReader to read the data. Once the data is down, since HttpWebRequest returns on a non-UI thread, we just use Dispatcher.Update to show the data in the UI. Run the program and you should see results similar to what's above.
public partial class HttpWebRequestPage : PhoneApplicationPage
{
public HttpWebRequestPage()
{
InitializeComponent();
}
// STEP3 STEP3 STEP3
private void Button_Click(object sender, RoutedEventArgs e)
{
System.Uri targetUri = new System.Uri(TextBlockTargetUri.Text);
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(targetUri);
request.BeginGetResponse(new AsyncCallback(ReadWebRequestCallback), request);
}
// STEP4 STEP4 STEP4
private void ReadWebRequestCallback(IAsyncResult callbackResult)
{
HttpWebRequest myRequest = (HttpWebRequest)callbackResult.AsyncState;
HttpWebResponse myResponse = (HttpWebResponse)myRequest.EndGetResponse(callbackResult);
using (StreamReader httpwebStreamReader = new StreamReader(myResponse.GetResponseStream()))
{
string results = httpwebStreamReader.ReadToEnd();
//TextBlockResults.Text = results; //-- on another thread!
Dispatcher.BeginInvoke(() => TextBlockResults.Text = results);
}
myResponse.Close();
}
}
Try this. Comment out the Dispatcher.BeginInvoke line and uncomment the TextBlockResults.Text line. Run the program. Now the background thread the callback is running on is trying to update the UI directly. Directly hitting the UI from a background thread results in an System.UnauthorizedAccessException: Invalid cross-thread access. Something to watch out for, definitely!
All in all, HttpWebRequest provides a greater flexibility than WebClient by being able to address http headers more directly. Since the thread returns on a non-UI thread, there's also a greater opportunity to boost performance and give a better user experience. The pattern isn't as simple as WebClient, but its not too onerous either.
I'll have another post up soon with some service consumption examples against some real world endpoints. See you then.
Consuming Windows Phone Services Series
Part 1 - Web Client Basics - https://blogs.msdn.com/b/devfish/archive/2011/04/06/webclient-windows-phone-services-consumption-part-1.aspx
Part 2 - HttpWebRequest Fundamentals - https://blogs.msdn.com/b/devfish/archive/2011/04/07/httpwebrequest-fundamentals-windows-phone-services-consumption-part-2.aspx
Part 3 - Parsing REST based XML Data - Part A - Single Result - https://blogs.msdn.com/b/devfish/archive/2011/05/05/consuming-geocode-yhoo-api-via-rest-windows-phone-services-consumption-part-3.aspx
Part 4 - Parsing REST based XML Data - Part B - Multiple Results - https://blogs.msdn.com/b/devfish/archive/2011/05/10/consuming-geocode-yhoo-api-via-rest-dealing-with-multiple-results-part-4.aspx
Comments
Anonymous
April 10, 2011
good stuff Joe. keep them coming.Anonymous
January 01, 2012
Hi Joe, thank you for an excellent article. All the example in HttpWebRequest article on msdn are wrong. Finally I saw a simple and working example on yours.Anonymous
January 31, 2013
Hi nice tutorial it worked for me when I am using get request. When I tried to do http post method it throws an error "The remote server returned an error : Not found" Can you help me in this regards can you post the code for http post method. By the way I am working on Windows phone 8 simulator.Anonymous
February 01, 2013
@bhanushali - send me an email at jhealy@microsoft.com - lets touch base on this....Anonymous
October 03, 2013
to: Bhanushali Ashwin N. "The remote server returned an error : Not found" Make sure that there is a internet connection in your emulator.