Delen via


How to connect to an HTTP server using Windows.Web.Http.HttpClient (XAML)

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

Send a GET request to a web service and retrieve the response using the Windows.Web.Http.HttpClient class in the Windows.Web.Http namespace.

Classes in the Windows.Web.Http namespace provide a programming interface for modern HTTP client apps. The Windows.Web.Http namespace and the related Windows.Web.Http.Headers and Windows.Web.Http.Filters namespaces provide HTTP client components that allow users to make HTTP requests and receive HTTP responses from modern web services over HTTP.

This new API replaces the use of three different APIs with differing features that previously were needed for each language projection in Windows 8.

For basic request operations, the Windows.Web.Http.HttpClient API has a simple design to handle the most common tasks and provides sensible defaults for authentication (AUTH) that apply to most scenarios. For more complex HTTP operations, additional capabilities are included.

  • Methods for common verbs (DELETE, GET, PUT, and POST)

  • Support for common authentication settings and patterns

  • Access to Secure Sockets Layer (SSL) details on the transport

  • Ability to include customized filters in advanced apps

  • Ability to get, set, and delete cookies

  • HTTP Request progress info available on asynchronous methods

The Windows.Web.Http.HttpClient class is used to send and receive basic requests over HTTP. It provides the main class for sending HTTP requests and receiving HTTP responses from a resource identified by a URI. This class can be used to send a GET, PUT, POST, DELETE, and other requests to a web service. Each of these requests is sent as an asynchronous operation.

The Windows.Web.Http.HttpRequestMessage class represents an HTTP request message sent by Windows.Web.Http.HttpClient. The Windows.Web.Http.HttpResponseMessage class represents an HTTP response message received from an HTTP request. HTTP messages are defined in RFC 2616 by the IETF.

The Windows.Web.Http namespace represents HTTP content as the HTTP entity body and headers including cookies. HTTP content can be associated with an HTTP request or an HTTP response. The Windows.Web.Http namespace provides a number of different classes to represent HTTP content.

In this example, the HttpStringContent class is used to represent the HTTP response from an HTTP GET request as a string.

The Windows.Web.Http.Headers namespace supports creation of HTTP headers and cookies, which are then associated as properties with HttpRequestMessage and HttpResponseMessage objects.

Prerequisites

The following examples in this topic are provided in C# using the .NET Framework 4.5 and in C++. A basic understanding of HTTP requests as detailed in RFC 2616 is required.

It is also possible to make HTTP requests in an app using C++ and XAML using XML HTTP Extended Request (IXMLHTTPRequest2). For more information, see Connecting using XML HTTP Extended Request and IXMLHTTPRequest2.

To make HTTP requests in an app using JavaScript and HTML, see Connecting to web services (HTML).

Instructions

Create a new project

  1. Open Microsoft Visual Studio 2013 and select New Project from the File menu.
  2. In the list of templates, choose Visual C# or Visual C++.
  3. Under the section, choose Store apps.
  4. Under the section, select Universal Apps, Windows apps, or Windows Phone apps (depending on the platform you are targeting), and then select Blank Application.
  5. Name the application HttpClientGet and click OK.

Set capabilities to enable network access

  • Set network capabilities for your app to enable access to a private home or work network and to the Internet. For this app, you would need to enable network capabilities since the client is connecting to web services.

    The Internet (Client) capability is needed if the app needs to be able to connect as a client to a web services on the Internet. The Private Networks (Client & Server) capability is needed if the app needs to be able to connect as a client to web services on a home network or work network.

    Note  On Windows Phone, there is only one network capability (Internet (Client & Server) which enables all network access for the app.

     

    If an app wants to access the web service running on the same computer as the app, this requires loopback access. An app is normally not allowed loopback access for security reasons. However, apps developed and run in Visual Studio 2013 will automatically be registered as being exempt from the loopback restrictions when run in the debugger. For more information, see How to enable loopback and debug network isolation.

    For more information on network access, see How to configure network isolation capabilities.

    These steps are needed to set network capabilities for an app before it is deployed if it accesses a web service on the Internet or on a private or work network.

  1. Use Visual Studio 2013 to open the package.appxmanifest file.

  2. Select the Capabilities tab.

  3. To build the Windows version of the sample, select the Internet (Client) and Private Networks (Client & Server) capabilities.

    To build the Windows Phone version of the sample, select the Internet (Client & Server) capability.

  4. Save and close the manifest file.

Add XAML UI

  • We first define the app layout in XAML to specify the size and position of each object in the app. We complete the UI for the app by adding controls and content to display data.

    This sample uses simple XAML UI elements that include the following:

    • A horizontal StackPanel that contains a TextBlock for a label, a TextBox for the input URI address, and a Button used to start the asynchronous request.
    • A horizontal StackPanel that contains a TextBlock for a label and a TextBox for the current status. This is where status and error messages will be displayed.
    • A StackPanel that contains a TextBox where the output received from the web service is displayed. In this sample, the result of the HTTP GET operation is displayed as plain text containing HTML markup. We can change the output display from plain text to a web view by using a WebView instead of a TextBox.

    Open the cs or cpp folder depending on the language you are using to develop your app. Open the existing BlankPage.xaml file and rename the file to MainPage.xaml. For a Windows Store app, add the following UI elements to this file.

    Note  The XAML code below will need to be changed for a Windows Phone Store app.

     

    <Page
        x:Class="HttpClientGet.MainPage"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:HttpClientGet"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:controls="using:Windows.UI.Xaml.Controls"
        mc:Ignorable="d"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    
        <Grid>
            <StackPanel Orientation="Vertical">
                <TextBox x:Name="inputAddress" Text="https://www.contoso.com" FontSize="18" 
                         Height="17" Margin="10,50,10,10" />
                <TextBlock Text="Status:" FontSize="24" Height="28" Margin="10,20,0,0" />
                <TextBox x:Name="statusText" Text="Idle" FontSize="18" Height="Auto" 
                        TextWrapping="Wrap" Margin="10,10,10,10" />
                <TextBlock Text="Response:" FontSize="24" Height="28" Margin="10,10,10,10" />
                <TextBox x:Name="outputView" FontSize="18" Margin="10,10,10,10" TextWrapping="Wrap" 
                        Height="296" ScrollViewer.VerticalScrollBarVisibility="Visible"/>
                <Button Content="Start" Click="Start_Click" Margin="10,10,10,10" FontSize="24" Height="80" Width="380" />
            </StackPanel>
        </Grid>
    </Page>
    

Create the HttpClient

  • Create a Windows.Web.Http.HttpClient object and add a user-agent header.

    By default, no user-agent header is sent with the HTTP request to the web service by the HttpClient object. Some HTTP servers, including some Microsoft web servers, require that a user-agent header be included with the HTTP request sent from the client. The user-agent header is used by the HTTP server to determine how to format some HTTP pages so they render better on the client for different web browsers and form factors (mobile phones, for example). Some HTTP servers return an error if no user-agent header is present on the client request. We need to add a user-agent header to avoid these errors using classes in the Windows.Web.Http.Headers namespace. We add this header to the HttpClient.DefaultRequestHeaders property.

    For C#, open the cs folder and the MainPage.xaml.cs file and add the following code to the file.

    using System;
    using Windows.Web.Http;
    using Windows.Web.Http.Headers;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Navigation;
    
    namespace HttpClientGet
    {
        public sealed partial class MainPage : Page
        {
    
            private HttpClient httpClient;
            private HttpResponseMessage response;
    
            public MainPage()
            {
                this.InitializeComponent();
    
                this.NavigationCacheMode = NavigationCacheMode.Required;
    
                httpClient = new HttpClient();
    
                // Add a user-agent header
                var headers = httpClient.DefaultRequestHeaders;
    
                // HttpProductInfoHeaderValueCollection is a collection of 
                // HttpProductInfoHeaderValue items used for the user-agent header
    
                // The safe way to check a header value from the user is the TryParseAdd method
                // Since we know this header is okay, we use ParseAdd with will throw an exception
                // with a bad value 
                headers.UserAgent.ParseAdd("ie");
                headers.UserAgent.ParseAdd("Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)");
    }
    

    For C++, open the cpp folder and the MainPage.xaml.h file and add the following code to the file.

    //
    // MainPage.xaml.h
    // Declaration of the MainPage class.
    //
    
    #pragma once
    
    #include "MainPage.g.h"
    
    namespace HttpClientGet
    {
        /// <summary>
        /// An empty page that can be used on its own or navigated to within a Frame.
        /// </summary>
        public ref class MainPage sealed
        {
            public:
                MainPage();
    
            private:
                //  MainPage^ rootPage;
                Windows::Web::Http::HttpClient^ httpClient;
                Windows::Web::Http::HttpResponseMessage^ response;
    
                concurrency::cancellation_token_source cancellationTokenSource;
                void Start_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
    
                static Platform::String^ Trim(Platform::String^ s);
        };
    }
    

    For C++, open the cpp folder and then open the MainPage.xaml.cpp file and add the following code to the file.

    //
    // MainPage.xaml.cpp
    // Implementation of the MainPage class.
    //
    
    #include "pch.h"
    #include "MainPage.xaml.h"
    
    using namespace HttpClientGet;
    
    using namespace concurrency;
    using namespace Platform;
    using namespace Windows::Foundation;
    using namespace Windows::UI::Core;
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Navigation;
    using namespace Windows::Web::Http;
    using namespace Windows::Web::Http::Headers;
    
    // The Blank Page item template is documented at https://go.microsoft.com/fwlink/p/?LinkID=234238
    
    MainPage::MainPage()
    {
        InitializeComponent();
    
        httpClient = ref new HttpClient();
    
        // Add a user-agent header
        HttpRequestHeaderCollection ^headers = httpClient->DefaultRequestHeaders;
    
        // HttpProductInfoHeaderValueCollection is a collection of 
        // HttpProductInfoHeaderValue items used for the user-agent header
    
           // The safe way to check a header value from the user is the TryParseAdd method
        // Since we know this header is okay, we use Add which will throw an exception
        // with a bad value
        headers->UserAgent->ParseAdd("ie");
        headers->UserAgent->ParseAdd("Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)");
    }
    

Send the GET request and receive the response

  • When the Start button is clicked, we first check that the URI specified in the inputAddress is valid. Then we send the GET request using the URI and wait to receive the response from the HTTP server.

    Most of the work is done in the click handler for the Start Button. When this button is clicked, the text in the statusText and outputView UI elements is updated. We first check the input URI address to make sure that the user passed a valid URI address. If we have a valid URI from the user, then the app sends the HTTP GET request to the specified URI and waits for the HTTP response. If an error or exception occurs, the result is displayed in the statusText UI element. If no errors occur, the response from the web service is displayed in the outputView UI element as text.

    An exception is thrown when an invalid string for a URI is passed to the constructor for the Windows.Foundation.Uri object.

.NET 4.5 Framework: The System.Uri class is used instead of the Windows.Foundation.Uri class in C# and Visual Basic.

In C\# and Visual Basic, this error can be avoided by using the [**System.Uri**](https://msdn.microsoft.com/en-us/library/system.uri\(v=Win.10\)) class in the .NET 4.5 and one of the [**System.Uri.TryCreate**](https://msdn.microsoft.com/en-us/library/system.uri.trycreate\(v=Win.10\)) methods to test the string received from the app user before the URI is constructed. In C++, there is no method to try and parse a string to a URI. To catch this exception in that case, use a try/catch block around the code where the URI is constructed.

The sample also checks that the HTTP scheme in the URI is HTTP or HTTPS since these are the only schemes supported by the [**Windows.Web.Http.HttpClient**](https://msdn.microsoft.com/en-us/library/Dn298639).

Using the **await** keyword in C\# and Visual Basic, the code for sending the GET request and retrieving the response asynchronously is similar to the code we would use to complete this operation synchronously. You can use the **await** keyword only if the method is defined as **async**. You can use the **await** keyword with any Windows Runtime method that is asynchronous. This includes methods whose name ends in `Async`, as well as with any of your own methods which are defined using the **Async (Visual Basic)** or **async (C\#)** modifier.

In C++, the sample uses tasks and the **concurrency** namespace for the asynchronous methods.

Exceptions from network errors (loss of connectivity, connection failures, and HTTP server failures, for example) can happen at any time. These errors result in exceptions being thrown. If not handled by your app, an exception can cause your entire app to be terminated by the runtime. You must write code to handle exceptions when you call most asynchronous network methods. Sometimes when an exception occurs, a network method can be retried to try and resolve the problem. Other times, your app may need to plan to continue without network connectivity using previously cached data. For more information on handling network exceptions, see [Handling exceptions in network apps](https://msdn.microsoft.com/en-us/library/Dn263211).

The [**HttpResponse.EnsureSuccessStatusCode**](https://msdn.microsoft.com/en-us/library/Dn279636) method throws an exception if the web server returns an HTTP error status code, [**HttpResponse.StatusCode**](https://msdn.microsoft.com/en-us/library/Dn279645) not in the **Successful** range (200-299) for the request. We use a try/catch block for any exceptions and print out the exception message in the **statusText** UI element if an error occurs.

The [**HttpResponse.Content**](https://msdn.microsoft.com/en-us/library/Dn279634) property represents the content of the HTTP response. The [**HttpClient.GetAsync(Uri)**](https://msdn.microsoft.com/en-us/library/Dn298646) method reads the HTTP content to a string as an asynchronous operation. We replace any \<br\> tags in the returned HTML text as newlines for display purposes. If the method is successful, we display the [**HttpResponse.StatusCode**](https://msdn.microsoft.com/en-us/library/Dn279645) in the **statusText** UI element and **HttpResponse.Content** returned by the web service in the **outputView** UI element.

Open the **cs** or **cpp** folder depending on language you are using to develop your app. Open the *MainPage.xaml.cs* or *MainPage.xaml.cpp*file and add the following code to the file.

``` csharp
        private async void Start_Click(object sender, RoutedEventArgs e)
        {

            response = new HttpResponseMessage();
            outputView.Text = "";

            // The value of 'InputAddress' is set by the user and is therefore untrusted input. 
            // If we can't create a valid URI, 
            // We notify the user about the incorrect input.

            statusText.Text = "Test if URI is valid";

            Uri resourceUri;
            if (!Uri.TryCreate(inputAddress.Text.Trim(), UriKind.Absolute, out resourceUri))
            {
                statusText.Text = "Invalid URI, please re-enter a valid URI";
                return;
            }
            if (resourceUri.Scheme != "http" && resourceUri.Scheme != "https")
            {
                statusText.Text = "Only 'http' and 'https' schemes supported. Please re-enter URI";
                return;
            }

            string responseBodyAsText;
            statusText.Text = "Waiting for response ...";

            try
            {
                response = await httpClient.GetAsync(resourceUri);

                response.EnsureSuccessStatusCode();

                responseBodyAsText = await response.Content.ReadAsStringAsync();

            }
            catch (Exception ex)
            {
                // Need to convert int HResult to hex string
                statusText.Text = "Error = " + ex.HResult.ToString("X") +
                    "  Message: " + ex.Message;
                responseBodyAsText = "";
            }
            statusText.Text = response.StatusCode + " " + response.ReasonPhrase;

            // Format the HTTP response to display better
            responseBodyAsText = responseBodyAsText.Replace("<br>", Environment.NewLine);
            outputView.Text = responseBodyAsText;
        }
```

``` cpp
void MainPage::Start_Click(Object ^sender, RoutedEventArgs ^e)
{
    Uri^ resourceUri;
    response = ref new HttpResponseMessage();
    outputView->Text = "";

    // The value of 'InputAddress' is set by the user and is therefore untrusted input. 
    // If we can't create a valid URI, 
    // We notify the user about the incorrect input.

    statusText->Text = "Test if URI is valid.";

    String^ uriString = inputAddress->Text;

    try
    {
        resourceUri = ref new Uri(uriString);
    }
    catch (Exception ^ex)
    {
        statusText->Text = "Invalid URI, please re-enter a valid URI.";
        return;
    }

    if (resourceUri->SchemeName != "http" && resourceUri->SchemeName != "https")
    {
        statusText->Text = "Only 'http' and 'https' schemes supported. Please re-enter URI";
        return;
    }

    String^ responseBodyAsText;
    statusText->Text = "Waiting for response ...";

    create_task(httpClient->GetAsync(resourceUri)).then([=](HttpResponseMessage^ response)
    {
        response->EnsureSuccessStatusCode();
        statusText->Text = response->StatusCode.ToString() + " " + response->ReasonPhrase;

        return create_task(response->Content->ReadAsStringAsync());
    }).then([=](String^ responseBodyAsText){
        
// Format the HTTP response to display better
// responseBodyAsText = responseBodyAsText->Replace("<br>", Environment->NewLine); // Insert new lines
        outputView->Text = responseBodyAsText;
    }).then([=](task<void> prevTask)
    {
        try
        {
            prevTask.get();
        }
        catch (Exception ^ex)
        {
            statusText->Text = "Error = " + ex->HResult + "  Message: " + ex->Message;
            return;
        }
    });
}

String^ MainPage::Trim(String^ s)
{
    const char16* first = s->Begin();
    const char16* last = s->End();

    while (first != last && iswspace(*first))
    {
        ++first;
    }

    while (first != last && iswspace(last[-1]))
    {
        --last;
    }
    return ref new String(first, (int)(last - first));
}
```

[**Windows.Web.Http.HttpClient**](https://msdn.microsoft.com/en-us/library/Dn298639) uses [Windows Internet (WinInet)](https://msdn.microsoft.com/en-us/library/Aa385331) to send HTTP and web service requests and receive responses. The default timeout value used by WinInet for an HTTP connect operation is 60 seconds. If an HTTP server or web service is temporarily down or blocked by a firewall and the server doesn't or can't respond to the **Windows.Web.Http.HttpClient** request, WinInet waits the default 60 seconds before it returns an error which causes an exception to be thrown in the app. If the name query for an HTTP server name returns multiple IP addresses for the name, WinInet tries up to five IP addresses for the site each with a default timeout of 60 seconds before it fails. An app making an HTTP or web service request could wait several minutes making retries for multiple IP addresses, before an error is returned by WinInet and an exception is thrown. This behavior could appear to the user as if the app stopped working. The default timeout used by WinInet for send and receive operations after a connection has been established is 30 seconds.

To make the app more responsive and minimize these issues, an app could be enhanced by setting a timeout on the task that makes the [**Windows.Web.Http.HttpClient**](https://msdn.microsoft.com/en-us/library/Dn298639) operations so that operations fail sooner from the timeout rather than from the default [WinInet](https://msdn.microsoft.com/en-us/library/Aa385331) settings. For more information on how to setup a timeout, see [How to set timeouts on socket operations](jj710176\(v=win.10\).md).

Remarks

In this topic we reviewed how to use the Windows.Web.Http.HttpClient class to send a GET request to a web service and retrieve the response using the Windows.Web.Http.HttpResponseMessage and related classes in the Windows.Web.Http.Headers namespace.

Other resources

Connecting to web services

Handling exceptions in network apps

How to configure network capabilities

How to enable loopback and debug network isolation

How to set timeouts on socket operations

Reference

System.Uri

Windows.Foundation.Uri

Windows.Web.Http

Windows.Web.Http.Filters

Windows.Web.Http.Headers

Windows Internet (WinInet)

Samples

HttpClient Sample

Windows authentication broker sample