C++

Verwenden des C++-REST-SDK in Windows Store-Apps

Sridhar Poduri

In meinem vorherigen Artikel (msdn.microsoft.com/magazine/dn342869) habe ich das C++-REST-SDK vorgestellt und seine Verwendung in Win32/MFC-Anwendungen erläutert. In diesem Artikel erörtere ich, wie das C++-REST-SDK in Windows Store-Apps integriert werden kann. Als ursprüngliches Ziel hatte ich mir u. a. vorgenommen, bei der Verwendung des C++-REST-SDK und der OAuth-Authentifizierungsklasse so viel Standard-C++ wie möglich zu verwenden und nur bei Bedarf eine Verknüpfung mit plattformspezifischen APIs herzustellen. Es folgt eine kurze Zusammenfassung des vorherigen Artikels.

  1. Der in der OAuth-Authentifizierungsklasse verwendete Code nutzt C++-Standardtypen und keine Windows-spezifischen Typen.
  2. Der Code, der zum Senden von Webanforderungen an den Dropbox-REST-Dienst verwendet wird, nutzt die Typen des C++-REST-SDK.
  3. Der einzige plattformspezifische Code ist die Funktion, mit der Internet Explorer gestartet und die Anwendungsauthentifizierung und -genehmigung auf dem Dropbox-Apps-Konsolenportal abgeschlossen wird.

Ich habe mir dieselben Ziele für meine Windows Store-App gesetzt, um die Authentifizierung zu unterstützen und eine Datei auf Dropbox hochzuladen. Ich habe mich bemüht, so viel portablen C++-Code wie möglich zu verwenden und nur bei Bedarf eine Verknüpfung mit Windows-Runtime (WinRT) herzustellen. Die herunterzuladenden Beispielcodes für beide Artikel finden Sie unter msdn.microsoft.com/en-us/magazine/msdnmag0813.

Probleme mit der Win32-Lösung

Einer der größten Nachteile der vorherigen Win32-Anwendung bestand darin, dass zum Abschließen des OAuth-Autorisierungsvorgangs eine externe Anwendung gestartet werden musste. Dies bedeutete, dass ich Internet Explorer starten musste (Sie können auch Ihren bevorzugten Browser verwenden), mich mit meinen Anmeldeinformationen bei Dropbox anmelden und dann den Workflow abschließen musste. Dies wird in den Abbildungen 1 und 2 dargestellt.

Logging in to Dropbox Using My Credentials Before Authorizing Application AccessAbbildung 1: Anmelden bei Dropbox mit meinen Anmeldeinformationen vor der Autorisierung des Zugriffs auf die Anwendung

Successful Authorization for My Application on the Dropbox PortalAbbildung 2: Erfolgreiche Autorisierung für meine Anwendung im Dropbox-Portal

Wie Sie sehen, nimmt das Starten einer externen Anwendung und das Auffordern der Benutzer, den Workflow über die externe Anwendung abzuschließen, den Fokus von meiner App. Als Entwickler verfüge ich auch nicht über Standardmechanismen, mithilfe derer meine App bei Abschluss des Workflows benachrichtigt werden kann. Da mein Schwerpunkt auf asynchroner Programmierung liegt und das C++-REST-SDK so konzipiert ist, dass asynchrone aufgabenbasierte Programmierung unterstützt wird, ist es für mich wirklich ein Ärgernis, ein externes Programm starten zu müssen. Ich habe diese Problematik mithilfe von Named Pipes, im Speicher abgebildeten Dateien usw. ausführlich untersucht. Bei all diesen Methoden muss jedoch eine andere Anwendung geschrieben werden, um eine Instanz eines Websteuerelements zu hosten. Anschließend muss der erfolgreiche Wert über eine Named Pipe, freigegebenen Speicher oder eine im Speicher abgebildete Datei zurückgeschrieben werden. Ich habe mich letztendlich entschieden, den Browser zum Ausführen der Aufgabe zu verwenden, da ich keine andere Anwendung schreiben wollte, die das Websteuerelement einschließt.

Integrieren in Windows-Runtime

Als ich damit begann, meine App zur Unterstützung von Windows-Runtime zu entwickeln, habe ich einige Möglichkeiten in Erwägung gezogen. Ich führe diese hier kurz auf und erläuterte dann die gewählte Methode ausführlicher:

  1. Verwenden der Protokollaktivierung und Starten des richtigen Prozesses durch das System, um das Protokoll durch Aufrufen der Windows::System::Launcher::LaunchUriAsync-Funktion zu verarbeiten. Dies bedeutet, dass der Standardbrowser für einen HTTPS-basierten URI vom Betriebssystem aufgerufen wird. Dieser Vorgang verhält sich ähnlich wie der Start von Internet Explorer im Win32-Beispiel. Es gibt jedoch zwei Nachteile: Meine Windows Store-App wird in den Hintergrund verschoben und der Standardbrowser im Vollbildmodus geöffnet. Im schlimmsten Fall würde meine App bis zum Abschluss des Workflows durch den Benutzer angehalten werden. Das ist auf keinen Fall akzeptabel.
  2. Integrieren des WebView-Steuerelements in meiner App. Mit dem XAML-WebView-Steuerelement kann ich die gesamte Workflownavigation in den Kontext meiner App integrieren. Theoretisch kann ich auch benachrichtigt werden, wenn der Prozess abgeschlossen wurde, indem ich das window.external.notify-Ereignis überwache, das vom WebView-Steuerelement ausgelöst wird. In der Praxis wird das Ereignis jedoch nur ausgelöst, wenn die Website das Benachrichtigungsereignis auslöst. In meinen Fall löst die Dropbox-Seite, auf der der Autorisierungsvorgang abgeschlossen wird, das Ereignis nicht aus. Wie schade!
  3. Verwenden der WebAuthenticationBroker-Klasse in meiner App. Während ich mich ausführlich mit Windows-Runtime beschäftigt habe, bin ich zufällig auf die WebAuthenticationBroker-Klasse gestoßen. Es hatte den Anschein, als ob diese Klasse mir beim Abschließen des Autorisierungsvorgangs behilflich sein könnte, und nach Erstellung einiger weniger Codezeilen konnte ich die komplette Funktion tatsächlich ausführen. Bevor ich den Code erläutere, möchte ich die WebAuthenticationBroker-Klasse ausführlicher erklären.

Die WebAuthenticationBroker-Klasse

In einer Welt vernetzter Apps ist die Aufforderung zur Eingabe von Benutzeranmeldeinformationen über einen vertrauenswürdigen und sicheren Mechanismus von großer Bedeutung, um die Zustimmung des Benutzers für eine App zu gewinnen. Niemand möchte der Entwickler sein, dessen App Benutzeranmeldeinformationen preisgibt oder das Ziel eines Ausspähangriffs ist, mit dem Zweck des Missbrauchs von Benutzerdaten. Windows-Runtime enthält eine Reihe von APIs und erforderlichen Technologien, die es den Entwicklern ermöglichen, Benutzeranmeldeinformationen sicher und vertrauenswürdig zu suchen. Die WebAuthenticationBroker-Klasse ist eines der Tools, mit denen Windows Store-Apps internetbasierte Authentifizierungs- und Autorisierungsprotokolle wie OAuth und OpenID verwenden können. Wie funktioniert dies also im meiner Dropbox-Beispiel-App? Die folgenden Schritte werden ausgeführt:

  • Ich sende eine erste asynchrone Anforderung an Dropbox, und ich erhalte von Dropbox ein Token und ein Tokengeheimnis für meine App zurück. Diese erste Anforderung erfolgt über die oAuthLoginAsync-Funktion.
  • Sobald die oAuthLoginAsync-Funktion zurückgegeben wurde, setze ich die Sequenz fort, indem ich den URI erstelle, mit dem der Autorisierungsvorgang beginnen soll. In meinem Beispiel habe ich den ursprünglichen URI als const-Zeichenfolge definiert:
const std::wstring DropBoxAuthorizeURI = 
  L"https://www.dropbox.com/1/oauth/authorize?oauth_token=";
  • Dann habe ich den URI der HTTP-Anforderung erstellt, indem ich das von Dropbox zurückgegebene Token angefügt habe.
  • In einem zusätzlichen Schritt habe ich den Parameter „callback URI“ durch Aufrufen der WebAuthenticationBroker::GetCurrentApplicationCallbackUri-Funktion erstellt. Beachten Sie, dass ich den Parameter „callback URI“ nicht in meiner Desktopanwendung verwendet habe, da der Rückrufparameter optional ist und ich zur Durchführung der Autorisierungsaufgabe auf Internet Explorer angewiesen bin.
  • Meine Anforderungszeichenfolge ist nun bereit, und ich kann meine Anforderung übermitteln. Anstatt die http_client-Klasse des C++-REST-SDK oder die IHttpWebRequest2-Schnittstelle zum Aufrufen von Webdiensten zu verwenden, rufe ich die WebAuthenticationBroker::AuthenticateAsync-Funktion auf.
  • Die WebAuthenticationBroker::AuthenticateAsync-Funktion akzeptiert zwei Parameter: eine WebAuthenticationOptions-Enumeration und einen URI. Eine überladene Instanz der gleichen Funktion akzeptiert die WebAuthenticationOptions-Enumeration und zwei URIs, jeweils einen für den Anfangs-URI, mit dem der Authentifizierungsprozess startet, und den End-URI, mit dem der Authentifizierungsprozess beendet wird.
  • Ich verwende die erste Version der AuthenticateAsync-Funktion und übergebe einen None-Wert für die WebAuthenticationOptions-Enumeration. Für die zwei URIs übergebe ich den für meine Webanforderung entwickelten URI.
  • Die WebAuthenticationBroker-Klasse ist zwischen meiner App und dem System angesiedelt. Zu dem Zeitpunkt, zu dem ich die AuthenticateAsync-Funktion aufrufe, erstellt der Broker ein modales Dialogfeld des Systems, das ein modales Dialogfeld für meine App ist.
  • Der Broker fügt ein Webhostfenster an das von ihm erstellte modale Dialogfeld an.
  • Der Broker wählt dann einen dedizierten App-Containerprozess aus, der von dem App-Container getrennt ist, in dem meine App ausgeführt wird. Er löscht auch alle permanenten Daten aus meiner App.
  • Der Broker startet anschließend den Authentifizierungsprozess in diesem neu ausgewählten App-Container und navigiert zum URI, wie in der AuthenticateAsync-Funktion festgelegt.
  • Während Benutzer mit den Webseiten interagieren, überprüft der Broker jede URL auf den von ihm festgelegten Rückruf-URI.
  • Sobald eine Übereinstimmung gefunden wird, beendet der Webhost die Navigation und sendet ein Signal an den Broker. Der Broker entfernt das Dialogfeld, löscht alle permanenten, vom Webhost erstellten Cookies aus dem App-Container und gibt die Protokolldaten zurück an die App.

In Abbildung 3 ist das modale Dialogfeld der Web­AuthenticationBroker-Klasse in meiner Beispiel-Dropbox-App dargestellt, nachdem der Webhost zum ursprünglichen URI navigiert hat. Da Dropbox davon ausgeht, dass Benutzer sich anmelden, bevor die Autorisierungsseite angezeigt werden kann, leitet der Webhost die Navigation an die Dropbox-Anmeldeseite um.

The Sign-in Page of Dropbox as Displayed in the Modal Dialog
Abbildung 3: Die Anmeldeseite von Dropbox, wie sie im modalen Dialogfeld angezeigt wird

Sobald sich ein Benutzer bei Dropbox angemeldet hat, navigiert der Webhost zum aktuellen Autorisierungs-URI. Dies ist in Abbildung 4 dargestellt. Aus Abbildung 3 und Abbildung 4 ist ersichtlich, dass das Dialogfeld meinem App-URI überlagert wird. Die Benutzeroberfläche bleibt ebenfalls konsistent, ungeachtet der Quell-App, die die WebAuthenticationBroker::Authen­ticateAsync-Methode aufruft. Da die gesamte Erfahrung unverändert bleibt, können Benutzer Anmeldeinformationen angeben, ohne sich Gedanken machen zu müssen, dass Apps, die diese Informationen verarbeiten, solche Daten unbeabsichtigt preisgeben könnten.

User Consent Being Asked for Application Authorization from Dropbox
Abbildung 4: Einzuholende Genehmigung des Benutzers von Dropbox für die Anwendungsautorisierung

Etwas Wichtiges, was ich nicht erwähnt habe, ist die Tatsache, dass die WebAuthenticationBroker::AuthenticateAsync-Funktion aus dem UI-Thread aufgerufen werden muss. Alle Webanforderungen des C++-REST-SDK erfolgen in einem Hintergrundthread, ich kann die Benutzeroberfläche aus diesem Thread jedoch nicht anzeigen. Stattdessen verwende ich das System-Dispatcher-Objekt und rufe seine Memberfunktion „RunAsync“ auf, um die modale Benutzeroberfläche anzuzeigen, wie in Abbildung 5 dargestellt.

Abbildung 5: Verwenden des System-Dispatcher-Objekts zum Anzeigen der modalen Benutzeroberfläche

auto action = m_dispatcher->RunAsync(
  Windows::UI::Core::CoreDispatcherPriority::Normal,
  ref new Windows::UI::Core::DispatchedHandler([this]()
  {
    auto beginUri = ref new Uri(ref new String(m_authurl.c_str()));
    task<WebAuthenticationResult^> authTask(WebAuthenticationBroker::
      AuthenticateAsync(WebAuthenticationOptions::None, beginUri));
      authTask.then([this](WebAuthenticationResult^ result)
      {
        String^ statusString;
        switch(result->ResponseStatus)
        {
          case WebAuthenticationStatus::Success:
          {
            auto actionEnable = m_dispatcher->RunAsync(
              Windows::UI::Core::CoreDispatcherPriority::Normal,
              ref new Windows::UI::Core::DispatchedHandler([this]()
              {
                UploadFileBtn->IsEnabled = true;
              }));
          }
        }
      });
}));

Nachdem der Autorisierungsvorgang abgeschlossen wurde, führe ich das Dispatcher-Objekt erneut aus, um die Schaltfläche „Datei hochladen“ für meine Hauptbenutzeroberfläche zu aktivieren. Diese Schaltfläche bleibt deaktiviert, bis meine Benutzer meine App zum Zugriff auf Dropbox authentifiziert und autorisiert haben.

Verketten von asynchronen Webanforderungen

Die Kombination all dieser Technologien ist nun ein Kinderspiel. Bei all den Funktionen, die nicht mit Windows-Runtime verknüpft werden, habe ich erneut denselben Code meiner Desktopanwendung verwendet. Es gibt keine wesentlichen Codeänderungen, außer diese: die Entscheidung, das WinRT StorageFile-Objekt im Vergleich zu einem C++-E/A-Datenstrom in der UploadFileToDropboxAsync-Funktion zu verwenden.

Beim Schreiben von Apps für den Windows Store gibt es einige Einschränkungen, mit denen Sie sich abfinden müssen. Eine Einschränkung besteht darin, dass WinRT StorageFile-Objekte statt C++-Datenströme zum Lesen und Schreiben von Daten aus Dateien verwendet werden müssen. Bei der Entwicklung einer Windows Store-App und der Verwendung des C++-REST-SDK setzen alle Vorgänge in Verbindung mit Dateien voraus, dass ein Entwickler ein StorageFile-Objekt anstatt eines C++-Datenstromobjekts weiterleitet. Angesichts dieser einen kleinen Änderung kann ich all meinen Standard-C++-Code neu verwenden, der den OAuth- und Dropbox-Autorisierungscode in meiner Windows Store-Beispiel-App unterstützt.

Das ist mein Pseudocodefluss (die einzelnen Funktionen erläutere ich im Anschluss an den Pseudocode):

On clicking the SignIn Button
  Call oAuthLoginAsync function
    Then call WebAuthenticationBroker::AuthenticateAsync
    Then enable the "Upload File" button on my UI
On clicking the "Upload File" button
   Call the Windows::Storage::Pickers::FileOpenPicker::
     PickSingleFileAsync function
    Then call oAuthAcquireTokenAsync function
    Then call UploadFileToDropboxAsync function

Bei dem in Abbildung 6 dargestellten Ereignishandler für die SignInBtnClicked-Schaltfläche führe ich zuerst eine einfache Parametervalidierung durch, um sicherzustellen, dass nicht leere Zeichenfolgenwert für die ConsumerKey- und ConsumerSecret-Parameter weitergeleitet werden, die ich zur Authentifizierung an Dropbox weiterleite. Als Nächstes erhalte ich eine Instanz des Dispatcher-Objekts, das dem aktuellen Thread von CoreWindow zugeordnet ist, und speichere es in einer Membervariable der MainPage-Klasse. Ein Dispatcher-Objekt ist für die Verarbeitung von Dialogfeldmeldungen und die Verteilung der Ereignisse an die App verantwortlich. Danach erstelle ich eine Instanz der OnlineIdAuthenticator-Klasse. Die OnlineIdAuthenticator-Klasse enthält Hilfsfunktionen, mit denen ich ein modales Dialogfeld der App öffnen und den sicheren Autorisierungsworkflow abschließen kann. Somit ist es nicht mehr erforderlich, eine Browserinstanz zu starten und den App-Fokus auf den Browser zu lenken.

Abbildung 6: Die SignInBtnClicked-Funktion

void MainPage::SignInBtnClicked(Platform::Object^ sender, 
  RoutedEventArgs^ e)
{
  if ((ConsumerKey->Text == nullptr) || 
    (ConsumerSecret->Text == nullptr))
  {
    using namespace Windows::UI::Popups;
    auto msgDlg = ref new MessageDialog(
      "Please check the input for the Consumer Key and/or Consumer Secret tokens");
    msgDlg->ShowAsync();
  }
  m_dispatcher =
     Windows::UI::Core::CoreWindow::GetForCurrentThread()->Dispatcher;
  m_creds = std::make_shared<AppCredentials>();
  m_authenticator = ref new OnlineIdAuthenticator();
  consumerKey = ConsumerKey->Text->Data();
  consumerSecret = ConsumerSecret->Text->Data();
  ConsumerKey->Text = nullptr;
  ConsumerSecret->Text = nullptr;
  OAuthLoginAsync(m_creds).then([this]
  {          
    m_authurl = DropBoxAuthorizeURI;               
    m_authurl += 
      utility::conversions::to_string_t(this->m_creds->Token());
    m_authurl += L"&oauth_callback=";
    m_authurl += WebAuthenticationBroker::
      GetCurrentApplicationCallbackUri()->AbsoluteUri->Data();
    auto action = m_dispatcher->RunAsync(
      Windows::UI::Core::CoreDispatcherPriority::Normal,
      ref new Windows::UI::Core::DispatchedHandler([this]()
    {
      auto beginUri = ref new Uri(ref new String(m_authurl.c_str()));
      task<WebAuthenticationResult^>authTask(
        WebAuthenticationBroker::AuthenticateAsync(
        WebAuthenticationOptions::None, beginUri));
      authTask.then([this](WebAuthenticationResult^ result)
      {
        String^ statusString;
        switch(result->ResponseStatus)
        {
          case WebAuthenticationStatus::Success:
          {
            auto actionEnable = m_dispatcher->RunAsync(
              Windows::UI::Core::CoreDispatcherPriority::Normal,
              ref new Windows::UI::Core::DispatchedHandler([this]()
              {
                UploadFileBtn->IsEnabled = true;
              }));
          }
        }
      });
    }));
}

Dann rufe ich die OAuthLoginAsync-Funktion auf, die den Anmeldevorgang bei Dropbox durchführt. Sobald die Async-Funktion zurückgegeben wird, verwende ich die RunAsync-Funktion des Dispatcher-Objekts zum Marshallen des Rückrufs an meinen UI-Thread vom Hintergrundthread meiner asynchronen Aufgabe. Die RunAsync-Funktion erfordert zwei Parameter: einen Prioritätswert und eine DispatchedHandler-Instanz. Ich lege den Prioritätswert auf „Normal“ fest und leite eine Lambda-Funktion an die DispatchedHandler-Instanz weiter. Im Lambda-Ausdruck rufe ich die statische AuthenticateAsync-Funktion der WebAuthenticationBroker-Klasse auf, die dann das modale Dialogfeld der App anzeigt und die sichere Autorisierung abschließt.

Sobald der Workflow abgeschlossen wurde, wird das Dialogfeld entfernt und die Funktion gibt entweder einen erfolgreichen Abschluss oder jegliche aufgetretene Fehlerbedingungen zurück. In meinem Fall verarbeite ich einfach den WebAuthenticationStatus::Success-Rückgabetyp und verwende das Dispatcher-Objekt noch einmal, um die UploadFile-Schaltfläche auf der App-Benutzeroberfläche zu aktivieren. Da alle von mir aufgerufenen Funktionen naturgemäß asynchron sind, muss ich das Dispatcher-Objekt zum Marshallen von Aufrufen des UI-Threads verwenden, wenn ich auf beliebige Benutzeroberflächenelemente zugreifen möchte.

Abbildung 7 zeigt den UploadFileBtnClicked-Ereignishandler. Der Handler selbst verfügt nicht über viel Code. Ich rufe die FileOpenPicker::PickSingleFileAsync-Funktion auf, die die Auswahl einer einzigen Textdatei über die Auswahlbenutzeroberfläche ermöglicht. Dann rufe ich die OAuthAcquireTokenAsync-Funktion auf, wie in Abbildung 8 dargestellt, und nach erfolgreicher Durchführung rufe ich die UploadFileToDropBoxAsync-Funktion auf, wie in Abbildung 9 angegeben.

Abbildung 7: Die UploadFileBtnClicked-Funktion

void MainPage::UploadFileBtnClicked(  Platform::Object^ sender, 
  RoutedEventArgs^ e)
{
  using namespace Windows::Storage::Pickers;
  using namespace Windows::Storage;
  auto picker = ref new FileOpenPicker();
  picker->SuggestedStartLocation = PickerLocationId::DocumentsLibrary;
  picker->FileTypeFilter->Append(".txt");
  task<StorageFile^> (picker->PickSingleFileAsync())
    .then([this](StorageFile^ selectedFile)
  {
    m_fileToUpload = selectedFile;
    OAuthAcquireTokenAsync(m_creds).then([this](){
      UploadFileToDropBoxAsync(m_creds);
    });
  });         
}

Abbildung 8: Die OAuthAcquireTokenAsync-Funktion

task<void> MainPage::OAuthAcquireTokenAsync(
  std::shared_ptr<AppCredentials>& creds)
{
  uri url(DropBoxAccessTokenURI);
  std::shared_ptr<OAuth> oAuthObj = std::make_shared<OAuth>();
  auto signatureParams =
    oAuthObj->CreateOAuthSignedParameters(url.to_string(),
    L"GET",
    NULL,
    consumerKey,
    consumerSecret,
    creds->Token(),
    creds->TokenSecret()
    );
  std::wstring sb = oAuthObj->OAuthBuildSignedHeaders(url);
  http_client client(sb);   
  // Make the request and asynchronously process the response.
  return client.request(methods::GET)
    .then([&creds](http_response response)
  {
    if(response.status_code() != status_codes::OK)
    {
      auto stream = response.body();                    
      container_buffer<std::string> inStringBuffer;
      return stream.read_to_end(inStringBuffer)
        .then([inStringBuffer](pplx::task<size_t> previousTask)
      {
        UNREFERENCED_PARAMETER(previousTask);
        const std::string &text = inStringBuffer.collection();
        // Convert the response text to a wide-character string.
        std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,
           wchar_t> utf16conv;
        std::wostringstream ss;
        ss << utf16conv.from_bytes(text.c_str()) << std::endl;
        OutputDebugString(ss.str().data());
        // Handle error cases.                   
        return pplx::task_from_result();
      });
    }
    // Perform actions here reading from the response stream.
    istream bodyStream = response.body();
    container_buffer<std::string> inStringBuffer;
    return bodyStream.read_to_end(inStringBuffer)
      .then([inStringBuffer, &creds](pplx::task<size_t> previousTask)
    {
      UNREFERENCED_PARAMETER(previousTask);
      const std::string &text = inStringBuffer.collection();
      // Convert the response text to a wide-character string.
      std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, 
        wchar_t> utf16conv;
      std::wostringstream ss;
      std::vector<std::wstring> parts;
      ss << utf16conv.from_bytes(text.c_str()) << std::endl;
      Split(ss.str(), parts, '&', false);
      unsigned pos = parts[1].find('=');
      std::wstring token = parts[1].substr(pos + 1, 16);
      pos = parts[0].find('=');
      std::wstring tokenSecret = parts[0].substr(pos + 1);
      creds->SetToken(token);
      creds->SetTokenSecret(tokenSecret);
    });
  });
}

Abbildung 9: Die UploadFileToDropBoxAsync-Funktion

task<void> MainPage::UploadFileToDropBoxAsync(
  std::shared_ptr<AppCredentials>& creds)
{
  using concurrency::streams::file_stream;
  using concurrency::streams::basic_istream;
  uri url(DropBoxFileUploadURI);
  std::shared_ptr<oAuth> oAuthObj = std::make_shared<oAuth>();
  auto signatureParams =
    oAuthObj->CreateOAuthSignedParameters(url.to_string(),
    L"PUT",
    NULL,
    consumerKey,
    consumerSecret,
    creds->Token(),
    creds->TokenSecret()
  );          
  std::wstring sb = oAuthObj->OAuthBuildSignedHeaders(url);
  return file_stream<unsigned char>::open_istream(this->m_fileToUpload)
    .then([this, sb, url](pplx::task<basic_istream<unsigned char>> previousTask)
  {
    try
    {
      auto fileStream = previousTask.get();
      // Get the content length, used to set the Content-Length property.
      fileStream.seek(0, std::ios::end);
      auto length = static_cast<size_t>(fileStream.tell());
      fileStream.seek(0, 0);
      // Make HTTP request with the file stream as the body.
      http_request req;
      http_client client(sb);
      req.set_body(fileStream, length);
      req.set_method(methods::PUT);
      return client.request(req)
        .then([this, fileStream](pplx::task<http_response> previousTask)
      {
        fileStream.close();
        std::wostringstream ss;
        try
        {
          auto response = previousTask.get();
          auto body = response.body();                  
          // Log response success code.
          ss << L"Server returned status code "
          << response.status_code() << L"."
          << std::endl;
          OutputDebugString(ss.str().data());
          if (response.status_code() == web::http::status_codes::OK)
          {
            auto action = m_dispatcher->RunAsync(
              Windows::UI::Core::CoreDispatcherPriority::Normal,
              ref new Windows::UI::Core::DispatchedHandler([this]()
              {
                using namespace Windows::UI::Popups;
                auto msgDlg = ref new MessageDialog(
                  "File uploaded successfully to Dropbox");
                msgDlg->ShowAsync();
              }));
          }
        }
        catch (const http_exception& e)
        {
          ss << e.what() << std::endl;
          OutputDebugString(ss.str().data());
        }
      });           
    }                         
    catch (const std::system_error& e)
    {
      // Log any errors here.
      // Return an empty task.
      std::wostringstream ss;
      ss << e.what() << std::endl;
      OutputDebugString(ss.str().data());
      return pplx::task_from_result();
    }
  });
}

Die OAuthAcquireTokenAsync-Funktion führt die Aktion durch, um das mit dem Dropbox-Konto verbundene aktuelle Token zu erfassen. Ich erstelle zuerst die erforderliche Zugriffszeichenfolge und die HTTP-Anforderungsheader und rufe dann den Dropbox-Dienst zur Überprüfung der Anmeldeinformationen auf. Diese HTTP-Anforderung ist vom Typ „GET“, und die Antwort wird als Zeichenstrom zurückgegeben. Ich analysiere den Zeichenstrom und teile ihn auf, um das aktuelle Token und die Tokengeheimniswerte zu erhalten. Diese werden dann in der Instanz der AppCredentials-Klasse gespeichert.

Nachdem ich das aktuelle Token und die Tokengeheimniswerte von Dropbox erhalten habe, ist es an der Zeit, sie in die Praxis umzusetzen, indem eine Datei auf Dropbox hochgeladen wird. Wie es bei allen Webendpunkten in Dropbox üblich ist, erstelle ich zuerst die Parameterzeichenfolge und die HTTP-Header. Dann rufe ich den Endpunkt des Dropbox-Diensts auf, der mit dem Hochladen von Dateien verbunden ist. Diese HTTP-Anforderung ist vom Typ „PUT“, da ich versuche, Inhalt im Dienst zu platzieren. Bevor ich den Inhalt platziere, muss ich Dropbox auch die Größe des Inhalts mitteilen. Dies wird durch Festlegen der content_length-Eigenschaft der HTTP_request::­set_body-Methode auf die Größe der hochzuladenden Datei angegeben. Wenn die PUT-Methode erfolgreich zurückgegeben wurde, verwende ich das Dispatcher-Objekt, um dem Benutzer eine Erfolgsmeldung anzuzeigen.

Integration in Linux erfolgt später

Die Integration des C++-REST-SDK in Windows 8-Anwendungen (sowohl Windows Store-Apps als auch Desktopanwendungen) ist einfach und unkompliziert. Addieren Sie alle Vorteile zusammen, d. h. Schreiben von Code, der zwischen Plattformen freigegeben werden kann, die Verwendung von modernen C++-Programmierausdrücken und die Tatsache, dass der Code zwischen Windows-Apps und anderen Apps übertragbar ist, und Sie haben einen klaren Gewinner. Sie müssen sich über plattformspezifische Feinheiten keine Gedanken mehr machen, da sich diese auf Netzwerk-APIs beziehen. Nutzen Sie stattdessen Ihre Zeit, und arbeiten Sie Features aus, die Ihre Anwendung unterstützen sollte. In diesem einfachen Beispiel habe ich das C++-REST-SDK verwendet, um einen Benutzer bei Dropbox zu authentifizieren und dann eine Datei in die Dropbox-Cloud hochzuladen. Weitere Informationen zur Dropbox-REST-API finden Sie in der Dokumentation unter bit.ly/10OdTD0. In einem künftigen Artikel werde ich erläutern, wie dieselbe Aufgabe von einem Linux-Client durchgeführt wird.

Sridhar Poduri ist als Programm-Manager bei Microsoft im Windows-Team tätig. Er ist ein begeisterter Anhänger von C++ und Autor des Buchs „Modern C++ and Windows Store Apps“ (Sridhar Poduri, 2013). Seinen Blog zu C++ und Windows-Runtime, den er regelmäßig schreibt, finden Sie unter sridharpoduri.com.

Unser Dank gilt den folgenden technischen Experten von Microsoft für die Durchsicht dieses Artikels: Niklas Gustaffson, Sana Mithani und Oggy Sobajic