Применение C++ REST SDK в приложениях Windows Store

Сридхар Подури

Исходный код можно скачать по ссылке.

Продукты и технологии:
C++ REST SDK, OAuth
В статье рассматриваются:

  • предыдущий пример Win32-приложения;
  • варианты интеграции с Windows Runtime;
  • использование класса WebAuthenticationBroker;
  • создание цепочки асинхронных веб-запросов.

В предыдущей статье (msdn.microsoft.com/magazine/dn342869) я познакомил вас с C++ REST SDK и тем, как его можно использовать в Win32/MFC-приложениях. В этой статье мы обсудим, как C++ REST SDK можно интегрировать в приложения Windows Store. Одной из моих исходных целей в применении C++ REST SDK и класса аутентификации на основе OAuth было максимально полное использование стандартного C++ и лишь при необходимости взаимодействие со специфичными для платформы API. Кратко напомню суть предыдущей статьи.

  1. Код в классе аутентификации на основе OAuth использует только стандартные C++-типы - без специфичных для Windows типов.
  2. Код для выдачи веб-запросов к REST-сервису Dropbox использует типы из C++ REST SDK.
  3. Единственный специфичный для платформы код - это функция, которая запускает Internet Explorer, выполняет аутентификацию приложения и получает одобрение от портала Dropbox.

Я поставил те же цели в своем приложении Windows Store для поддержки аутентификации и загрузки файла в Dropbox. Я стремился написать максимально больше портируемого кода на C++ и взаимодействовать с Windows Runtime (WinRT) только при необходимости.

Проблемы с Win32-решением

Одним из крупных недостатков в предыдущем Win32-приложении была необходимость запуска внешнего приложения для выполнения процесса авторизации на основе OAuth. Это означало, что я должен был запускать Internet Explorer (можно было бы запускать и любой другой браузер), входить в Dropbox по своим удостоверениям, а затем выполнять необходимый рабочий процесс. Он проиллюстрирован на рис. 1 и 2.

Вход в Dropbox по моим удостоверениям до авторизации доступа приложения
Рис. 1. Вход в Dropbox по моим удостоверениям до авторизации доступа приложения

Успешная авторизация моего приложения на портале Dropbox
Рис. 2. Успешная авторизация моего приложения на портале Dropbox

Как видите, запуск внешнего приложения и предложение пользователям пройти рабочий процесс через внешнее приложение уводит фокус ввода из окна моего приложения. Как разработчик я также лишен стандартного механизма, через который мое приложение могло бы уведомляться о завершении этого рабочего процесса. При концентрации на асинхронном программировании и использовании C++ REST SDK, предназначенного для поддержки программирования на основе асинхронных задач, вынужденный запуск внешнего приложения со всей очевидностью является для меня неприятной ситуацией. Я исследовал подходы с применением именованных каналов (named pipes), проецируемых в память файлов (memory-mapped files) и т. д., но все эти подходы требуют создания другого приложения для хостинга экземпляра элемента управления "веб-браузер" и последующей записи значения, свидетельствующего об успехе, обратно через именованный канал, общую память или проецируемый в память файл. В итоге я остановился на использовании браузера для выполнения этой задачи, так как не хотел писать другую программу, которая обертывала бы элемент управления "веб-браузер".

Одним из крупных недостатков в предыдущем Win32-приложении была необходимость запуска внешнего приложения для выполнения процесса авторизации на основе OAuth.

Интеграция с Windows Runtime

Начав проектировать свое приложение под Windows Runtime, я рассмотрел несколько вариантов. Я вкратце расскажу о них здесь, а потом мы подробно обсудим выбранный мной подход.

  1. Используем активацию протокола и даем системе запустить подходящий процесс для обработки этого протокола, вызвав функцию Windows::System::Launcher::LaunchUriAsync. Это означает, что для URI на основе HTTPS операционная система запустит браузер по умолчанию. Это аналогично запуску Internet Explorer из Win32-примера, но с "двойной комиссией": мое приложение Windows Store станет фоновым процессом, браузер по умолчанию запустится в полноэкранном режиме, и в самом худшем случае мое приложение будет приостановлено на время выполнения пользователем рабочего процесса. Совершенно не годится!
  2. Интегрируем в приложение элемент управления WebView. Использование XAML-элемента WebView позволяет встроить всю навигацию по рабочему процессу в контекст моего приложения. Теоретически, я также могу получать уведомления о завершении процесса, слушая событие window.external.notify, генерируемое элементом управления WebView. Однако на практике это событие генерируется, только если веб-страница генерирует событие уведомления. В моем случае страница Dropbox, где выполняется процесс авторизации, такое событие не генерирует. Неприятная ситуация!
  3. Используем WebAuthenticationBroker в моем приложении. Продолжая копаться в Windows Runtime, я случайно наткнулся на класс WebAuthenticationBroker. Судя по всему, он мог бы помочь мне в выполнении процесса авторизации, и, действительно, я сумел создать всю необходимую функциональность на его основе. Прежде чем перейти к рассмотрению кода, позвольте мне пояснить некоторые детали, касающиеся WebAuthenticationBroker.

WebAuthenticationBroker

В мире подключенных приложений, чтобы получить согласие и одобрение пользователя, важно запрашивать его удостоверения через безопасный и доверяемый механизм. Никто не хочет быть разработчиком, чьи приложения допускают утечку удостоверений пользователя или оказываются уязвимыми к скрытым атакам с целью похищения информации о пользователе. Windows Runtime включает ряд API и необходимые технологии, позволяющие разработчику безопасно передавать удостоверения пользователя. WebAuthenticationBroker - одно из таких средств, которое дает возможность приложениям Windows Store использовать протоколы аутентификации и авторизации через Интернет, такие как OAuth и OpenID. Как же это работает в моем приложении-примере для Dropbox?

  1. Я выдаю начальный асинхронный запрос к Dropbox, который возвращает маркер и секрет для моего приложения. Этот начальный запрос передается через функцию oAuthLoginAsync.
  2. Как только функция oAuthLoginAsync возвращает управление, я конструирую в продолжении последовательности URI, где должен начаться процесс авторизации. В своем примере я определил начальный URI как строковую константу:
  1. const std::wstring DropBoxAuthorizeURI = 
  2.   L"https://www.dropbox.com/1/oauth/authorize?oauth_token=";
  1. Затем я формирую URI HTTP-запроса, дописывая маркер, возвращенный Dropbox.
  2. В качестве дополнительного шага я конструирую параметр с URI обратного вызова, обращаясь к функции WebAuthenticationBroker::GetCurrentApplicationCallbackUri. Заметьте, что я не использовал URI обратного вызова в своем настольном приложении, так как этот параметр не обязателен и я полагался на Internet Explorer в выполнении задачи авторизации.
  3. Теперь строка запроса готова, и я могу выдать запрос. Вместо использования класса http_client или интерфейса IHttpWebRequest2 из C++ REST SDK для вызовов веб-сервиса я вызываю функцию WebAuthenticationBroker::AuthenticateAsync.
  4. Функция WebAuthenticationBroker::AuthenticateAsync принимает два параметра: перечисление WebAuthenticationOptions и URI. Перегруженный экземпляр той же функции принимает перечисление WebAuthenticationOptions и два URI, по одному из которых начинается процесс аутентификации, а по другому - заканчивается.
  5. Я использую первую версию функции AuthenticateAsync и передаю значение None для перечисления WebAuthenticationOptions, а также URI, сформированный для моего веб-запроса.
  6. WebAuthenticationBroker размещается между моим приложением и системой. В точке, где я вызываю AuthenticateAsync, он создает системный модальный диалог, который является модальным для моего приложения.
  7. Брокер подключает окно веб-хоста к созданному им модальному диалоговому окну.
  8. Затем брокер выбирает выделенный процесс контейнера приложений, отделенный от контейнера, в котором выполняется мое приложение. Это также приводит к очистке любых сохраненных данных в моем приложении.
  9. Далее брокер начинает процесс аутентификации в этом только что выбранном контейнере приложения и переходит к URI, указанному функцией AuthenticateAsync.
  10. Когда пользователи взаимодействуют с веб-страницами, брокер проверяет каждый URL для указанного URI обратного вызова.
  11. Как только обнаруживается совпадение, веб-хост прекращает навигацию и посылает брокеру сигнал. Брокер убирает диалоговое окно, очищает любые сохраненные файлы cookie, созданные веб-хостом, из контейнера приложения и возвращает данные протокола обратно приложению.

В мире подключенных приложений, чтобы получить согласие и одобрение пользователя, важно запрашивать его удостоверения через безопасный и доверяемый механизм.

Рис. 3 иллюстрирует модальный диалог WebAuthenticationBroker в приложении-примере Dropbox после того, как веб-хост перешел на начальный URI. Поскольку Dropbox ожидает входа пользователей до появления страницы авторизации, веб-хост перенаправляет процесс навигации на страницу входа в Dropbox.

Страница входа в Dropbox, показываемая в модальном диалоге
Рис. 3. Страница входа в Dropbox, показываемая в модальном диалоге

Как только пользователь вошел в Dropbox, веб-хост переходит к URI авторизации. Это отражено на рис. 4. Из рис. 3 и 4 понятно, что диалог размещается поверх UI моего приложения. UI также остается согласованным безотносительно приложения-источника, вызывающего метод WebAuthenticationBroker::AuthenticateAsync. Поскольку вся пользовательская среда сохраняет согласованность, пользователи могут предоставлять удостоверения, не беспокоясь о приложениях, обрабатывающих эту информацию, и о ее случайной утечке.

Dropbox запрашивает согласие пользователя на авторизацию приложения
Рис. 4. Dropbox запрашивает согласие пользователя на авторизацию приложения

Одна важная вещь, о которой я не упомянул, - необходимость вызова функции WebAuthenticationBroker::AuthenticateAsync из UI-потока. Все веб-запросы в C++ REST SDK выдаются в фоновом потоке, а вывести UI из фонового потока нельзя. Поэтому я использую системный диспетчер и вызываю его функцию-член RunAsync для отображения модального UI (рис. 5).

Рис. 5. Использование системного диспетчера для отображения модального UI

  1. auto action = m_dispatcher->RunAsync(
  2.   Windows::UI::Core::CoreDispatcherPriority::Normal,
  3.   ref new Windows::UI::Core::DispatchedHandler([this]()
  4.   {
  5.     auto beginUri = ref new Uri(ref new String(m_authurl.c_str()));
  6.     task<WebAuthenticationResult^> authTask(WebAuthenticationBroker::
  7.       AuthenticateAsync(WebAuthenticationOptions::None, beginUri));
  8.       authTask.then([this](WebAuthenticationResult^ result)
  9.       {
  10.         String^ statusString;
  11.         switch(result->ResponseStatus)
  12.         {
  13.           case WebAuthenticationStatus::Success:
  14.           {
  15.             auto actionEnable = m_dispatcher->RunAsync(
  16.               Windows::UI::Core::CoreDispatcherPriority::Normal,
  17.               ref new Windows::UI::Core::DispatchedHandler([this]()
  18.               {
  19.                 UploadFileBtn->IsEnabled = true;
  20.               }));
  21.           }
  22.         }
  23.       });
  24. }));

Как только пользователь вошел в Dropbox, веб-хост переходит к URI авторизации.

По окончании процесса авторизации я снова запускаю диспетчер, чтобы сделать доступной кнопку Upload File в основном UI. Эта кнопка остается недоступной, пока пользователи не аутентифицировали и не авторизовали мое приложение для доступа к Dropbox.

Создание цепочки асинхронных веб-запросов

Теперь легко свести все воедино. Во всех функциях, не взаимодействующих с Windows Runtime, я повторно использовал код из моего настольного приложения. Крупных изменений в коде нет, кроме одного: в функции UploadFileToDropboxAsync вместо C++ iostream используется WinRT-объект StorageFile.

При написании приложений Windows Store приходится учитывать некоторые ограничения, с которыми вы должны смириться. Одно из них - необходимость использования WinRT-объектов StorageFile вместо C++-потоков (streams) для чтения и записи данных в файлы. При разработке приложения Windows Store с применением C++ REST SDK все операции, связанные с файлами, ожидают передачи объекта StorageFile, а не C++-объекта потока данных. Внеся это небольшое изменение, я смог повторно использовать весь свой стандартный C++-код, поддерживающий код OAuth-авторизации и Dropbox, в приложении-примере для Windows Store.

Вот как выглядит соответствующий псевдокод (индивидуальные функции мы обсудим после этого псевдокода):

При щелчке кнопки SignIn   Вызов функции oAuthLoginAsync     Затем вызов WebAuthenticationBroker::AuthenticateAsync     Затем делаем доступной кнопку Upload File в UI При щелчке кнопки Upload File    Вызов функции Windows::Storage::Pickers::FileOpenPicker::      PickSingleFileAsync     Затем вызов функции oAuthAcquireTokenAsync     Затем вызов функции UploadFileToDropboxAsync

В обработчике событий кнопки SignInBtnClicked, показанном на рис. 6, я сначала выполняю простую проверку параметров, чтобы удостовериться, что в параметрах ConsumerKey и ConsumerSecret не передаются пустые строки, отправляемые потом в Dropbox для аутентификации. Затем я получаю экземпляр объекта Dispatcher, сопоставленного с текущим потоком CoreWindow, и сохраняю его как переменную-член класса MainPage. Dispatcher отвечает за обработку оконных сообщений и диспетчеризацию событий для приложения. Далее я создаю экземпляр класса OnlineIdAuthenticator. Этот класс содержит вспомогательные функции, которые позволяют мне выводить модальное диалоговое окно приложения и выполнять защищенный рабочий процесс авторизации. Это избавляет от необходимости запуска экземпляра браузера и перевода фокуса ввода с приложения в браузер.

Рис. 6. Функция SignInBtnClicked

  1. void MainPage::SignInBtnClicked(Platform::Object^ sender, 
  2.   RoutedEventArgs^ e)
  3. {
  4.   if ((ConsumerKey->Text == nullptr) // 
  5.     (ConsumerSecret->Text == nullptr))
  6.   {
  7.     using namespace Windows::UI::Popups;
  8.     auto msgDlg = ref new MessageDialog(
  9.       "Please check the input for the Consumer Key and/or Consumer Secret tokens");
  10.     msgDlg->ShowAsync();
  11.   }
  12.   m_dispatcher =
  13.      Windows::UI::Core::CoreWindow::GetForCurrentThread()->Dispatcher;
  14.   m_creds = std::make_shared<AppCredentials>();
  15.   m_authenticator = ref new OnlineIdAuthenticator();
  16.   consumerKey = ConsumerKey->Text->Data();
  17.   consumerSecret = ConsumerSecret->Text->Data();
  18.   ConsumerKey->Text = nullptr;
  19.   ConsumerSecret->Text = nullptr;
  20.   OAuthLoginAsync(m_creds).then([this]
  21.   {          
  22.     m_authurl = DropBoxAuthorizeURI;               
  23.     m_authurl += 
  24.       utility::conversions::to_string_t(this->m_creds->Token());
  25.     m_authurl += L"&oauth_callback=";
  26.     m_authurl += WebAuthenticationBroker::
  27.       GetCurrentApplicationCallbackUri()->AbsoluteUri->Data();
  28.     auto action = m_dispatcher->RunAsync(
  29.       Windows::UI::Core::CoreDispatcherPriority::Normal,
  30.       ref new Windows::UI::Core::DispatchedHandler([this]()
  31.     {
  32.       auto beginUri = ref new Uri(ref new String(m_authurl.c_str()));
  33.       task<WebAuthenticationResult^>authTask(
  34.         WebAuthenticationBroker::AuthenticateAsync(
  35.         WebAuthenticationOptions::None, beginUri));
  36.       authTask.then([this](WebAuthenticationResult^ result)
  37.       {
  38.         String^ statusString;
  39.         switch(result->ResponseStatus)
  40.         {
  41.           case WebAuthenticationStatus::Success:
  42.           {
  43.             auto actionEnable = m_dispatcher->RunAsync(
  44.               Windows::UI::Core::CoreDispatcherPriority::Normal,
  45.               ref new Windows::UI::Core::DispatchedHandler([this]()
  46.               {
  47.                 UploadFileBtn->IsEnabled = true;
  48.               }));
  49.           }
  50.         }
  51.       });
  52.     }));
  53. }

Затем я вызываю функцию OAuthLoginAsync, которая выполняет операцию входа в Dropbox. Как только эта асинхронная функция возвращает управление, я использую функцию RunAsync объекта Dispatcher для маршалинга вызова обратно в UI-поток из фонового потока асинхронной задачи. Функция RunAsync принимает два параметра: значение приоритета и экземпляр DispatchedHandler. Я задаю приоритет как "Normal" и передаю функцию лямбды экземпляру DispatchedHandler. В теле лямбды я вызываю статическую функцию AuthenticateAsync класса WebAuthenticationBroker, которая потом отображает модальный диалог приложения и помогает выполнить защищенную аутентификацию.

При написании приложений Windows Store приходится учитывать некоторые ограничения, с которыми вы должны смириться.

По окончании рабочего процесса диалог удаляется, и функция возвращает либо код успешного завершения, либо обнаруженные ошибки. В моем случае я просто обрабатываю возвращаемый тип WebAuthenticationStatus::Success и снова использую объект диспетчера, чтобы сделать доступной кнопку UploadFile в UI. Поскольку все вызываемые мной функции являются асинхронными, мне нужно задействовать объект диспетчера для маршалинга вызовов в UI-поток, если я хочу обращаться к каким-либо UI-элементам.

Обработчик событий UploadFileBtnClicked показан на рис. 7. В самом обработчике кода не так уж много. Я вызываю функцию FileOpenPicker::PickSingleFileAsync, которая позволяет выбрать один текстовый файл через интерфейс выбора (picker interface). Затем вызываю функцию OAuthAcquireTokenAsync (рис. 8) и при успешном завершении обращаюсь к функции UploadFileToDropBoxAsync (рис. 9).

Рис. 7. Функция UploadFileBtnClicked

  1. void MainPage::UploadFileBtnClicked(  Platform::Object^ sender, 
  2.   RoutedEventArgs^ e)
  3. {
  4.   using namespace Windows::Storage::Pickers;
  5.   using namespace Windows::Storage;
  6.   auto picker = ref new FileOpenPicker();
  7.   picker->SuggestedStartLocation = PickerLocationId::DocumentsLibrary;
  8.   picker->FileTypeFilter->Append(".txt");
  9.   task<StorageFile^> (picker->PickSingleFileAsync())
  10.     .then([this](StorageFile^ selectedFile)
  11.   {
  12.     m_fileToUpload = selectedFile;
  13.     OAuthAcquireTokenAsync(m_creds).then([this](){
  14.       UploadFileToDropBoxAsync(m_creds);
  15.     });
  16.   });         
  17. }

Рис. 8. Функция OAuthAcquireTokenAsync

  1. task<void> MainPage::OAuthAcquireTokenAsync(
  2.   std::shared_ptr<AppCredentials>& creds)
  3. {
  4.   uri url(DropBoxAccessTokenURI);
  5.   std::shared_ptr<OAuth> oAuthObj = std::make_shared<OAuth>();
  6.   auto signatureParams =
  7.     oAuthObj->CreateOAuthSignedParameters(url.to_string(),
  8.     L"GET",
  9.     NULL,
  10.     consumerKey,
  11.     consumerSecret,
  12.     creds->Token(),
  13.     creds->TokenSecret()
  14.     );
  15.   std::wstring sb = oAuthObj->OAuthBuildSignedHeaders(url);
  16.   http_client client(sb);   
  17.   // Выдаем запрос и асинхронно обрабатываем ответ
  18.   return client.request(methods::GET)
  19.     .then([&creds](http_response response)
  20.   {
  21.     if(response.status_code() != status_codes::OK)
  22.     {
  23.       auto stream = response.body();                    
  24.       container_buffer<std::string> inStringBuffer;
  25.       return stream.read_to_end(inStringBuffer)
  26.         .then([inStringBuffer](pplx::task<size_t> previousTask)
  27.       {
  28.         UNREFERENCED_PARAMETER(previousTask);
  29.         const std::string &text = inStringBuffer.collection();
  30.         // Преобразуем текст ответа в широкосимвольную строку
  31.         std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,
  32.            wchar_t> utf16conv;
  33.         std::wostringstream ss;
  34.         ss << utf16conv.from_bytes(text.c_str()) << std::endl;
  35.         OutputDebugString(ss.str().data());
  36.         // Обработка ошибок                  
  37.         return pplx::task_from_result();
  38.       });
  39.     }
  40.     // Здесь выполняем операции, читая из потока ответа
  41.     istream bodyStream = response.body();
  42.     container_buffer<std::string> inStringBuffer;
  43.     return bodyStream.read_to_end(inStringBuffer)
  44.       .then([inStringBuffer, &creds](pplx::task<size_t> previousTask)
  45.     {
  46.       UNREFERENCED_PARAMETER(previousTask);
  47.       const std::string &text = inStringBuffer.collection();
  48.       // Преобразуем текст ответа в широкосимвольную строку
  49.       std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, 
  50.         wchar_t> utf16conv;
  51.       std::wostringstream ss;
  52.       std::vector<std::wstring> parts;
  53.       ss << utf16conv.from_bytes(text.c_str()) << std::endl;
  54.       Split(ss.str(), parts, '&', false);
  55.       unsigned pos = parts[1].find('=');
  56.       std::wstring token = parts[1].substr(pos + 1, 16);
  57.       pos = parts[0].find('=');
  58.       std::wstring tokenSecret = parts[0].substr(pos + 1);
  59.       creds->SetToken(token);
  60.       creds->SetTokenSecret(tokenSecret);
  61.     });
  62.   });
  63. }

Рис. 9. Функция UploadFileToDropBoxAsync

  1. task<void> MainPage::UploadFileToDropBoxAsync(
  2.   std::shared_ptr<AppCredentials>& creds)
  3. {
  4.   using concurrency::streams::file_stream;
  5.   using concurrency::streams::basic_istream;
  6.   uri url(DropBoxFileUploadURI);
  7.   std::shared_ptr<oAuth> oAuthObj = std::make_shared<oAuth>();
  8.   auto signatureParams =
  9.     oAuthObj->CreateOAuthSignedParameters(url.to_string(),
  10.     L"PUT",
  11.     NULL,
  12.     consumerKey,
  13.     consumerSecret,
  14.     creds->Token(),
  15.     creds->TokenSecret()
  16.   );          
  17.   std::wstring sb = oAuthObj->OAuthBuildSignedHeaders(url);
  18.   return file_stream<unsigned char>::open_istream(this->m_fileToUpload)
  19.     .then([this, sb, url](pplx::task<basic_istream<unsigned char>> previousTask)
  20.   {
  21.     try
  22.     {
  23.       auto fileStream = previousTask.get();
  24.       // Получаем длину контента,
  25.      // присвоенного свойству Content-Length
  26.       fileStream.seek(0, std::ios::end);
  27.       auto length = static_cast<size_t>(fileStream.tell());
  28.       fileStream.seek(0, 0);
  29.       // Выдаем HTTP-запрос с файловым потоком в качестве тела
  30.       http_request req;
  31.       http_client client(sb);
  32.       req.set_body(fileStream, length);
  33.       req.set_method(methods::PUT);
  34.       return client.request(req)
  35.         .then([this, fileStream](pplx::task<http_response> previousTask)
  36.       {
  37.         fileStream.close();
  38.         std::wostringstream ss;
  39.         try
  40.         {
  41.           auto response = previousTask.get();
  42.           auto body = response.body();                  
  43.           // Протоколируем код успешного ответа
  44.           ss << L"Server returned status code "
  45.           << response.status_code() << L"."
  46.           << std::endl;
  47.           OutputDebugString(ss.str().data());
  48.           if (response.status_code() == web::http::status_codes::OK)
  49.           {
  50.             auto action = m_dispatcher->RunAsync(
  51.               Windows::UI::Core::CoreDispatcherPriority::Normal,
  52.               ref new Windows::UI::Core::DispatchedHandler([this]()
  53.               {
  54.                 using namespace Windows::UI::Popups;
  55.                 auto msgDlg = ref new MessageDialog(
  56.                   "File uploaded successfully to Dropbox");
  57.                 msgDlg->ShowAsync();
  58.               }));
  59.           }
  60.         }
  61.         catch (const http_exception& e)
  62.         {
  63.           ss << e.what() << std::endl;
  64.           OutputDebugString(ss.str().data());
  65.         }
  66.       });           
  67.     }                         
  68.     catch (const std::system_error& e)
  69.     {
  70.       // Здесь протоколируем любые ошибки
  71.       // и возвращаем пустую задачу
  72.       std::wostringstream ss;
  73.       ss << e.what() << std::endl;
  74.       OutputDebugString(ss.str().data());
  75.       return pplx::task_from_result();
  76.     }
  77.   });
  78. }

Функция OAuthAcquireTokenAsync получает маркер, сопоставленный с учетной записью Dropbox. Сначала я формирую необходимую строку доступа (access string) и заголовки HTTP-запроса и вызываю сервис Dropbox для проверки удостоверений. Этот HTTP-запрос имеет тип GET, а ответ возвращается как поток символов. Я разбираю этот поток для получения самого маркера и секрета, которые потом сохраняются в экземпляре класса AppCredentials.

Успешно получив маркер и его секрет от Dropbox, я использую их для загрузки файла в Dropbox. Как и в случае любой конечной точки веб-доступа к Dropbox, сначала формируется строка параметров и HTTP-заголовки. Затем вызывается конечная точка сервиса Dropbox, сопоставленная с загрузкой файлов. Этот HTTP-запрос имеет тип PUT, поскольку я пытаюсь поместить контент в сервис. Перед этим мне также нужно сообщить Dropbox о размере контента. Это указывается установкой значения свойства content_length в методе HTTP_request::set_body равным размеру загружаемого файла. После успешного возврата PUT-метода с помощью объекта диспетчера я вывожу пользователю сообщение об успешном завершении операции.

На очереди Linux

Интеграция C++ REST SDK в приложения Windows 8 (как Windows Store, так и настольные) проста и прямолинейна. Добавьте преимущества написания кода, который может быть общим между двумя платформами, применение идиом программирования на современном C++ и тот факт, что данный код является портируемым между приложениями как Windows, так и других ОС, - и приз ваш. Вы можете больше не беспокоиться о специфичных для платформ тонкостях, относящихся к сетевым API, и вместо этого уделять больше времени продумыванию функциональности, которую должно поддерживать ваше приложение. В этом простом примере я задействовал C++ REST SDK для аутентификации пользователя в Dropbox и последующей загрузки файла в облако Dropbox. Подробнее о Dropbox REST API см. документацию по ссылке bit.ly/10OdTD0. В следующей статье я покажу, как выполнять те же задачи из Linux-клиента.

Сридхар Подури (Sridhar Poduri) - менеджер программ в группе Windows в Microsoft. Страстный поклонник C++ и автор книги "Modern C++ and Windows Store Apps" (Sridhar Poduri, 2013), регулярно пишет о C++ и Windows Runtime в своем блоге sridharpoduri.com.

Выражаю благодарность за рецензирование статьи экспертам Microsoft Никласу Густаффсону (Niklas Gustaffson), Сана Митани (Sana Mithani) и Огги Шобахичу (Oggy Sobajic).


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=35639