Dela via


How to connect to web services using the C++ REST SDK (Windows Store apps using C++ and 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]

Use the C++ REST SDK to connect to Bing Maps using C++ in a Windows Store app. For C++ scenarios in which you want to write reusable HTTP networking code that can run on desktop apps as well as earlier editions of Windows or Linux, you can use the C++ REST SDK. The C++ REST SDK includes full support for asynchronous programming models and has built-in JSON parsing functionality. The SDK is included in Microsoft Visual Studio. For more information, see C++ REST SDK.

For code that is tightly integrated to a specific app using C++ and XAML, we recommend that you use the Windows::Web::Http::HttpClient class.

This topic shows how to connect to the Bing Maps Location API REST endpoint, and retrieve location data as text in JSON format based on longitude and latitude inputs. For more information about how to add a Bing Map to an app written in C++, see Create a Windows Store app (for Windows 8.1).

Prerequisites

The following examples use C++ and XAML. For general help creating a Windows Runtime app using C++, see Create your first Windows Runtime app using C++.

To ensure your Windows Store app is network ready, you must set any network capabilities that are needed in the project Package.appxmanifest file. For more information, see How to set network capabilities.

Create an account with Bing Maps and obtain a key

****

  • If you haven't done so already, you need to first get a key that your app will use to access the service. Follow the instructions in Getting a Bing Maps Key.

Create a new C++ Windows Store blank app project

  1. Open Microsoft Visual Studio 2013 and select New Project from the File menu.
  2. In the list of templates, choose Visual C++.
  3. Under the section, choose Store apps.
  4. Under the section, select Windows apps and then select Blank Application.
  5. Name the application CPPRESTSDKSample and click OK.

Add a reference to the C++ REST SDK

  1. Click on Project | Properties and click the Add New Reference button.
  2. In the Add Reference dialog, expand Windows | Extensions in the left page, and then check the box next to C++ REST SDK in the middle pane.
  3. Click OK to exit the dialog.

Set capabilities to enable network access

Set network capabilities for your app to enable access 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 since the app needs to be able to connect as a client to a web service on the Internet.

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

 

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.

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

  2. Select the Capabilities tab.

  3. Select the Internet (Client) capability.

  4. Save and close the manifest file.

Create a wrapper class for connecting to Bing Maps

In this example we'll create a wrapper class and put it in a separate .h and .cpp file that has no dependencies on C++/CX or the Windows Runtime so that we can easily use it in other projects, including Windows desktop projects. This code is implemented as a class with a single static function, but it could just as easily be a free function or a non-static class. It could also be packaged in a Win32 DLL or a static lib although if that makes sense for your scenario. For more information, see Building apps and libraries (C++/CX).

  1. In Visual Studio, click on Project | Add new Item and then select Header File and call it "BingMapsWrapper.h". Follow the same steps to create a new .cpp file by the same name.

  2. Copy the following code into the .h file:

    #pragma once
    
    #include <string>
    #include <ppltasks.h>
    
    namespace BingMaps
    {
        class BingMapsWrapper
        {
        public:
            concurrency::task<std::wstring> GetLocationAsync(double latitude, double longitude);
        };
    }
    
  3. The code to access Bing maps consists of a single function, GetLocationAsync. Inside that function, the call to http_client::request initiates the asynchronous HTTP request, and when the response comes back it is passed to a continuation where the app has a chance to examine the status code. The response body, a JSON-formatted string, is passed to a second continuation that parses it and concatenates the elements of interest into a string which is then returned to the caller.

    Copy this code into the BingMapsWrapper.cpp file:

    #include "pch.h"
    #include "BingMapsWrapper.h"
    #include <ppltasks.h>
    #include "http_client.h"
    
    using namespace std;
    using namespace concurrency;
    using namespace BingMaps;
    
    using namespace web;
    using namespace web::json;
    using namespace web::http;
    using namespace web::http::client;
    
    task<wstring> BingMapsWrapper::GetLocationAsync(double latitude, double longitude)
    {
        wstring BingServiceKey = L"INSERT_YOUR_KEY_HERE";
        uri_builder uri(L"http://dev.virtualearth.net/REST/v1/Locations/");
        wostringstream fragment;
        fragment << latitude << L',' << longitude;
        uri.append_path(fragment.str(), true);
        uri.append_query(L"o", L"json");
        uri.append_query(L"key", BingServiceKey.c_str());
    
        http_client client(uri.to_uri());
        return client.request(methods::GET).then([](http_response response) -> task<utility::string_t>
        {
            unsigned long status = response.status_code();
            utility::string_t retval;
    
            // TODO In a real-world app, you should handle connection errors here.
            return response.extract_string();
    
        }).then([this, latitude, longitude](utility::string_t responseBody)
        {
            wstring addressString;
            json::value v;
    
            try
            {
                v = json::value::parse(responseBody);
            }
    
            catch (const json::json_exception&)
            {
                wostringstream ss;
                ss << L"BingLocationService: Failed to parse resource set for (" << latitude << L',' << longitude << ')';
                return addressString.append(ss.str());
            }
    
            const json::value& resourceSets = v[L"resourceSets"];
            int32_t estimatedTotal = resourceSets[0][L"estimatedTotal"].as_integer();
    
            if (estimatedTotal == 0)
            {
                wostringstream ss;
                ss << L"BingLocationService: request for (" << latitude << L',' << longitude << L") returned 0 resources";
                return addressString.append(ss.str());
            }
    
            const json::value& resources = resourceSets[0][L"resources"];
    
            // For more fine-grained control over the parsing, enclose each
            // attempt to read a json value in its own try-catch block.
            try
            {
                auto address = resources[0][L"address"];
                if (address[L"adminDistrict"].is_string())
                {
                    addressString.append(address[L"adminDistrict"].as_string());
                    addressString.append(L", ");
                }
    
                if (address[L"countryRegion"].is_string())
                {
                    addressString.append(address[L"countryRegion"].as_string());
                    addressString.append(L", ");
                }
    
                if (address[L"locality"].is_string())
                {
                    addressString.append(address[L"locality"].as_string());
                }
            }
    
            catch (const json::json_exception& e)
            {
                // A field was not in the payload.
                auto t = e.what();
            }
            return addressString;
        }).then([](task<wstring> result)
        {
            try
            {
                result.get(); // trap exceptions in the task chain           
            }
            catch (const exception& e)
            {
                auto a = e.what();
            }
            return result;
        });
    }
    

Create the user interface

****

  1. The user interface consists of two TextBox controls that enable you to enter longitude and latitude values, a TextBlock that displays the location information returned by Bing, and a Button to start the operation. Add the following XAML StackPanel to the grid in MainPage.xaml:

     <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" HorizontalAlignment="Right" Width="1366">
        <TextBlock x:Name="TitleText" HorizontalAlignment="Left" Margin="10,47,0,0" 
            TextWrapping="Wrap" Text="Connect to Bing Maps Web Service with the C++ REST SDK" 
            VerticalAlignment="Top" Height="52" Width="1356" FontSize="36"/>
        <StackPanel HorizontalAlignment="Left" Height="204" Margin="26,117,0,0" VerticalAlignment="Top" Width="922">
            <StackPanel Orientation="Horizontal">
                <TextBlock Padding="0,0,10,0" FontSize="14" Text="Enter a valid longitude:"></TextBlock>
                    <TextBox x:Name="Longitude"  TextWrapping="Wrap" Text="-122.125542" Height="28.519"  
                             UseLayoutRounding="False" Width="149.021" d:LayoutRounding="Auto"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <TextBlock Padding="0,0,10,0" FontSize="14" Text="Enter a valid latitude:" Width="152"></TextBlock>
                    <TextBox x:Name="Latitude" TextWrapping="Wrap" Text="47.642857" Height="28" Width="149"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal" Margin="0,20,20,20">
                <TextBlock Padding="0,10,10,10" FontSize="14" Text="Bing Maps Response:"></TextBlock>
                    <TextBlock x:Name="BingMapsResponse"  FontSize="14" HorizontalAlignment="Left" TextWrapping="Wrap"  
                    VerticalAlignment="Top" Height="40" Width="744"/>
                </StackPanel>
        <Button x:Name="LocationButton" Content="Get Location" Height="50" Width="150" Click="Button_Click"></Button>
        </StackPanel>
    
        </Grid>
    
  2. Add the button handler for LocationButton. An easy way to do this is to delete the existing handler, then start to type it in again, and allow the smart tag to stub out the function for you in MainPage.xaml.h.

Connect to Bing Maps Location API and parse the results

The real work in this sample is all done in the GetLocationAsync method. In this method, we first create a URI to send to the web service that includes the longitude and latitude input values and a query string that specifies that we want the response formatted as JSON and provides the unique key. This URI is used to initialize a new http_client object, which represents an HTTP session. This class is defined in http_client.h, one of the headers in the C++ REST SDK. The request method starts an asynchronous operation that performs the actions defined inside the lambda expression. In this case a GET operation is requested. When the operation completes, the response from the server is returned and passed to the .then method, which is called a continuation. In this continuation we parse the JSON text and write the results into the appropriate TextBlock in our user interface. We use the task_options object to specify that we need to run the continuation on the UI thread. This is necessary because we are writing directly into the XAML TextBlock control.

  1. Copy this code into MainPage.xaml.h:

    //
    // MainPage.xaml.h
    // Declaration of the MainPage class.
    //
    
    #pragma once
    
    #include "MainPage.g.h"
    
    
    namespace CPPRESTSDKSample
    {
          /// <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:
                void GetLocation(float latitude, float longitude);      
                void Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
        };
    }
    
  2. The user interface code is fairly simple. We just call concurrency::create_task to create an IAsyncOperation and then convert the result from a std::wstring to a Platform:String, which we then insert into the appropriate text block.

    Replace all the contents of MainPage.xaml.cpp with this code:

    //
    // MainPage.xaml.cpp
    // Implementation of the MainPage class.
    //
    
    #include "pch.h"
    #include "MainPage.xaml.h"
    #include "http_client.h"
    #include <memory>
    #include <assert.h>
    #include <sstream>
    #include "BingMapsWrapper.h"
    
    using namespace CPPRESTSDKSample;
    using namespace concurrency;
    using namespace web;
    using namespace web::json;
    using namespace web::http;
    using namespace web::http::client;
    
    using namespace std;
    using namespace BingMaps;
    
    using namespace Platform;
    using namespace Windows::Foundation;
    using namespace Windows::Foundation::Collections;
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Controls;
    using namespace Windows::UI::Xaml::Controls::Primitives;
    using namespace Windows::UI::Xaml::Data;
    using namespace Windows::UI::Xaml::Input;
    using namespace Windows::UI::Xaml::Media;
    using namespace Windows::UI::Xaml::Navigation;
    
    // The Blank Page item template is documented at https://go.microsoft.com/fwlink/p/?LinkID=234238
    
    MainPage::MainPage()
    {
        InitializeComponent();
    }
    
    
    void MainPage::GetLocation(float latitude, float longitude)
    {
        BingMapsWrapper wrapper;
        task_options options;
        options.set_continuation_context(task_continuation_context::use_current());
    
        create_task(wrapper.GetLocationAsync(latitude, longitude)).then
            ([this](wstring result)
        {
            BingMapsResponse->Text = ref new String(result.c_str());
        }, options);
    }
    
    
    void CPPRESTSDKSample::MainPage::Button_Click(Object^ sender, RoutedEventArgs^ e)
    {
        wstringstream converter;
        float longitude = 0;
        float latitude = 0;
    
        converter.precision(12);
        converter.fill('0');
        converter.setf(std::ios::fixed, std::ios::floatfield);
    
        converter << this->Longitude->Text->Data();
        converter >> longitude;
    
        converter.clear();
        converter << this->Latitude->Text->Data();
        converter >> latitude;
    
        GetLocation(latitude, longitude);
    }
    

The raw JSON response

The snippet below shows the complete response that comes back from Bing Maps web service in response to our HTTP request.

{
   "authenticationResultCode": "ValidCredentials",
   "brandLogoUri": "http://dev.virtualearth.net/Branding/logo_powered_by.png",
   "copyright": "Copyright © 2013 Microsoft and its suppliers. All rights reserved. This API cannot be accessed and the content and any results may not be used, reproduced or transmitted in any manner without express written permission from Microsoft Corporation.",
   "resourceSets": [
      {
         "estimatedTotal": 1,
         "resources": [
            {
               "__type": "Location:https://schemas.microsoft.com/search/local/ws/rest/v1",
               "bbox": [
                  47.636272,
                  -122.13744,
                  47.643997,
                  -122.12216
               ],
               "name": "Microsoft Way, Redmond, WA 98052",
               "point": {
                  "type": "Point",
                  "coordinates": [
                     47.640137,
                     -122.12981
                  ]
               },
               "address": {
                  "addressLine": "Microsoft Way",
                  "adminDistrict": "WA",
                  "adminDistrict2": "King Co.",
                  "countryRegion": "United States",
                  "formattedAddress": "Microsoft Way, Redmond, WA 98052",
                  "locality": "Redmond",
                  "postalCode": "98052"
               },
               "confidence": "Medium",
               "entityType": "Address",
               "geocodePoints": [
                  {
                     "type": "Point",
                     "coordinates": [
                        47.640137,
                        -122.12981
                     ],
                     "calculationMethod": "Interpolation",
                     "usageTypes": [
                        "Display",
                        "Route"
                     ]
                  }
               ],
               "matchCodes": [
                  "Good"
               ]
            }
         ]
      }
   ],
   "statusCode": 200,
   "statusDescription": "OK",
   "traceId": "0a734b03d01b483b9e3b80216e831cac|BAYM000274|02.00.161.1800|BY2MSNVM000002, BY2MSNVM001076"
}

Other resources

Connecting to web services

Handling exceptions in network apps

How to configure network capabilities

How to enable loopback and debug network isolation

Reference

C++ REST SDK