Поделиться через


Краткий старт: Пуш-уведомления в Windows SDK для приложений

В этом кратком руководстве вы создадите классическое приложение Windows, которое отправляет и получает push-уведомления с помощью пакета SDK для приложений Windows.

Предпосылки

Пример приложения

В этом кратком руководстве описывается добавление поддержки push-уведомлений в приложение. См. пример кода из этого краткого руководства в контексте примеров приложений, найденных на GitHub.

Справочник по API

Справочную документацию по API для push-уведомлений можно найти в пространстве имен Microsoft.Windows.PushNotifications, см.

Настройка идентификатора приложения в Azure Active Directory (AAD)

Push-уведомления в Windows App SDK используют идентификацию из Azure Active Directory (AAD). Учетные данные Azure необходимы при запросе URI канала WNS и при запросе маркеров доступа для отправки push-уведомлений. Примечание: Мы НЕ поддерживаем push-уведомления пакета Windows App SDK через Центр партнеров Майкрософт.

Шаг 1. Создание регистрации приложения AAD

Войдите в учетную запись Azure и создайте новый ресурс регистрации приложения AAD . Выберите Новая регистрация.

Шаг 2. Укажите имя и выберите параметр многоарендности

  1. Введите имя приложения

  2. Для push-уведомлений требуется параметр с несколькими клиентами, поэтому выберите это.

    1. Дополнительные сведения об арендаторах см. в разделе Кто может войти в приложение?.
  3. Нажмите кнопку Зарегистрировать.

  4. Запишите идентификатор приложения (клиента) , так как это ваш Azure AppId , который вы будете использовать во время регистрации активации и запроса токена доступа.

  5. Запишите ваш идентификаторкаталога (клиента), так как это ваш Azure TenantId , который вы будете использовать при запросе токена доступа.

    Это важно

    клиент регистрации приложений AAD запишите идентификатор приложения (клиента) иидентификатор каталога (клиента).

  6. Запишите ваш идентификатор объекта , так как это ваш Azure ObjectId, который вы будете использовать при запросе канала. Обратите внимание, что это НЕ идентификатор объекта, указанный на странице Essentials . Вместо этого, чтобы найти правильный идентификатор объекта , щелкните название приложения в поле управляемого приложения в локальном каталоге на странице Essentials.

    Снимок экрана, показывающий параметр управляемого приложения в локальном каталоге на странице Essentials

    Снимок экрана: поле

    Замечание

    Для получения идентификатора объекта требуется субъекта-службы; если ни один из них не связан с вашим приложением, выполните действия, описанные в одной из следующих статей, чтобы создать его на портале Azure или с помощью командной строки.

    Используйте портал для создания приложения Azure AD и представителя службы, который может получить доступ к ресурсам

    Используйте Azure PowerShell для создания учетной записи службы с помощью сертификата

Шаг 3. Создание секрета для регистрации приложения

Ваш секрет будет использоваться вместе с Azure AppId/ClientId, когда вы запрашиваете токен доступа, чтобы отправлять push-уведомления.

секрет приложения AAD

Перейдите к сертификатам и секретам и выберите новый секрет клиента.

Это важно

Убедитесь, что вы скопируете секрет после создания и сохраните его в безопасном расположении, например Azure Key Vault. Он будет доступен только один раз сразу после создания.

Шаг 4. Сопоставьте имя семейства пакетов вашего приложения с его Azure AppId

Если ваше приложение упаковано (в том числе упаковано с внешним расположением), можно использовать этот процесс для определения имени семейства пакетов приложения (PFN) и его идентификатора приложения Azure.

Если приложение является упакованным приложением Win32, создайте запрос сопоставления имен семейства пакетов (PFN), отправив Win_App_SDK_Push@microsoft.com сообщение электронной почты с строкой темы "Запрос на сопоставление push-уведомлений пакета SDK для приложений Windows" и текст "PFN: [ваш PFN]", AppId: [your APPId], ObjectId: [your ObjectId]. Запросы на сопоставление выполняются еженедельно. Вы получите уведомление после завершения запроса на сопоставление.

Настройка приложения для получения push-уведомлений

Шаг 1. Добавьте объявления пространства имен

Добавьте пространство имен для push-уведомлений пакета SDK для Приложений Windows Microsoft.Windows.PushNotifications.

#include <winrt/Microsoft.Windows.PushNotifications.h>

using namespace winrt::Microsoft::Windows::PushNotifications;

Шаг 2. Добавление активатора COM в манифест приложения

Это важно

Если ваше приложение неупаковано (т. е. у него отсутствует удостоверение пакета во время выполнения), перейдите к Шагу 3: Зарегистрируйтесь для получения push-уведомлений и реагируйте на них при запуске приложения.

Если приложение упаковано (включая пакет с внешним расположением): откройте Package.appxmanifest. Добавьте следующее внутрь элемента <Application>. Замените значения Id, Executable, и DisplayName на те, которые специфичны для вашего приложения.

<!--Packaged apps only-->
<!--package.appxmanifest-->

<Package
  ...
  xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
  ...
  <Applications>
    <Application>
      ...
      <Extensions>

        <!--Register COM activator-->    
        <com:Extension Category="windows.comServer">
          <com:ComServer>
              <com:ExeServer Executable="SampleApp\SampleApp.exe" DisplayName="SampleApp" Arguments="----WindowsAppRuntimePushServer:">
                <com:Class Id="[Your app's Azure AppId]" DisplayName="Windows App SDK Push" />
            </com:ExeServer>
          </com:ComServer>
        </com:Extension>
    
      </Extensions>
    </Application>
  </Applications>
 </Package>    

Шаг 3. Регистрация и реагирование на push-уведомления при запуске приложения

Обновите метод приложения main() , чтобы добавить следующее:

  1. Зарегистрируйте приложение для получения push-уведомлений, вызвав PushNotificationManager::Default().Register().
  2. Проверьте источник запроса активации, вызвав AppInstance::GetCurrent().GetActivatedEventArgs(). Если активация была вызвана push-уведомлением, ответьте, учитывая данные уведомления.

Это важно

Необходимо вызвать PushNotificationManager::D efault(). Зарегистрируйте перед вызовом AppInstance.GetCurrent.GetActivatedEventArgs.

Следующий пример взят из примера упакованного приложения, найденного на GitHub.

// cpp-console.cpp
#include "pch.h"
#include <iostream>
#include <winrt/Microsoft.Windows.PushNotifications.h>
#include <winrt/Microsoft.Windows.AppLifecycle.h>
#include <winrt/Windows.Foundation.h>
#include <wil/result.h>
#include <wil/cppwinrt.h>


using namespace winrt;
using namespace Windows::Foundation;

using namespace winrt::Microsoft::Windows::PushNotifications;
using namespace winrt::Microsoft::Windows::AppLifecycle;

winrt::guid remoteId{ "7edfab6c-25ae-4678-b406-d1848f97919a" }; // Replace this with your own Azure ObjectId



void SubscribeForegroundEventHandler()
{
    winrt::event_token token{ PushNotificationManager::Default().PushReceived([](auto const&, PushNotificationReceivedEventArgs const& args)
    {
        auto payload{ args.Payload() };

        std::string payloadString(payload.begin(), payload.end());
        std::cout << "\nPush notification content received in the FOREGROUND: " << payloadString << std::endl;
    }) };
}

int main()
{
    // Setup an event handler, so we can receive notifications in the foreground while the app is running.
    SubscribeForegroundEventHandler();

    PushNotificationManager::Default().Register();

    auto args{ AppInstance::GetCurrent().GetActivatedEventArgs() };
    switch (args.Kind())
    {
        // When it is launched normally (by the users, or from the debugger), the sample requests a WNS Channel URI and
        // displays it, then waits for notifications. This user can take a copy of the WNS Channel URI and use it to send
        // notifications to the sample
        case ExtendedActivationKind::Launch:
        {
            // Checks to see if push notifications are supported. Certain self-contained apps may not support push notifications by design
            if (PushNotificationManager::IsSupported())
            {
                // Request a WNS Channel URI which can be passed off to an external app to send notifications to.
                // The WNS Channel URI uniquely identifies this app for this user and device.
                PushNotificationChannel channel{ RequestChannel() };
                if (!channel)
                {
                    std::cout << "\nThere was an error obtaining the WNS Channel URI" << std::endl;
    
                    if (remoteId == winrt::guid { "00000000-0000-0000-0000-000000000000" })
                    {
                        std::cout << "\nThe ObjectID has not been set. Refer to the readme file accompanying this sample\nfor the instructions on how to obtain and setup an ObjectID" << std::endl;
                    }
                }
    
                std::cout << "\nPress 'Enter' at any time to exit App." << std::endl;
                std::cin.ignore();
            }
            else
            {
                // App implements its own custom socket here to receive messages from the cloud since Push APIs are unsupported.
            }
        }
        break;

        // When it is activated from a push notification, the sample only displays the notification.
        // It doesn’t register for foreground activation of perform any other actions
        // because background activation is meant to let app perform only small tasks in order to preserve battery life.
        case ExtendedActivationKind::Push:
        {
            PushNotificationReceivedEventArgs pushArgs{ args.Data().as<PushNotificationReceivedEventArgs>() };

            // Call GetDeferral to ensure that code runs in low power
            auto deferral{ pushArgs.GetDeferral() };

            auto payload{ pushArgs.Payload() } ;

            // Do stuff to process the raw notification payload
            std::string payloadString(payload.begin(), payload.end());
            std::cout << "\nPush notification content received in the BACKGROUND: " << payloadString.c_str() << std::endl;
            std::cout << "\nPress 'Enter' to exit the App." << std::endl;

            // Call Complete on the deferral when finished processing the payload.
            // This removes the override that kept the app running even when the system was in a low power mode.
            deferral.Complete();
            std::cin.ignore();
        }
        break;

        default:
            std::cout << "\nUnexpected activation type" << std::endl;
            std::cout << "\nPress 'Enter' to exit the App." << std::endl;
            std::cin.ignore();
            break;
    }

    // We do not call PushNotificationManager::UnregisterActivator
    // because then we wouldn't be able to receive background activations, once the app has closed.
    // Call UnregisterActivator once you don't want to receive push notifications anymore.
}

Шаг 4. Запрос URI канала WNS и регистрация его с помощью сервера WNS

URI канала WNS — это конечные точки HTTP для отправки push-уведомлений. Каждый клиент должен запросить URI канала и зарегистрировать его на сервере WNS для получения push-уведомлений.

Замечание

Срок действия URI канала WNS истекает через 30 дней.

auto channelOperation{ PushNotificationManager::Default().CreateChannelAsync(winrt::guid("[Your app's Azure ObjectID]")) };

PushNotificationManager попытается создать URI канала, повторяя попытку в течение не более чем 15 минут. Создайте обработчик событий для ожидания завершения вызова. После завершения вызова, если он был успешным, зарегистрируйте URI на сервере WNS.

// cpp-console.cpp

winrt::Windows::Foundation::IAsyncOperation<PushNotificationChannel> RequestChannelAsync()
{
    // To obtain an AAD RemoteIdentifier for your app,
    // follow the instructions on https://learn.microsoft.com/azure/active-directory/develop/quickstart-register-app
    auto channelOperation = PushNotificationManager::Default().CreateChannelAsync(remoteId);

    // Setup the inprogress event handler
    channelOperation.Progress(
        [](auto&& sender, auto&& args)
        {
            if (args.status == PushNotificationChannelStatus::InProgress)
            {
                // This is basically a noop since it isn't really an error state
                std::cout << "Channel request is in progress." << std::endl << std::endl;
            }
            else if (args.status == PushNotificationChannelStatus::InProgressRetry)
            {
                LOG_HR_MSG(
                    args.extendedError,
                    "The channel request is in back-off retry mode because of a retryable error! Expect delays in acquiring it. RetryCount = %d",
                    args.retryCount);
            }
        });

    auto result = co_await channelOperation;

    if (result.Status() == PushNotificationChannelStatus::CompletedSuccess)
    {
        auto channelUri = result.Channel().Uri();

        std::cout << "channelUri: " << winrt::to_string(channelUri.ToString()) << std::endl << std::endl;

        auto channelExpiry = result.Channel().ExpirationTime();

        // Caller's responsibility to keep the channel alive
        co_return result.Channel();
    }
    else if (result.Status() == PushNotificationChannelStatus::CompletedFailure)
    {
        LOG_HR_MSG(result.ExtendedError(), "We hit a critical non-retryable error with channel request!");
        co_return nullptr;
    }
    else
    {
        LOG_HR_MSG(result.ExtendedError(), "Some other failure occurred.");
        co_return nullptr;
    }

};

PushNotificationChannel RequestChannel()
{
    auto task = RequestChannelAsync();
    if (task.wait_for(std::chrono::seconds(300)) != AsyncStatus::Completed)
    {
        task.Cancel();
        return nullptr;
    }

    auto result = task.GetResults();
    return result;
}

Шаг 5. Создание и установка приложения

Используйте Visual Studio для сборки и установки приложения. Щелкните правой кнопкой мыши на файл решения в Проводнике решений и выберите Развертывание. Visual Studio создаст приложение и установит его на компьютере. Приложение можно запустить, запустив его с помощью меню "Пуск" или отладчика Visual Studio.

Отправка push-уведомления в приложение

На этом этапе все конфигурации завершено, и сервер WNS может отправлять push-уведомления клиентским приложениям. В следующих шага х дополнительные сведения см. в заголовках запросов и ответов сервера push-уведомлений.

Шаг 1: Запрос токена доступа

Чтобы отправить push-уведомление, сервер WNS сначала должен запросить маркер доступа. Отправьте HTTP-запрос POST с вашим Azure TenantId, Azure AppId и секретным ключом. Сведения о получении Идентификатора клиента (TenantId) Azure и Идентификатора приложения (AppId) Azure см. в разделе Получение значений идентификатора клиента и приложения для входа.

Пример HTTP-запроса:

POST /{tenantID}/oauth2/v2.0/token Http/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 160

grant_type=client_credentials&client_id=<Azure_App_Registration_AppId_Here>&client_secret=<Azure_App_Registration_Secret_Here>&scope=https://wns.windows.com/.default/

Пример запроса на C#:

//Sample C# Access token request
var client = new RestClient("https://login.microsoftonline.com/{tenantID}/oauth2/v2.0");
var request = new RestRequest("/token", Method.Post);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("grant_type", "client_credentials");
request.AddParameter("client_id", "[Your app's Azure AppId]");
request.AddParameter("client_secret", "[Your app's secret]");
request.AddParameter("scope", "https://wns.windows.com/.default");
RestResponse response = await client.ExecutePostAsync(request);
Console.WriteLine(response.Content);

Если запрос выполнен успешно, вы получите ответ, содержащий токен в поле access_token.

{
    "token_type":"Bearer",
    "expires_in":"86399",
    "ext_expires_in":"86399",
    "expires_on":"1653771789",
    "not_before":"1653685089",
    "access_token":"[your access token]"
}

Шаг 2. Отправка необработанного уведомления

Создайте HTTP-запрос POST, содержащий маркер доступа, полученный на предыдущем шаге, и содержимое push-уведомления, которое вы хотите отправить. Содержимое push-уведомления будет доставлено в приложение.

POST /?token=[The token query string parameter from your channel URL. E.g. AwYAAABa5cJ3...] HTTP/1.1
Host: dm3p.notify.windows.com
Content-Type: application/octet-stream
X-WNS-Type: wns/raw
Authorization: Bearer [your access token]
Content-Length: 46

{ Sync: "Hello from the Contoso App Service" }
var client = new RestClient("[Your channel URL. E.g. https://wns2-by3p.notify.windows.com/?token=AwYAAABa5cJ3...]");
var request = new RestRequest();
request.Method = Method.Post; 
request.AddHeader("Content-Type", "application/octet-stream");
request.AddHeader("X-WNS-Type", "wns/raw");
request.AddHeader("Authorization", "Bearer [your access token]");
request.AddBody("Notification body");
RestResponse response = await client.ExecutePostAsync(request);");

Шаг 3. Отправка уведомления об облачном приложении

Если вы заинтересованы только в отправке необработанных уведомлений, игнорируйте этот шаг. Чтобы отправить уведомление приложения из облака, также известное как push-уведомление или всплывающее уведомление, сначала выполните Краткое руководство: Уведомления приложений в Windows App SDK. Уведомления приложений могут отправляться (отправляться из облака) или отправляться локально. Отправка уведомления об использовании облачного приложения аналогична отправке необработанного уведомления в шаге 2, за исключением заголовка X-WNS-Type , типа контента, а содержимое содержит полезные данные XML-уведомлений приложения. См. схему уведомлений XML и для получения дополнительных сведений о том, как создать полезную нагрузку XML.

Создайте HTTP-запрос POST, содержащий маркер доступа и содержимое уведомления о облачном приложении, которое вы хотите отправить. Содержимое push-уведомления будет доставлено в приложение.

POST /?token=AwYAAAB%2fQAhYEiAESPobjHzQcwGCTjHu%2f%2fP3CCNDcyfyvgbK5xD3kztniW%2bjba1b3aSSun58SA326GMxuzZooJYwtpgzL9AusPDES2alyQ8CHvW94cO5VuxxLDVzrSzdO1ZVgm%2bNSB9BAzOASvHqkMHQhsDy HTTP/1.1
Host: dm3p.notify.windows.com
Content-Type: text/xml
X-WNS-Type: wns/toast
Authorization: Bearer [your access token]
Content-Length: 180

<toast><visual><binding template="ToastGeneric"><text>Example cloud toast notification</text><text>This is an example cloud notification using XML</text></binding></visual></toast>
var client = new RestClient("https://dm3p.notify.windows.com/?token=AwYAAAB%2fQAhYEiAESPobjHzQcwGCTjHu%2f%2fP3CCNDcyfyvgbK5xD3kztniW%2bjba1b3aSSun58SA326GMxuzZooJYwtpgzL9AusPDES2alyQ8CHvW94cO5VuxxLDVzrSzdO1ZVgm%2bNSB9BAzOASvHqkMHQhsDy");
client.Timeout = -1;

var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "text/xml");
request.AddHeader("X-WNS-Type", "wns/toast");
request.AddHeader("Authorization", "Bearer <AccessToken>");
request.AddParameter("text/xml", "<toast><visual><binding template=\"ToastGeneric\"><text>Example cloud toast notification</text><text>This is an example cloud notification using XML</text></binding></visual></toast>",  ParameterType.RequestBody);
Console.WriteLine(response.Content);

Ресурсы