Руководство. Отправка push-уведомлений в приложения Xamarin.Forms с помощью Центров уведомлений Azure через серверную службу

Скачать пример Скачивание примера

В этом руководстве вы используете Центры уведомлений Azure для отправки push-уведомлений в приложение Xamarin.Forms , предназначенное для Android и iOS.

Серверная часть веб-API ASP.NET Core используется для обработки регистрации устройств для клиента с использованием последнего и наилучшего подхода к установке. Служба также будет отправлять push-уведомления кроссплатформенным способом.

Эти операции обрабатываются с помощью пакета SDK центров уведомлений для внутренних операций. Дополнительные сведения об общем подходе приведены в документации по регистрации из серверной части приложения .

В этом руководстве описаны следующие действия.

Предварительные требования

Для этого вам потребуется:

  • Подписка Azure, в которой можно создавать ресурсы и управлять ими.
  • Компьютер Mac с установленным Visual Studio для Mac или компьютер с Visual Studio 2019.
  • У пользователей Visual Studio 2019 также должна быть установлена разработка мобильных приложений с помощью .NET, а также рабочие нагрузки ASP.NET и веб-разработки.
  • Возможность запуска приложения на устройствах Android (физические устройства или устройства эмулятора) или iOS (только на физических устройствах).

Для Android необходимо иметь:

  • Разработчик разблокировал физическое устройство или эмулятор (с API 26 и более поздних версий с установленными службами Google Play).

Для iOS необходимо:

Примечание

Симулятор iOS не поддерживает удаленные уведомления, поэтому при изучении этого примера на iOS требуется физическое устройство. Однако для работы с этим руководством вам не нужно запускать приложение на устройствах Android и iOS .

Вы можете выполнить действия, описанные в этом примере с основными принципами, без предварительного опыта. Однако вы сможете ознакомиться со следующими аспектами.

Важно

Указанные действия относятся к Visual Studio для Mac. С помощью Visual Studio 2019 можно выполнить инструкции, но при согласовании могут возникнуть некоторые различия. Например, описания пользовательского интерфейса и рабочих процессов, имена шаблонов, конфигурации среды и т. д.

Настройка служб push-уведомлений и Центра уведомлений Azure

В этом разделе описана настройка Firebase Cloud Messaging (FCM) и служб push-уведомлений Apple (APNS). Затем вы создадите и настроите центр уведомлений для работы с этими службами.

Создание проекта Firebase и включение Firebase Cloud Messaging для Android

  1. Войдите в консоль Firebase. Создайте проект Firebase, указав PushDemo в качестве имени проекта.

    Примечание

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

  2. После создания проекта выберите Добавить Firebase в приложение Android.

    Добавление Firebase в приложение Android

  3. На странице Добавление Firebase в приложение Android выполните следующие действия.

    1. В поле Имя пакета Android введите имя пакета. Например: com.<organization_identifier>.<package_name>.

      Указание имени пакета

    2. Выберите Зарегистрировать приложение.

    3. Выберите Скачать google-services.json. Затем сохраните файл в локальную папку для последующего использования и нажмите кнопку Далее.

      Скачать google-services.json

    4. Нажмите кнопку Далее.

    5. Выберите Continue to console (Продолжить в консоль).

      Примечание

      Если кнопка Продолжить в консоли не включена из-за проверка проверки установки, выберите Пропустить этот шаг.

  4. В консоли Firebase выберите шестеренку для проекта. Затем выберите Параметры проекта.

    Выбор параметров проекта

    Примечание

    Если вы еще не скачали файл google-services.json , его можно скачать на этой странице.

  5. Перейдите на вкладку Cloud Messaging вверху. Скопируйте и сохраните ключ сервера для последующего использования. Это значение используется для настройки центра уведомлений.

    Копирование ключа сервера

Регистрация приложения iOS для push-уведомлений

Чтобы отправлять push-уведомления в приложение iOS, зарегистрируйте приложение в Apple, а также зарегистрируйтесь для получения push-уведомлений.

  1. Если вы еще не зарегистрировали приложение, перейдите на портал подготовки iOS в Центре разработчиков Apple. Войдите на портал с помощью идентификатора Apple ID, перейдите в раздел Сертификаты, идентификаторы & Профили, а затем выберите Идентификаторы. Щелкните + , чтобы зарегистрировать новое приложение.

    Страница идентификаторов приложений портала подготовки iOS

  2. На экране Регистрация нового идентификатора выберите переключатель Идентификаторы приложений . Затем нажмите кнопку Продолжить.

    Страница регистрации нового идентификатора на портале подготовки iOS

  3. Обновите следующие три значения для нового приложения, а затем нажмите кнопку Продолжить:

    • Описание. Введите описательное имя приложения.

    • Идентификатор пакета. Введите идентификатор пакета в форме com.organization_identifier<>.<>product_name, как указано в руководстве по распространению приложений. На следующем снимке экрана значение используется в качестве идентификатора организации, mobcat а значение PushDemo — в качестве имени продукта.

      Страница регистрации идентификатора приложения на портале подготовки iOS

    • Push-уведомления. Установите флажок Push-уведомления в разделе Возможности .

      Форма для регистрации нового идентификатора приложения

      Это действие создает идентификатор приложения и запрашивает подтверждение информации. Нажмите кнопку Продолжить, а затем нажмите кнопку Зарегистрировать , чтобы подтвердить новый идентификатор приложения.

      Подтверждение нового идентификатора приложения

      После нажатия кнопки Зарегистрировать вы увидите новый идентификатор приложения в виде элемента строки на странице Сертификаты, идентификаторы & Профили .

  4. На странице Сертификаты, идентификаторы & Профили в разделе Идентификаторы найдите созданный элемент строки идентификатора приложения. Затем выберите ее строку, чтобы отобразить экран Изменение конфигурации идентификатора приложения .

Создание сертификата для Центров уведомлений

Сертификат необходим, чтобы центр уведомлений мог работать со службами push-уведомлений Apple (APNS), и его можно предоставить одним из двух способов:

  1. Создание сертификата push-уведомлений p12, который можно отправить непосредственно в Центр уведомлений (исходный подход)

  2. Создание сертификата p8, который можно использовать для проверки подлинности на основе маркеров (новый и рекомендуемый подход)

Новый подход имеет ряд преимуществ, описанных в статье Проверка подлинности на основе токенов (HTTP/2) для APNS. Требуется меньшее количество шагов, но также требуется для конкретных сценариев. Однако для обоих подходов предусмотрены шаги, так как любой из них будет работать для целей этого руководства.

ВАРИАНТ 1. Создание сертификата push-уведомлений p12, который можно отправить непосредственно в Центр уведомлений
  1. На компьютере Mac запустите средство keychain Access. Его можно открыть из папки Utilities или Other на панели запуска.

  2. Выберите Keychain Access (Доступ к цепочке ключей), разверните узел Помощник по сертификатам, а затем выберите Запросить сертификат из центра сертификации.

    Запрос нового сертификата с помощью доступа к цепочке ключей

    Примечание

    По умолчанию keychain Access выбирает первый элемент в списке. Это может быть проблемой, если вы находитесь в категории Сертификаты , а центр сертификации Apple Worldwide Developer Relations Не является первым элементом в списке. Перед созданием CSR (запрос на подпись сертификата) убедитесь, что у вас есть неключеловой элемент или выбран ключ центра сертификации Apple Worldwide Developer Relations.

  3. Выберите адрес Email пользователя, введите общее имя, убедитесь, что задано значение Сохранено на диске, а затем нажмите кнопку Продолжить. Оставьте адрес ЦС Email пустым, так как он не требуется.

    Ожидаемые сведения о сертификате

  4. Введите имя файла запроса на подпись сертификата (CSR) в поле Сохранить как, выберите расположение в поле Где, а затем нажмите кнопку Сохранить.

    Выбор имени файла для сертификата

    Это действие сохраняет CSR-файл в выбранном расположении. Расположение по умолчанию — Desktop. Запомните расположение, выбранное для файла.

  5. Вернитесь на страницу Сертификаты, идентификаторы & Профилина портале подготовки iOS, прокрутите вниз до флажка Push-уведомления и выберите Настроить , чтобы создать сертификат.

    Страница

  6. Откроется окно Tls/SSL-сертификаты службы push-уведомлений Apple . Нажмите кнопку Создать сертификат в разделе Разработка TLS/SSL-сертификата .

    Кнопка

    Отобразится экран Создание сертификата .

    Примечание

    В этом руководстве используется сертификат разработки. Этот же процесс используется при регистрации производственного сертификата. Просто убедитесь, что при отправке уведомлений используется один и тот же тип сертификата.

  7. Выберите Выбрать файл, перейдите в расположение, в котором вы сохранили CSR-файл, а затем дважды щелкните имя сертификата, чтобы загрузить его. Затем нажмите кнопку Продолжить.

  8. Когда портал создаст сертификат, нажмите кнопку Скачать . Сохраните сертификат и запомните расположение, в котором он сохранен.

    Страница скачивания созданного сертификата

    Сертификат скачается и сохраняется на компьютере в папке "Загрузки ".

    Найдите файл сертификата в папке

    Примечание

    По умолчанию скачанный сертификат разработки называется aps_development.cer.

  9. Дважды щелкните скачанный сертификат push-уведомлений aps_development.cer. Это действие устанавливает новый сертификат в цепочке ключей, как показано на следующем рисунке:

    Список сертификатов для доступа к цепочке ключей с новым сертификатом

    Примечание

    Хотя имя в сертификате может отличаться, оно будет иметь префикс с помощью push-служб Apple Development iOS и соответствующего идентификатора пакета.

  10. В разделе Доступ к + цепочке ключейщелкните новый сертификат push-уведомлений, созданный в категории Сертификаты. Выберите Экспорт, присвойте файлу имя, выберите формат p12 и нажмите кнопку Сохранить.

    Экспорт сертификата в формате p12

    Вы можете защитить сертификат паролем, но пароль является необязательным. Нажмите кнопку ОК , чтобы обойти создание пароля. Запишите имя файла и расположение экспортированного сертификата p12. Они используются для включения проверки подлинности с помощью APN.

    Примечание

    Имя и расположение файла p12 могут отличаться от указанных в этом руководстве.

ВАРИАНТ 2. Создание сертификата p8, который можно использовать для проверки подлинности на основе маркеров
  1. Запишите следующие сведения:

    • Префикс идентификатора приложения (идентификатор команды)
    • Идентификатор пакета
  2. Вернитесь в раздел Сертификаты, Идентификаторы & Профили и щелкните Ключи.

    Примечание

    Если у вас уже есть ключ, настроенный для APNS, можно повторно использовать сертификат p8, скачанный сразу после его создания. В этом случае можно игнорировать шаги 35.

  3. Нажмите кнопку + (или кнопку Создать клавишу ), чтобы создать новую клавишу.

  4. Укажите подходящее значение имени ключа, а затем проверка параметр Службы push-уведомлений Apple (APNS) и нажмите кнопку Продолжить, а затем зарегистрировать на следующем экране.

  5. Нажмите кнопку Скачать , а затем переместите p8-файл (с префиксом AuthKey_) в безопасный локальный каталог, а затем нажмите кнопку Готово.

    Примечание

    Не забудьте сохранить файл p8 в безопасном месте (и сохранить резервную копию). После скачивания ключа его невозможно скачать повторно, так как копия сервера удаляется.

  6. В разделе Ключи щелкните созданный ключ (или существующий ключ, если вы решили использовать его).

  7. Запишите значение идентификатора ключа .

  8. Откройте сертификат p8 в подходящем приложении, например Visual Studio Code. Запишите значение ключа (между -----BEGIN PRIVATE KEY----- и -----END PRIVATE KEY-----).

    закрытый ключ -----BEGIN-----
    <key_value>
    -----КОНЕЧНЫЙ ЗАКРЫТЫЙ КЛЮЧ-----

    Примечание

    Это значение маркера , которое будет использоваться позже для настройки центра уведомлений.

В конце этих действий у вас должны быть следующие сведения для последующего использования в разделе Настройка центра уведомлений с помощью сведений об APNS:

  • Идентификатор команды (см. шаг 1)
  • Идентификатор пакета (см. шаг 1)
  • Идентификатор ключа (см. шаг 7)
  • Значение токена (значение ключа p8, полученное на шаге 8)

Создание профиля подготовки для приложения

  1. Вернитесь на портал подготовки iOS, выберите Сертификаты, Идентификаторы & Профили, в меню слева выберите Профили , а затем создайте + новый профиль. Откроется экран Регистрация нового профиля подготовки .

  2. Выберите Разработка приложений iOS в разделе Разработка в качестве типа профиля подготовки, а затем нажмите кнопку Продолжить.

    Список профилей подготовки

  3. Затем выберите созданный идентификатор приложения в раскрывающемся списке Идентификатор приложения и нажмите кнопку Продолжить.

    Выберите идентификатор приложения.

  4. В окне Выбор сертификатов выберите сертификат разработки, используемый для подписывания кода, и нажмите кнопку Продолжить.

    Примечание

    Этот сертификат не является сертификатом push-уведомлений, созданным на предыдущем шаге. Это ваш сертификат разработки. Если он не существует, его необходимо создать, так как это необходимое условие для работы с этим руководством. Сертификаты разработчиков можно создать на портале разработчика Apple, с помощью Xcode или Visual Studio.

  5. Вернитесь на страницу Сертификаты, идентификаторы & Профили , выберите Профили в меню слева, а затем создайте + новый профиль. Откроется экран Регистрация нового профиля подготовки .

  6. В окне Выбор сертификатов выберите созданный сертификат разработки. Затем нажмите кнопку Продолжить.

  7. Затем выберите устройства для тестирования и нажмите кнопку Продолжить.

  8. Наконец, выберите имя профиля в поле Имя профиля подготовки и нажмите кнопку Создать.

    Выбор имени профиля подготовки

  9. После создания нового профиля подготовки выберите Скачать. Запомните расположение, в котором оно сохранено.

  10. Перейдите к расположению профиля подготовки и дважды щелкните его, чтобы установить его на компьютере разработки.

Создание центра уведомлений

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

  1. Войдите в Azure.

  2. Щелкните Создать ресурс, найдите и выберите Центр уведомлений, а затем нажмите кнопку Создать.

  3. Обновите следующие поля, а затем нажмите кнопку Создать:

    ОСНОВНЫЕ СВЕДЕНИЯ

    Подписки: Выберите целевую подписку из раскрывающегося списка.
    Группа ресурсов: Создание новой группы ресурсов (или выбор существующей)

    СВЕДЕНИЯ О ПРОСТРАНСТВЕ ИМЕН

    Пространство имен концентратора уведомлений: Введите глобально уникальное имя для пространства имен центра уведомлений .

    Примечание

    Убедитесь, что для этого поля выбран параметр Создать .

    СВЕДЕНИЯ О КОНЦЕНТРАТОРЕ УВЕДОМЛЕНИЙ

    Центр уведомлений: Введите имя центра уведомлений.
    Расположение: Выберите подходящее расположение из раскрывающегося списка
    Ценовая категория: Оставьте параметр по умолчанию "Бесплатный "

    Примечание

    Если вы не достигли максимального количества концентраторов на уровне "Бесплатный".

  4. После подготовки центра уведомлений перейдите к ресурсу.

  5. Перейдите к новому центру уведомлений.

  6. Выберите Политики доступа в списке (в разделе УПРАВЛЕНИЕ).

  7. Запишите значения имени политики вместе с соответствующими значениями строки подключения .

Настройка центра уведомлений с помощью сведений об APNS

В разделе Службы уведомлений выберите Apple , а затем выполните соответствующие действия в зависимости от подхода, выбранного ранее в разделе Создание сертификата для Центров уведомлений .

Примечание

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

ВАРИАНТ 1. Использование сертификата push-уведомлений .p12

  1. Выберите Сертификат.

  2. Щелкните значок файла.

  3. Выберите P12-файл, экспортируемый ранее, и нажмите кнопку Открыть.

  4. При необходимости укажите правильный пароль.

  5. Выберите Режим песочницы .

  6. Нажмите кнопку Сохранить.

ВАРИАНТ 2. Использование проверки подлинности на основе маркеров

  1. Выберите Токен.

  2. Введите следующие значения, полученные ранее:

    • Идентификатор ключа
    • Идентификатор пакета
    • Идентификатор команды
    • Маркер
  3. Выберите Песочница.

  4. Нажмите кнопку Сохранить.

Настройка центра уведомлений с помощью сведений О FCM

  1. Выберите Google (GCM/FCM) в разделе Параметры в меню слева.
  2. Введите ключ сервера , записанный в консоли Google Firebase.
  3. Выберите Сохранить на панели инструментов.

Создание серверного приложения веб-API ASP.NET Core

В этом разделе описано, как создать серверную часть веб-API ASP.NET Core для обработки регистрации устройств и отправки уведомлений в мобильное приложение Xamarin.Forms.

Создание веб-проекта

  1. В Visual Studio выберите Файл>Создать решение.

  2. ВыберитеПриложение>.NET Core>ASP.NET Core>API>Далее.

  3. В диалоговом окне Настройка нового веб-API ASP.NET Core выберите Целевая платформа.NET Core 3.1.

  4. Введите PushDemoApi в поле Имя проекта , а затем выберите Создать.

  5. Запустите отладку (ввод команды), + чтобы протестировать шаблонное приложение.

    Примечание

    Шаблонное приложение настроено для использования WeatherForecastController в качестве launchUrl. Этот параметр задается в разделе Свойства>launchSettings.json.

    Если появится запрос на сообщение о недопустимом сертификате разработки, найдено следующее сообщение:

    1. Нажмите кнопку Да , чтобы согласиться на запуск средства dotnet dev-certs https, чтобы устранить эту проблему. Затем средство dotnet dev-certs https предложит ввести пароль для сертификата и пароль для цепочки ключей.

    2. Нажмите кнопку Да при появлении запроса Установить и доверять новому сертификату, а затем введите пароль для цепочки ключей.

  6. Разверните папку Контроллеры и удалите WeatherForecastController.cs.

  7. Удаление WeatherForecast.cs.

  8. Настройте значения локальной конфигурации с помощью средства Диспетчер секретов. Разделение секретов из решения гарантирует, что они не будут в конечном итоге в системе управления версиями. Откройте терминал , а затем перейдите в каталог файла проекта и выполните следующие команды:

    dotnet user-secrets init
    dotnet user-secrets set "NotificationHub:Name" <value>
    dotnet user-secrets set "NotificationHub:ConnectionString" <value>
    

    Замените значения заполнителей собственным именем центра уведомлений и значениями строка подключения. Вы записали их в разделе Создание центра уведомлений . В противном случае их можно найти в Azure.

    NotificationHub:Name:
    См. раздел Имя в сводке основных компонентов в верхней части окна Обзор.

    NotificationHub:ConnectionString:
    См . раздел DefaultFullSharedAccessSignature в разделе Политики доступа.

    Примечание

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

Проверка подлинности клиентов с помощью ключа API (необязательно)

Ключи API не так безопасны, как маркеры, но их будет достаточно для целей этого руководства. Ключ API можно легко настроить с помощью ПО промежуточного слоя ASP.NET.

  1. Добавьте ключ API в значения локальной конфигурации.

    dotnet user-secrets set "Authentication:ApiKey" <value>
    

    Примечание

    Замените значение заполнителя собственным и запишите его.

  2. Управления + Щелкните проект PushDemoApi , выберите Создать папку в меню Добавить , а затем щелкните Добавить с помощью проверки подлинности в качестве имени папки.

  3. Управления + Щелкните папку Authentication (Проверка подлинности ), а затем выберите Создать файл... в меню Добавить .

  4. Выберите Общий>пустой класс, введите ApiKeyAuthOptions.cs в поле Имя, а затем нажмите кнопку Создать , добавив следующую реализацию.

    using Microsoft.AspNetCore.Authentication;
    
    namespace PushDemoApi.Authentication
    {
        public class ApiKeyAuthOptions : AuthenticationSchemeOptions
        {
            public const string DefaultScheme = "ApiKey";
            public string Scheme => DefaultScheme;
            public string ApiKey { get; set; }
        }
    }
    
  5. Добавьте еще один пустой класс в папку Authenticationс именем ApiKeyAuthHandler.cs, а затем добавьте следующую реализацию.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Claims;
    using System.Text.Encodings.Web;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    
    namespace PushDemoApi.Authentication
    {
        public class ApiKeyAuthHandler : AuthenticationHandler<ApiKeyAuthOptions>
        {
            const string ApiKeyIdentifier = "apikey";
    
            public ApiKeyAuthHandler(
                IOptionsMonitor<ApiKeyAuthOptions> options,
                ILoggerFactory logger,
                UrlEncoder encoder,
                ISystemClock clock)
                : base(options, logger, encoder, clock) {}
    
            protected override Task<AuthenticateResult> HandleAuthenticateAsync()
            {
                string key = string.Empty;
    
                if (Request.Headers[ApiKeyIdentifier].Any())
                {
                    key = Request.Headers[ApiKeyIdentifier].FirstOrDefault();
                }
                else if (Request.Query.ContainsKey(ApiKeyIdentifier))
                {
                    if (Request.Query.TryGetValue(ApiKeyIdentifier, out var queryKey))
                        key = queryKey;
                }
    
                if (string.IsNullOrWhiteSpace(key))
                    return Task.FromResult(AuthenticateResult.Fail("No api key provided"));
    
                if (!string.Equals(key, Options.ApiKey, StringComparison.Ordinal))
                    return Task.FromResult(AuthenticateResult.Fail("Invalid api key."));
    
                var identities = new List<ClaimsIdentity> {
                    new ClaimsIdentity("ApiKeyIdentity")
                };
    
                var ticket = new AuthenticationTicket(
                    new ClaimsPrincipal(identities), Options.Scheme);
    
                return Task.FromResult(AuthenticateResult.Success(ticket));
            }
        }
    }
    

    Примечание

    Обработчик проверки подлинности — это тип, реализующий поведение схемы, в данном случае настраиваемой схемы ключа API.

  6. Добавьте еще один пустой класс в папку Authenticationс именем ApiKeyAuthenticationBuilderExtensions.cs, а затем добавьте следующую реализацию.

    using System;
    using Microsoft.AspNetCore.Authentication;
    
    namespace PushDemoApi.Authentication
    {
        public static class AuthenticationBuilderExtensions
        {
            public static AuthenticationBuilder AddApiKeyAuth(
                this AuthenticationBuilder builder,
                Action<ApiKeyAuthOptions> configureOptions)
            {
                return builder
                    .AddScheme<ApiKeyAuthOptions, ApiKeyAuthHandler>(
                        ApiKeyAuthOptions.DefaultScheme,
                        configureOptions);
            }
        }
    }
    

    Примечание

    Этот метод расширения упрощает код конфигурации ПО промежуточного слоя в Startup.cs что делает его более удобочитаемым и в целом проще для выполнения.

  7. В Startup.cs обновите метод ConfigureServices , чтобы настроить проверку подлинности ключа API под вызовом служб. Метод AddControllers .

    using PushDemoApi.Authentication;
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    
        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = ApiKeyAuthOptions.DefaultScheme;
            options.DefaultChallengeScheme = ApiKeyAuthOptions.DefaultScheme;
        }).AddApiKeyAuth(Configuration.GetSection("Authentication").Bind);
    }
    
  8. В Startup.cs обновите метод Configure , чтобы вызвать методы расширения UseAuthentication и UseAuthorization в IApplicationBuilder приложения. Убедитесь, что эти методы вызываются после UseRouting и перед приложением. UseEndpoints.

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.UseHttpsRedirection();
    
        app.UseRouting();
    
        app.UseAuthentication();
    
        app.UseAuthorization();
    
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
    

    Примечание

    Вызов Метода UseAuthentication регистрирует ПО промежуточного слоя, которое использует ранее зарегистрированные схемы проверки подлинности (из Раздела ConfigureServices). Это необходимо вызывать перед любым ПО промежуточного слоя, которое зависит от проверки подлинности пользователей.

Добавление зависимостей и настройка служб

ASP.NET Core поддерживает шаблон проектирования программного обеспечения внедрения зависимостей( DI), который является методом достижения инверсии управления (IoC) между классами и их зависимостями.

Использование центра уведомлений и пакета SDK центров уведомлений для внутренних операций инкапсулируется в службе. Служба регистрируется и становится доступной с помощью подходящей абстракции.

  1. Управления + Щелкните папку Зависимости , а затем выберите Управление пакетами NuGet....

  2. Выполните поиск по запросу Microsoft.Azure.NotificationHubs и убедитесь, что он установлен.

  3. Щелкните Добавить пакеты, а затем нажмите кнопку Принять при появлении запроса на принятие условий лицензионного соглашения.

  4. Управления + Щелкните проект PushDemoApi , выберите Создать папку в меню Добавить , а затем щелкните Добавить , используя models в качестве имени папки.

  5. Управления + Щелкните папку Модели , а затем выберите Создать файл... в меню Добавить .

  6. Выберите Общий>пустой класс, введите PushTemplates.cs в поле Имя, а затем нажмите кнопку Создать , добавив следующую реализацию.

    namespace PushDemoApi.Models
    {
        public class PushTemplates
        {
            public class Generic
            {
                public const string Android = "{ \"notification\": { \"title\" : \"PushDemo\", \"body\" : \"$(alertMessage)\"}, \"data\" : { \"action\" : \"$(alertAction)\" } }";
                public const string iOS = "{ \"aps\" : {\"alert\" : \"$(alertMessage)\"}, \"action\" : \"$(alertAction)\" }";
            }
    
            public class Silent
            {
                public const string Android = "{ \"data\" : {\"message\" : \"$(alertMessage)\", \"action\" : \"$(alertAction)\"} }";
                public const string iOS = "{ \"aps\" : {\"content-available\" : 1, \"apns-priority\": 5, \"sound\" : \"\", \"badge\" : 0}, \"message\" : \"$(alertMessage)\", \"action\" : \"$(alertAction)\" }";
            }
        }
    }
    

    Примечание

    Этот класс содержит полезные данные уведомлений с токенами для универсальных и автоматических уведомлений, необходимых для этого сценария. Полезные данные определяются за пределами установки , чтобы позволить экспериментировать без необходимости обновления существующих установок через службу. Обработка изменений в установках таким образом не область в этом руководстве. Для рабочей среды рассмотрите возможность использования пользовательских шаблонов.

  7. Добавьте еще один пустой класс в папку Modelsс именем DeviceInstallation.cs, а затем добавьте следующую реализацию.

    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    
    namespace PushDemoApi.Models
    {
        public class DeviceInstallation
        {
            [Required]
            public string InstallationId { get; set; }
    
            [Required]
            public string Platform { get; set; }
    
            [Required]
            public string PushChannel { get; set; }
    
            public IList<string> Tags { get; set; } = Array.Empty<string>();
        }
    }
    
  8. Добавьте еще один пустой класс в папку Modelsс именем NotificationRequest.cs, а затем добавьте следующую реализацию.

    using System;
    
    namespace PushDemoApi.Models
    {
        public class NotificationRequest
        {
            public string Text { get; set; }
            public string Action { get; set; }
            public string[] Tags { get; set; } = Array.Empty<string>();
            public bool Silent { get; set; }
        }
    }
    
  9. Добавьте еще один пустой класс в папку Modelsс именем NotificationHubOptions.cs, а затем добавьте следующую реализацию.

    using System.ComponentModel.DataAnnotations;
    
    namespace PushDemoApi.Models
    {
        public class NotificationHubOptions
        {
            [Required]
            public string Name { get; set; }
    
            [Required]
            public string ConnectionString { get; set; }
        }
    }
    
  10. Добавьте новую папку в проект PushDemoApiс именем Службы.

  11. Добавьте пустой интерфейс в папку Servicesс именем INotificationService.cs, а затем добавьте следующую реализацию.

    using System.Threading;
    using System.Threading.Tasks;
    using PushDemoApi.Models;
    
    namespace PushDemoApi.Services
    {
        public interface INotificationService
        {
            Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token);
            Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token);
            Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token);
        }
    }
    
  12. Добавьте пустой класс в папку Servicesс именем NotificationHubsService.cs, а затем добавьте следующий код для реализации интерфейса INotificationService :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Options;
    using PushDemoApi.Models;
    
    namespace PushDemoApi.Services
    {
        public class NotificationHubService : INotificationService
        {
            readonly NotificationHubClient _hub;
            readonly Dictionary<string, NotificationPlatform> _installationPlatform;
            readonly ILogger<NotificationHubService> _logger;
    
            public NotificationHubService(IOptions<NotificationHubOptions> options, ILogger<NotificationHubService> logger)
            {
                _logger = logger;
                _hub = NotificationHubClient.CreateClientFromConnectionString(
                    options.Value.ConnectionString,
                    options.Value.Name);
    
                _installationPlatform = new Dictionary<string, NotificationPlatform>
                {
                    { nameof(NotificationPlatform.Apns).ToLower(), NotificationPlatform.Apns },
                    { nameof(NotificationPlatform.Fcm).ToLower(), NotificationPlatform.Fcm }
                };
            }
    
            public async Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token)
            {
                if (string.IsNullOrWhiteSpace(deviceInstallation?.InstallationId) ||
                    string.IsNullOrWhiteSpace(deviceInstallation?.Platform) ||
                    string.IsNullOrWhiteSpace(deviceInstallation?.PushChannel))
                    return false;
    
                var installation = new Installation()
                {
                    InstallationId = deviceInstallation.InstallationId,
                    PushChannel = deviceInstallation.PushChannel,
                    Tags = deviceInstallation.Tags
                };
    
                if (_installationPlatform.TryGetValue(deviceInstallation.Platform, out var platform))
                    installation.Platform = platform;
                else
                    return false;
    
                try
                {
                    await _hub.CreateOrUpdateInstallationAsync(installation, token);
                }
                catch
                {
                    return false;
                }
    
                return true;
            }
    
            public async Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token)
            {
                if (string.IsNullOrWhiteSpace(installationId))
                    return false;
    
                try
                {
                    await _hub.DeleteInstallationAsync(installationId, token);
                }
                catch
                {
                    return false;
                }
    
                return true;
            }
    
            public async Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token)
            {
                if ((notificationRequest.Silent &&
                    string.IsNullOrWhiteSpace(notificationRequest?.Action)) ||
                    (!notificationRequest.Silent &&
                    (string.IsNullOrWhiteSpace(notificationRequest?.Text)) ||
                    string.IsNullOrWhiteSpace(notificationRequest?.Action)))
                    return false;
    
                var androidPushTemplate = notificationRequest.Silent ?
                    PushTemplates.Silent.Android :
                    PushTemplates.Generic.Android;
    
                var iOSPushTemplate = notificationRequest.Silent ?
                    PushTemplates.Silent.iOS :
                    PushTemplates.Generic.iOS;
    
                var androidPayload = PrepareNotificationPayload(
                    androidPushTemplate,
                    notificationRequest.Text,
                    notificationRequest.Action);
    
                var iOSPayload = PrepareNotificationPayload(
                    iOSPushTemplate,
                    notificationRequest.Text,
                    notificationRequest.Action);
    
                try
                {
                    if (notificationRequest.Tags.Length == 0)
                    {
                        // This will broadcast to all users registered in the notification hub
                        await SendPlatformNotificationsAsync(androidPayload, iOSPayload, token);
                    }
                    else if (notificationRequest.Tags.Length <= 20)
                    {
                        await SendPlatformNotificationsAsync(androidPayload, iOSPayload, notificationRequest.Tags, token);
                    }
                    else
                    {
                        var notificationTasks = notificationRequest.Tags
                            .Select((value, index) => (value, index))
                            .GroupBy(g => g.index / 20, i => i.value)
                            .Select(tags => SendPlatformNotificationsAsync(androidPayload, iOSPayload, tags, token));
    
                        await Task.WhenAll(notificationTasks);
                    }
    
                    return true;
                }
                catch (Exception e)
                {
                    _logger.LogError(e, "Unexpected error sending notification");
                    return false;
                }
            }
    
            string PrepareNotificationPayload(string template, string text, string action) => template
                .Replace("$(alertMessage)", text, StringComparison.InvariantCulture)
                .Replace("$(alertAction)", action, StringComparison.InvariantCulture);
    
            Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, CancellationToken token)
            {
                var sendTasks = new Task[]
                {
                    _hub.SendFcmNativeNotificationAsync(androidPayload, token),
                    _hub.SendAppleNativeNotificationAsync(iOSPayload, token)
                };
    
                return Task.WhenAll(sendTasks);
            }
    
            Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, IEnumerable<string> tags, CancellationToken token)
            {
                var sendTasks = new Task[]
                {
                    _hub.SendFcmNativeNotificationAsync(androidPayload, tags, token),
                    _hub.SendAppleNativeNotificationAsync(iOSPayload, tags, token)
                };
    
                return Task.WhenAll(sendTasks);
            }
        }
    }
    

    Примечание

    Выражение тега, предоставленное для SendTemplateNotificationAsync , ограничено 20 тегами. Для большинства операторов это ограничение ограничено 6, но в данном случае выражение содержит только OR (||). Если в запросе более 20 тегов, их необходимо разделить на несколько запросов. Дополнительные сведения см. в документации по выражениям маршрутизации и тегов .

  13. В Startup.cs обновите метод ConfigureServices , чтобы добавить NotificationHubsService в качестве одноэлементной реализации INotificationService.

    
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
    public void ConfigureServices(IServiceCollection services)
    {
        ...
    
        services.AddSingleton<INotificationService, NotificationHubService>();
    
        services.AddOptions<NotificationHubOptions>()
            .Configure(Configuration.GetSection("NotificationHub").Bind)
            .ValidateDataAnnotations();
    }
    

Создание API уведомлений

  1. Управления + Щелкните папку Контроллеры , а затем выберите Новый файл... в меню Добавить .

  2. Выберите ASP.NET Core>Веб-класс контроллера API, введите NotificationsController в поле Имя, а затем нажмите кнопку Создать.

    Примечание

    Если вы используете Visual Studio 2019, выберите шаблон Контроллер API с действиями чтения и записи .

  3. Добавьте следующие пространства имен в начало файла.

    using System.ComponentModel.DataAnnotations;
    using System.Net;
    using System.Threading;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Mvc;
    using PushDemoApi.Models;
    using PushDemoApi.Services;
    
  4. Обновите шаблонный контроллер, чтобы он был производным от ControllerBase и дополнен атрибутом ApiController .

    [ApiController]
    [Route("api/[controller]")]
    public class NotificationsController : ControllerBase
    {
        // Templated methods here
    }
    

    Примечание

    Базовый класс Controller обеспечивает поддержку представлений, но в этом случае это не требуется, поэтому вместо него можно использовать ControllerBase . Если вы используете Visual Studio 2019, этот шаг можно пропустить.

  5. Если вы решили выполнить проверку подлинности клиентов с помощью ключа API , необходимо также украсить NotificationsController атрибутом Authorize .

    [Authorize]
    
  6. Обновите конструктор, чтобы принять зарегистрированный экземпляр INotificationService в качестве аргумента и назначить его члену только для чтения.

    readonly INotificationService _notificationService;
    
    public NotificationsController(INotificationService notificationService)
    {
        _notificationService = notificationService;
    }
    
  7. В launchSettings.json (в папке Свойства) измените launchUrl с weatherforecast на api/notifications, чтобы он соответствовал URL-адресу, указанному в атрибуте Маршрута RegistrationsController.

  8. Запустите отладку (ввод команды + ), чтобы убедиться, что приложение работает с новым NotificationsController , и возвращает состояние 401 Не авторизовано .

    Примечание

    Visual Studio может не запускать приложение автоматически в браузере. С этого момента вы будете использовать Postman для тестирования API.

  9. На новой вкладке Postman задайте для запроса значение GET. Введите адрес ниже, заменив заполнитель <applicationUrl> https applicationUrl в разделе Свойства>launchSettings.json.

    <applicationUrl>/api/notifications
    

    Примечание

    ApplicationUrl должен иметь значение "https://localhost:5001" для профиля по умолчанию. Если вы используете СЛУЖБЫ IIS (по умолчанию в Visual Studio 2019 в Windows), следует использовать applicationUrl , указанный в элементе iisSettings . Если адрес неправильный, вы получите ответ 404.

  10. Если вы решили выполнить проверку подлинности клиентов с помощью ключа API , обязательно настройте заголовки запросов, чтобы включить значение apikey .

    Ключ Значение
    apikey <your_api_key>
  11. Нажмите кнопку Отправить .

    Примечание

    Вы должны получить состояние 200 ОК с некоторым содержимым JSON .

    Если появится предупреждение о проверке SSL-сертификата , можно отключить параметр Postman проверки SSL-сертификата в параметрах.

  12. Замените методы шаблонного класса в NotificationsController.cs следующим кодом.

    [HttpPut]
    [Route("installations")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)]
    public async Task<IActionResult> UpdateInstallation(
        [Required]DeviceInstallation deviceInstallation)
    {
        var success = await _notificationService
            .CreateOrUpdateInstallationAsync(deviceInstallation, HttpContext.RequestAborted);
    
        if (!success)
            return new UnprocessableEntityResult();
    
        return new OkResult();
    }
    
    [HttpDelete()]
    [Route("installations/{installationId}")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)]
    public async Task<ActionResult> DeleteInstallation(
        [Required][FromRoute]string installationId)
    {
        var success = await _notificationService
            .DeleteInstallationByIdAsync(installationId, CancellationToken.None);
    
        if (!success)
            return new UnprocessableEntityResult();
    
        return new OkResult();
    }
    
    [HttpPost]
    [Route("requests")]
    [ProducesResponseType((int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)]
    public async Task<IActionResult> RequestPush(
        [Required]NotificationRequest notificationRequest)
    {
        if ((notificationRequest.Silent &&
            string.IsNullOrWhiteSpace(notificationRequest?.Action)) ||
            (!notificationRequest.Silent &&
            string.IsNullOrWhiteSpace(notificationRequest?.Text)))
            return new BadRequestResult();
    
        var success = await _notificationService
            .RequestNotificationAsync(notificationRequest, HttpContext.RequestAborted);
    
        if (!success)
            return new UnprocessableEntityResult();
    
        return new OkResult();
    }
    

Создание приложения API

Теперь вы создадите приложение API в Служба приложений Azure для размещения серверной службы.

  1. Войдите в портал Azure.

  2. Щелкните Создать ресурс, найдите и выберите Приложение API, а затем нажмите кнопку Создать.

  3. Обновите следующие поля, а затем нажмите кнопку Создать.

    Имя приложения:
    Введите глобально уникальное имя для приложения API.

    Подписки:
    Выберите ту же целевую подписку, в которую вы создали центр уведомлений.

    Группа ресурсов:
    Выберите ту же группу ресурсов, в которую вы создали центр уведомлений.

    План или расположение Служба приложений:
    Создание плана Служба приложений

    Примечание

    Перейдите с параметра по умолчанию на план, который включает поддержку SSL . В противном случае вам потребуется выполнить соответствующие действия при работе с мобильным приложением, чтобы предотвратить блокировку HTTP-запросов .

    Application Insights:
    Оставьте предложенный параметр (новый ресурс будет создан с этим именем) или выберите существующий ресурс.

  4. После подготовки приложения API перейдите к ресурсу.

  5. Запишите свойство URL в сводке основных компонентов в верхней части обзора. Этот URL-адрес — это серверная конечная точка , которая будет использоваться далее в этом руководстве.

    Примечание

    В URL-адресе используется указанное ранее имя приложения API в формате https://<app_name>.azurewebsites.net.

  6. Выберите Конфигурация в списке (в разделе Параметры).

  7. Для каждого из приведенных ниже параметров щелкните Новый параметр приложения , чтобы ввести имя и значение, а затем нажмите кнопку ОК.

    Имя Значение
    Authentication:ApiKey <api_key_value>
    NotificationHub:Name <hub_name_value>
    NotificationHub:ConnectionString <hub_connection_string_value>

    Примечание

    Это те же параметры, которые вы определили ранее в параметрах пользователя. Вы сможете скопировать их. Параметр Authentication:ApiKey требуется только в том случае, если вы решили выполнить проверку подлинности клиентов с помощью ключа API . Для рабочих сценариев можно просмотреть такие варианты, как Azure KeyVault. Они были добавлены в качестве параметров приложения для простоты в данном случае.

  8. После добавления всех параметров приложения нажмите кнопку Сохранить и продолжить.

Публикация серверной службы

Затем разверните приложение в приложении API, чтобы сделать его доступным со всех устройств.

Примечание

Следующие действия относятся к Visual Studio для Mac. Если вы используете Visual Studio 2019 в Windows, поток публикации будет другим. См. статью Публикация в Служба приложений Azure в Windows.

  1. Измените конфигурацию с Отладка на Выпуск , если вы еще этого не сделали.

  2. Управления + Щелкните проект PushDemoApi , а затем выберите Опубликовать в Azure... в меню Опубликовать .

  3. Следуйте потоку проверки подлинности при появлении соответствующего запроса. Используйте учетную запись, которая использовалась в предыдущем разделе создание приложения API .

  4. Выберите ранее созданное приложение API Служба приложений Azure в списке в качестве целевого объекта публикации и нажмите кнопку Опубликовать.

После завершения работы мастера приложение будет опубликовано в Azure, а затем откроется приложение. Запишите URL-адрес , если вы еще этого не сделали. Этот URL-адрес — это серверная конечная точка , которая будет использоваться далее в этом руководстве.

Проверка опубликованного API

  1. В Postman откройте новую вкладку, задайте для запроса значение PUT и введите адрес ниже. Замените заполнитель базовым адресом, который вы записали в предыдущем разделе публикации серверной службы .

    https://<app_name>.azurewebsites.net/api/notifications/installations
    

    Примечание

    Базовый адрес должен быть в формате https://<app_name>.azurewebsites.net/

  2. Если вы решили выполнить проверку подлинности клиентов с помощью ключа API , обязательно настройте заголовки запросов, чтобы включить значение apikey .

    Ключ Значение
    apikey <your_api_key>
  3. Выберите необработанный параметр для текста, затем выберите JSON из списка параметров формата и включите заполнитель содержимого JSON :

    {}
    
  4. Нажмите кнопку Отправить.

    Примечание

    Вы должны получить состояние 422 UnprocessableEntity от службы.

  5. Выполните шаги 1–4 еще раз, но на этот раз укажите конечную точку запросов, чтобы убедиться, что вы получили ответ 400 Bad Request .

    https://<app_name>.azurewebsites.net/api/notifications/requests
    

Примечание

Пока невозможно протестировать API с использованием допустимых данных запроса, так как для этого потребуются сведения о конкретной платформе из клиентского мобильного приложения.

Создание кроссплатформенного приложения Xamarin.Forms

В этом разделе описано, как создать мобильное приложение Xamarin.Forms , реализующее push-уведомления кроссплатформенным способом.

Это позволяет зарегистрировать и отменить регистрацию в центре уведомлений через созданную вами серверную службу.

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

Примечание

Как правило, действия регистрации (и отмены регистрации) выполняются в течение соответствующего момента жизненного цикла приложения (или, возможно, в рамках первого запуска) без явной регистрации или отмены регистрации пользователей. Однако в этом примере потребуется явный ввод данных пользователем, чтобы упростить изучение и тестирование этой функции.

Создание решения Xamarin.Forms

  1. В Visual Studio создайте новое решение Xamarin.Forms , используя приложение Blank Forms в качестве шаблона и введя PushDemo в качестве имени проекта.

    Примечание

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

  2. Управления + Щелкните решение PushDemo , а затем выберите Обновить пакеты NuGet.

  3. Управления + Щелкните решение PushDemo , а затем выберите Управление пакетами NuGet...

  4. Выполните поиск по запросу Newtonsoft.Json и убедитесь, что он установлен.

  5. Щелкните Добавить пакеты, а затем нажмите кнопку Принять при появлении запроса на принятие условий лицензионного соглашения.

  6. Создайте и запустите приложение на каждой целевой платформе (ввод команды + ), чтобы протестировать шаблонное приложение, работающее на ваших устройствах.

Реализация кроссплатформенных компонентов

  1. Управления + Щелкните проект PushDemo , выберите Создать папку в меню Добавить , а затем щелкните Добавить с помощью моделей в качестве имени папки.

  2. Управления + Щелкните папку Модели , а затем выберите Создать файл... в меню Добавить .

  3. Выберите Общий>Пустой класс, введите DeviceInstallation.cs, а затем добавьте следующую реализацию.

    using System.Collections.Generic;
    using Newtonsoft.Json;
    
    namespace PushDemo.Models
    {
        public class DeviceInstallation
        {
            [JsonProperty("installationId")]
            public string InstallationId { get; set; }
    
            [JsonProperty("platform")]
            public string Platform { get; set; }
    
            [JsonProperty("pushChannel")]
            public string PushChannel { get; set; }
    
            [JsonProperty("tags")]
            public List<string> Tags { get; set; } = new List<string>();
        }
    }
    
  4. Добавьте пустое перечисление в папку Modelsс именем PushDemoAction.cs со следующей реализацией.

    namespace PushDemo.Models
    {
        public enum PushDemoAction
        {
            ActionA,
            ActionB
        }
    }
    
  5. Добавьте новую папку в проект PushDemoс именем Services , а затем добавьте пустой класс в папку с именем ServiceContainer.cs со следующей реализацией.

    using System;
    using System.Collections.Generic;
    
    namespace PushDemo.Services
    {
       public static class ServiceContainer
       {
           static readonly Dictionary<Type, Lazy<object>> services
               = new Dictionary<Type, Lazy<object>>();
    
           public static void Register<T>(Func<T> function)
               => services[typeof(T)] = new Lazy<object>(() => function());
    
           public static T Resolve<T>()
               => (T)Resolve(typeof(T));
    
           public static object Resolve(Type type)
           {
               {
                   if (services.TryGetValue(type, out var service))
                       return service.Value;
    
                   throw new KeyNotFoundException($"Service not found for type '{type}'");
               }
           }
       }
    }
    

    Примечание

    Это сокращенная версия класса ServiceContainer из репозитория XamCAT . Он будет использоваться в качестве легковесного контейнера IoC (inversion of Control).

  6. Добавьте пустой интерфейс в папку Servicesс именем IDeviceInstallationService.cs, а затем добавьте следующий код.

    using PushDemo.Models;
    
    namespace PushDemo.Services
    {
        public interface IDeviceInstallationService
        {
            string Token { get; set; }
            bool NotificationsSupported { get; }
            string GetDeviceId();
            DeviceInstallation GetDeviceInstallation(params string[] tags);
        }
    }
    

    Примечание

    Этот интерфейс будет реализован и загрузится каждым целевым объектом позже, чтобы предоставить функциональные возможности для конкретной платформы и сведения о установке устройства , необходимые серверной службе.

  7. Добавьте еще один пустой интерфейс в папку Servicesс именем INotificationRegistrationService.cs, а затем добавьте следующий код.

    using System.Threading.Tasks;
    
    namespace PushDemo.Services
    {
        public interface INotificationRegistrationService
        {
            Task DeregisterDeviceAsync();
            Task RegisterDeviceAsync(params string[] tags);
            Task RefreshRegistrationAsync();
        }
    }
    

    Примечание

    Это будет обрабатывать взаимодействие между клиентом и серверной службой.

  8. Добавьте еще один пустой интерфейс в папку Servicesс именем INotificationActionService.cs, а затем добавьте следующий код.

    namespace PushDemo.Services
    {
        public interface INotificationActionService
        {
            void TriggerAction(string action);
        }
    }
    

    Примечание

    Используется в качестве простого механизма для централизации обработки действий уведомлений.

  9. Добавьте пустой интерфейс в папку Servicesс именем IPushDemoNotificationActionService.cs , который является производным от INotificationActionService, со следующей реализацией.

    using System;
    using PushDemo.Models;
    
    namespace PushDemo.Services
    {
        public interface IPushDemoNotificationActionService : INotificationActionService
        {
            event EventHandler<PushDemoAction> ActionTriggered;
        }
    }
    

    Примечание

    Этот тип предназначен для приложения PushDemo и использует перечисление PushDemoAction для определения действия, которое активируется строго типизированным образом.

  10. Добавьте пустой класс в папку Servicesс именем NotificationRegistrationService.cs реализации INotificationRegistrationService с помощью следующего кода.

    using System;
    using System.Net.Http;
    using System.Text;
    using System.Threading.Tasks;
    using Newtonsoft.Json;
    using PushDemo.Models;
    using Xamarin.Essentials;
    
    namespace PushDemo.Services
    {
        public class NotificationRegistrationService : INotificationRegistrationService
        {
            const string RequestUrl = "api/notifications/installations";
            const string CachedDeviceTokenKey = "cached_device_token";
            const string CachedTagsKey = "cached_tags";
    
            string _baseApiUrl;
            HttpClient _client;
            IDeviceInstallationService _deviceInstallationService;
    
            public NotificationRegistrationService(string baseApiUri, string apiKey)
            {
                _client = new HttpClient();
                _client.DefaultRequestHeaders.Add("Accept", "application/json");
                _client.DefaultRequestHeaders.Add("apikey", apiKey);
    
                _baseApiUrl = baseApiUri;
            }
    
            IDeviceInstallationService DeviceInstallationService
                => _deviceInstallationService ??
                    (_deviceInstallationService = ServiceContainer.Resolve<IDeviceInstallationService>());
    
            public async Task DeregisterDeviceAsync()
            {
                var cachedToken = await SecureStorage.GetAsync(CachedDeviceTokenKey)
                    .ConfigureAwait(false);
    
                if (cachedToken == null)
                    return;
    
                var deviceId = DeviceInstallationService?.GetDeviceId();
    
                if (string.IsNullOrWhiteSpace(deviceId))
                    throw new Exception("Unable to resolve an ID for the device.");
    
                await SendAsync(HttpMethod.Delete, $"{RequestUrl}/{deviceId}")
                    .ConfigureAwait(false);
    
                SecureStorage.Remove(CachedDeviceTokenKey);
                SecureStorage.Remove(CachedTagsKey);
            }
    
            public async Task RegisterDeviceAsync(params string[] tags)
            {
                var deviceInstallation = DeviceInstallationService?.GetDeviceInstallation(tags);
    
                await SendAsync<DeviceInstallation>(HttpMethod.Put, RequestUrl, deviceInstallation)
                    .ConfigureAwait(false);
    
                await SecureStorage.SetAsync(CachedDeviceTokenKey, deviceInstallation.PushChannel)
                    .ConfigureAwait(false);
    
                await SecureStorage.SetAsync(CachedTagsKey, JsonConvert.SerializeObject(tags));
            }
    
            public async Task RefreshRegistrationAsync()
            {
                var cachedToken = await SecureStorage.GetAsync(CachedDeviceTokenKey)
                    .ConfigureAwait(false);
    
                var serializedTags = await SecureStorage.GetAsync(CachedTagsKey)
                    .ConfigureAwait(false);
    
                if (string.IsNullOrWhiteSpace(cachedToken) ||
                    string.IsNullOrWhiteSpace(serializedTags) ||
                    string.IsNullOrWhiteSpace(DeviceInstallationService.Token) ||
                    cachedToken == DeviceInstallationService.Token)
                    return;
    
                var tags = JsonConvert.DeserializeObject<string[]>(serializedTags);
    
                await RegisterDeviceAsync(tags);
            }
    
            async Task SendAsync<T>(HttpMethod requestType, string requestUri, T obj)
            {
                string serializedContent = null;
    
                await Task.Run(() => serializedContent = JsonConvert.SerializeObject(obj))
                    .ConfigureAwait(false);
    
                await SendAsync(requestType, requestUri, serializedContent);
            }
    
            async Task SendAsync(
                HttpMethod requestType,
                string requestUri,
                string jsonRequest = null)
            {
                var request = new HttpRequestMessage(requestType, new Uri($"{_baseApiUrl}{requestUri}"));
    
                if (jsonRequest != null)
                    request.Content = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
    
                var response = await _client.SendAsync(request).ConfigureAwait(false);
    
                response.EnsureSuccessStatusCode();
            }
        }
    }
    

    Примечание

    Аргумент apiKey требуется только в том случае, если вы решили выполнить проверку подлинности клиентов с помощью ключа API .

  11. Добавьте пустой класс в папку Servicesс именем PushDemoNotificationActionService.cs реализации IPushDemoNotificationActionService с помощью следующего кода.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using PushDemo.Models;
    
    namespace PushDemo.Services
    {
        public class PushDemoNotificationActionService : IPushDemoNotificationActionService
        {
            readonly Dictionary<string, PushDemoAction> _actionMappings = new Dictionary<string, PushDemoAction>
            {
                { "action_a", PushDemoAction.ActionA },
                { "action_b", PushDemoAction.ActionB }
            };
    
            public event EventHandler<PushDemoAction> ActionTriggered = delegate { };
    
            public void TriggerAction(string action)
            {
                if (!_actionMappings.TryGetValue(action, out var pushDemoAction))
                    return;
    
                List<Exception> exceptions = new List<Exception>();
    
                foreach (var handler in ActionTriggered?.GetInvocationList())
                {
                    try
                    {
                        handler.DynamicInvoke(this, pushDemoAction);
                    }
                    catch (Exception ex)
                    {
                        exceptions.Add(ex);
                    }
                }
    
                if (exceptions.Any())
                    throw new AggregateException(exceptions);
            }
        }
    }
    
  12. Добавьте пустой класс в проект PushDemoс именем Config.cs со следующей реализацией.

    namespace PushDemo
    {
        public static partial class Config
        {
            public static string ApiKey = "API_KEY";
            public static string BackendServiceEndpoint = "BACKEND_SERVICE_ENDPOINT";
        }
    }
    

    Примечание

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

    Поле ApiKey является обязательным только в том случае, если вы решили выполнить проверку подлинности клиентов с помощью ключа API .

  13. На этот раз добавьте еще один пустой класс в проект PushDemoс именем Config.local_secrets.cs со следующей реализацией.

    namespace PushDemo
    {
        public static partial class Config
        {
            static Config()
            {
                ApiKey = "<your_api_key>";
                BackendServiceEndpoint = "<your_api_app_url>";
            }
        }
    }
    

    Примечание

    Замените значения заполнителей собственными. Вы должны были запомнить их при создании серверной службы. URL-адрес приложения API должен иметь значение https://<api_app_name>.azurewebsites.net/. Не забудьте добавить *.local_secrets.* в файл Gitignore, чтобы избежать фиксации этого файла.

    Поле ApiKey является обязательным только в том случае, если вы решили выполнить проверку подлинности клиентов с помощью ключа API .

  14. Добавьте пустой класс в проект PushDemoс именем Bootstrap.cs со следующей реализацией.

    using System;
    using PushDemo.Services;
    
    namespace PushDemo
    {
        public static class Bootstrap
        {
            public static void Begin(Func<IDeviceInstallationService> deviceInstallationService)
            {
                ServiceContainer.Register(deviceInstallationService);
    
                ServiceContainer.Register<IPushDemoNotificationActionService>(()
                    => new PushDemoNotificationActionService());
    
                ServiceContainer.Register<INotificationRegistrationService>(()
                    => new NotificationRegistrationService(
                        Config.BackendServiceEndpoint,
                        Config.ApiKey));
            }
        }
    }
    

    Примечание

    Метод Begin будет вызываться каждой платформой при запуске приложения, передавая зависят от платформы реализацию IDeviceInstallationService.

    Аргумент конструктора ApiKeyNotificationRegistrationService требуется только в том случае, если вы решили выполнить проверку подлинности клиентов с помощью ключа API.

Реализация кроссплатформенного пользовательского интерфейса

  1. В проекте PushDemo откройте файл MainPage.xaml и замените элемент управления StackLayout следующим кодом.

    <StackLayout VerticalOptions="EndAndExpand"  
                 HorizontalOptions="FillAndExpand"
                 Padding="20,40">
        <Button x:Name="RegisterButton"
                Text="Register"
                Clicked="RegisterButtonClicked" />
        <Button x:Name="DeregisterButton"
                Text="Deregister"
                Clicked="DeregisterButtonClicked" />
    </StackLayout>
    
  2. Теперь в MainPage.xaml.cs добавьте резервное поле только для чтения для хранения ссылки на реализацию INotificationRegistrationService .

    readonly INotificationRegistrationService _notificationRegistrationService;
    
  3. В конструкторе MainPage разрешите реализацию INotificationRegistrationService с помощью ServiceContainer и назначьте ее резервному полю notificationRegistrationService .

    public MainPage()
    {
        InitializeComponent();
    
        _notificationRegistrationService =
            ServiceContainer.Resolve<INotificationRegistrationService>();
    }
    
  4. Реализуйте обработчики событий для кнопок RegisterButton и DeregisterButtonClicked , вызывающих соответствующие методы Register/Deregister .

    void RegisterButtonClicked(object sender, EventArgs e)
        => _notificationRegistrationService.RegisterDeviceAsync().ContinueWith((task)
            => { ShowAlert(task.IsFaulted ?
                    task.Exception.Message :
                    $"Device registered"); });
    
    void DeregisterButtonClicked(object sender, EventArgs e)
        => _notificationRegistrationService.DeregisterDeviceAsync().ContinueWith((task)
            => { ShowAlert(task.IsFaulted ?
                    task.Exception.Message :
                    $"Device deregistered"); });
    
    void ShowAlert(string message)
        => MainThread.BeginInvokeOnMainThread(()
            => DisplayAlert("PushDemo", message, "OK").ContinueWith((task)
                => { if (task.IsFaulted) throw task.Exception; }));
    
  5. Теперь в App.xaml.cs убедитесь, что имеются ссылки на следующие пространства имен.

    using PushDemo.Models;
    using PushDemo.Services;
    using Xamarin.Essentials;
    using Xamarin.Forms;
    
  6. Реализуйте обработчик событий для события IPushDemoNotificationActionServiceActionTriggered .

    void NotificationActionTriggered(object sender, PushDemoAction e)
        => ShowActionAlert(e);
    
    void ShowActionAlert(PushDemoAction action)
        => MainThread.BeginInvokeOnMainThread(()
            => MainPage?.DisplayAlert("PushDemo", $"{action} action received", "OK")
                .ContinueWith((task) => { if (task.IsFaulted) throw task.Exception; }));
    
  7. В конструкторе приложения разрешите реализацию IPushNotificationActionService с помощью ServiceContainer и подпишитесь на событие IPushDemoNotificationActionServiceActionTriggered .

    public App()
    {
        InitializeComponent();
    
        ServiceContainer.Resolve<IPushDemoNotificationActionService>()
            .ActionTriggered += NotificationActionTriggered;
    
        MainPage = new MainPage();
    }
    

    Примечание

    Это просто демонстрация получения и распространения действий push-уведомлений. Как правило, они обрабатываются автоматически, например переход к определенному представлению или обновление некоторых данных, а не отображение оповещения через корневую страницуMainPage в данном случае.

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

Проверка имени пакета и разрешений

  1. В PushDemo.Android откройте параметры проекта , а затем приложение Android из раздела Сборка .

  2. Убедитесь, что имя пакета соответствует значению, которое использовалось в проекте Firebase ConsolePushDemo . Имя пакета было в формате com.<organization>.pushdemo.

  3. Задайте для параметра Минимальная версия Androidзначение Android 8.0 (уровень API 26), а для параметра Целевая версия Android — последний уровень API.

    Примечание

    Для целей этого руководства поддерживаются только те устройства, на которых выполняется API уровня 26 и выше , однако его можно расширить для поддержки устройств под управлением более старых версий.

  4. Убедитесь, что разрешения ИНТЕРНЕТА и READ_PHONE_STATE включены в разделе Необходимые разрешения.

  5. Нажмите кнопку ОК.

Добавление базовых пакетов служб Xamarin Google Play и Xamarin.Firebase.Messaging

  1. В PushDemo.Androidщелкните + папку Пакеты, а затем выберите Управление пакетами NuGet....

  2. Выполните поиск по запросу Xamarin.GooglePlayServices.Base (не Basement) и убедитесь, что он установлен.

  3. Выполните поиск по запросу Xamarin.Firebase.Messaging и убедитесь, что он установлен.

  4. Щелкните Добавить пакеты, а затем нажмите кнопку Принять при появлении запроса на принятие условий лицензионного соглашения.

Добавление JSON-файла служб Google

  1. Управления + PushDemo.AndroidЩелкните проект, а затем выберите Существующий файл... в меню Добавить.

  2. Выберите файл google-services.json , скачанный ранее при настройке проекта PushDemo , в консоли Firebase , а затем нажмите кнопку Открыть.

  3. При появлении запроса выберите Копировать файл в каталог.

  4. Управления + Щелкнитефайл google-services.json в PushDemo.Android проекте, а затем убедитесь, что GoogleServicesJson установлен в качестве действия сборки.

Обработка push-уведомлений для Android

  1. Управления + PushDemo.AndroidЩелкните проект, выберите Создать папку в меню Добавить, а затем щелкните Добавить, используя службы в качестве имени папки.

  2. Управления + Щелкните папку Службы , а затем выберите Создать файл... в меню Добавить .

  3. Выберите Общий>Пустой класс, введите DeviceInstallationService.cs в поле Имя, а затем нажмите кнопку Создать , добавив следующую реализацию.

    using System;
    using Android.App;
    using Android.Gms.Common;
    using PushDemo.Models;
    using PushDemo.Services;
    using static Android.Provider.Settings;
    
    namespace PushDemo.Droid.Services
    {
        public class DeviceInstallationService : IDeviceInstallationService
        {
            public string Token { get; set; }
    
            public bool NotificationsSupported
                => GoogleApiAvailability.Instance
                    .IsGooglePlayServicesAvailable(Application.Context) == ConnectionResult.Success;
    
            public string GetDeviceId()
                => Secure.GetString(Application.Context.ContentResolver, Secure.AndroidId);
    
            public DeviceInstallation GetDeviceInstallation(params string[] tags)
            {
                if (!NotificationsSupported)
                    throw new Exception(GetPlayServicesError());
    
                if (string.IsNullOrWhiteSpace(Token))
                    throw new Exception("Unable to resolve token for FCM");
    
                var installation = new DeviceInstallation
                {
                    InstallationId = GetDeviceId(),
                    Platform = "fcm",
                    PushChannel = Token
                };
    
                installation.Tags.AddRange(tags);
    
                return installation;
            }
    
            string GetPlayServicesError()
            {
                int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(Application.Context);
    
                if (resultCode != ConnectionResult.Success)
                    return GoogleApiAvailability.Instance.IsUserResolvableError(resultCode) ?
                               GoogleApiAvailability.Instance.GetErrorString(resultCode) :
                               "This device is not supported";
    
                return "An error occurred preventing the use of push notifications";
            }
        }
    }
    

    Примечание

    Этот класс предоставляет уникальный идентификатор (с использованием Secure.AndroidId) в составе полезных данных регистрации центра уведомлений.

  4. Добавьте еще один пустой класс в папку Servicesс именем PushNotificationFirebaseMessagingService.cs, а затем добавьте следующую реализацию.

    using Android.App;
    using Android.Content;
    using Firebase.Messaging;
    using PushDemo.Services;
    
    namespace PushDemo.Droid.Services
    {
        [Service]
        [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
        public class PushNotificationFirebaseMessagingService : FirebaseMessagingService
        {
            IPushDemoNotificationActionService _notificationActionService;
            INotificationRegistrationService _notificationRegistrationService;
            IDeviceInstallationService _deviceInstallationService;
    
            IPushDemoNotificationActionService NotificationActionService
                => _notificationActionService ??
                    (_notificationActionService =
                    ServiceContainer.Resolve<IPushDemoNotificationActionService>());
    
            INotificationRegistrationService NotificationRegistrationService
                => _notificationRegistrationService ??
                    (_notificationRegistrationService =
                    ServiceContainer.Resolve<INotificationRegistrationService>());
    
            IDeviceInstallationService DeviceInstallationService
                => _deviceInstallationService ??
                    (_deviceInstallationService =
                    ServiceContainer.Resolve<IDeviceInstallationService>());
    
            public override void OnNewToken(string token)
            {
                DeviceInstallationService.Token = token;
    
                NotificationRegistrationService.RefreshRegistrationAsync()
                    .ContinueWith((task) => { if (task.IsFaulted) throw task.Exception; });
            }
    
            public override void OnMessageReceived(RemoteMessage message)
            {
                if(message.Data.TryGetValue("action", out var messageAction))
                    NotificationActionService.TriggerAction(messageAction);
            }
        }
    }
    
  5. В MainActivity.cs убедитесь, что в начало файла добавлены следующие пространства имен.

    using System;
    using Android.App;
    using Android.Content;
    using Android.Content.PM;
    using Android.OS;
    using Android.Runtime;
    using Firebase.Iid;
    using PushDemo.Droid.Services;
    using PushDemo.Services;
    
  6. В MainActivity.cs задайте для LaunchMode значение SingleTop , чтобы при открытии MainActivity не создавался повторно.

    [Activity(
        Label = "PushDemo",
        LaunchMode = LaunchMode.SingleTop,
        Icon = "@mipmap/icon",
        Theme = "@style/MainTheme",
        MainLauncher = true,
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    
  7. Добавьте частные свойства и соответствующие резервные поля для хранения ссылки на реализации IPushNotificationActionService и IDeviceInstallationService .

    IPushDemoNotificationActionService _notificationActionService;
    IDeviceInstallationService _deviceInstallationService;
    
    IPushDemoNotificationActionService NotificationActionService
        => _notificationActionService ??
            (_notificationActionService =
            ServiceContainer.Resolve<IPushDemoNotificationActionService>());
    
    IDeviceInstallationService DeviceInstallationService
        => _deviceInstallationService ??
            (_deviceInstallationService =
            ServiceContainer.Resolve<IDeviceInstallationService>());
    
  8. Реализуйте интерфейс IOnSuccessListener для получения и хранения токена Firebase .

    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity, Android.Gms.Tasks.IOnSuccessListener
    {
        ...
    
        public void OnSuccess(Java.Lang.Object result)
            => DeviceInstallationService.Token =
                result.Class.GetMethod("getToken").Invoke(result).ToString();
    }
    
  9. Добавьте новый метод ProcessNotificationActions, который будет проверка, имеет ли данное намерение дополнительное значение с именем action. Условный запуск этого действия с помощью реализации IPushDemoNotificationActionService .

    void ProcessNotificationActions(Intent intent)
    {
        try
        {
            if (intent?.HasExtra("action") == true)
            {
                var action = intent.GetStringExtra("action");
    
                if (!string.IsNullOrEmpty(action))
                    NotificationActionService.TriggerAction(action);
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.Message);
        }
    }
    
  10. Переопределите метод OnNewIntent , чтобы вызвать метод ProcessNotificationActions .

    protected override void OnNewIntent(Intent intent)
    {
        base.OnNewIntent(intent);
        ProcessNotificationActions(intent);
    }
    

    Примечание

    Так как launchMode для действия имеет значение SingleTop, намерение будет отправлено в существующий экземпляр Действия с помощью метода OnNewIntent , а не метода OnCreate , поэтому необходимо обрабатывать входящее намерение в методах OnCreate и OnNewIntent .

  11. Обновите метод OnCreate , чтобы он вызывал Bootstrap.Begin сразу после вызова для base.OnCreate передачи реализации IDeviceInstallationService для конкретной платформы.

    Bootstrap.Begin(() => new DeviceInstallationService());
    
  12. В том же методе условно вызовите GetInstanceId в экземпляре FirebaseApp сразу после вызова Bootstrap.Begin, добавив MainActivity в качестве IOnSuccessListener.

    if (DeviceInstallationService.NotificationsSupported)
    {
        FirebaseInstanceId.GetInstance(Firebase.FirebaseApp.Instance)
            .GetInstanceId()
            .AddOnSuccessListener(this);
    }
    
  13. По-прежнему в OnCreate вызовите ProcessNotificationActions сразу после вызова для LoadApplication передачи текущего намерения.

    ...
    
    LoadApplication(new App());
    
    ProcessNotificationActions(Intent);
    

Примечание

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

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

Настройка Info.plist и Entitlements.plist

  1. Убедитесь, что вы вошли в учетную запись разработчика Apple внастройкахVisual Studio>...>Публикации>Были скачаны учетные записи разработчика Apple и соответствующий сертификат и профиль подготовки. Вы должны были создать эти ресурсы в рамках предыдущих шагов.

  2. В PushDemo.iOS откройте Файл Info.plist и убедитесь, что BundleIdentifier соответствует значению, которое использовалось для соответствующего профиля подготовки на портале разработчика Apple. BundleIdentifier был в формате com.<organization>.PushDemo.

  3. В том же файле задайте для параметра Минимальная версия системы значение 13.0.

    Примечание

    Для целей этого руководства поддерживаются только те устройства под управлением iOS 13.0 и более поздних версий, однако его можно расширить для поддержки устройств под управлением более ранних версий.

  4. Откройте параметры проекта для PushDemo.iOS (дважды щелкните проект).

  5. В разделе Параметры проекта в разделе Создание > пакета iOS убедитесь, что в разделе Команда выбрана учетная запись разработчика. Затем убедитесь, что выбран параметр "Автоматическое управление подписыванием" и автоматически выбраны сертификат подписи и профиль подготовки.

    Примечание

    Если сертификат подписи и профиль подготовки не выбраны автоматически, выберите Подготовка вручную, а затем щелкните Параметры подписывания пакета. Убедитесь, что ваша команда выбрана для параметра Удостоверение подписывания , а ваш профиль подготовки PushDemo выбран в поле Профиль подготовки для конфигураций отладки и выпуска , гарантируя, что iPhone выбран для платформы в обоих случаях.

  6. В PushDemo.iOS откройте файл Entitlements.plist и убедитесь, что флажок Включить push-уведомления установлен при просмотре на вкладке Права . Затем убедитесь, что для параметра Среда APS задано значение разработка при просмотре на вкладке Источник .

Обработка push-уведомлений для iOS

  1. Управления + Щелкните проект PushDemo.iOS , выберите Создать папку в меню Добавить , а затем щелкните Добавить , используя службы в качестве имени папки.

  2. Управления + Щелкните папку Службы , а затем выберите Создать файл... в меню Добавить .

  3. Выберите Общий>Пустой класс, введите DeviceInstallationService.cs в поле Имя, а затем нажмите кнопку Создать , добавив следующую реализацию.

    using System;
    using PushDemo.Models;
    using PushDemo.Services;
    using UIKit;
    
    namespace PushDemo.iOS.Services
    {
        public class DeviceInstallationService : IDeviceInstallationService
        {
            const int SupportedVersionMajor = 13;
            const int SupportedVersionMinor = 0;
    
            public string Token { get; set; }
    
            public bool NotificationsSupported
                => UIDevice.CurrentDevice.CheckSystemVersion(SupportedVersionMajor, SupportedVersionMinor);
    
            public string GetDeviceId()
                => UIDevice.CurrentDevice.IdentifierForVendor.ToString();
    
            public DeviceInstallation GetDeviceInstallation(params string[] tags)
            {
                if (!NotificationsSupported)
                    throw new Exception(GetNotificationsSupportError());
    
                if (string.IsNullOrWhiteSpace(Token))
                    throw new Exception("Unable to resolve token for APNS");
    
                var installation = new DeviceInstallation
                {
                    InstallationId = GetDeviceId(),
                    Platform = "apns",
                    PushChannel = Token
                };
    
                installation.Tags.AddRange(tags);
    
                return installation;
            }
    
            string GetNotificationsSupportError()
            {
                if (!NotificationsSupported)
                    return $"This app only supports notifications on iOS {SupportedVersionMajor}.{SupportedVersionMinor} and above. You are running {UIDevice.CurrentDevice.SystemVersion}.";
    
                if (Token == null)
                    return $"This app can support notifications but you must enable this in your settings.";
    
    
                return "An error occurred preventing the use of push notifications";
            }
        }
    }
    

    Примечание

    Этот класс предоставляет уникальный идентификатор (используя значение UIDevice.IdentifierForVendor ) и полезные данные регистрации центра уведомлений.

  4. Добавьте новую папку в проект PushDemo.iOS с именем Extensions , а затем добавьте пустой класс в папку с именем NSDataExtensions.cs со следующей реализацией.

    using System.Text;
    using Foundation;
    
    namespace PushDemo.iOS.Extensions
    {
        internal static class NSDataExtensions
        {
            internal static string ToHexString(this NSData data)
            {
                var bytes = data.ToArray();
    
                if (bytes == null)
                    return null;
    
                StringBuilder sb = new StringBuilder(bytes.Length * 2);
    
                foreach (byte b in bytes)
                    sb.AppendFormat("{0:x2}", b);
    
                return sb.ToString().ToUpperInvariant();
            }
        }
    }
    
  5. В AppDelegate.cs убедитесь, что в начало файла добавлены следующие пространства имен.

    using System;
    using System.Diagnostics;
    using System.Threading.Tasks;
    using Foundation;
    using PushDemo.iOS.Extensions;
    using PushDemo.iOS.Services;
    using PushDemo.Services;
    using UIKit;
    using UserNotifications;
    using Xamarin.Essentials;
    
  6. Добавьте частные свойства и соответствующие резервные поля для хранения ссылки на реализации IPushDemoNotificationActionService, INotificationRegistrationService и IDeviceInstallationService .

    IPushDemoNotificationActionService _notificationActionService;
    INotificationRegistrationService _notificationRegistrationService;
    IDeviceInstallationService _deviceInstallationService;
    
    IPushDemoNotificationActionService NotificationActionService
        => _notificationActionService ??
            (_notificationActionService =
            ServiceContainer.Resolve<IPushDemoNotificationActionService>());
    
    INotificationRegistrationService NotificationRegistrationService
        => _notificationRegistrationService ??
            (_notificationRegistrationService =
            ServiceContainer.Resolve<INotificationRegistrationService>());
    
    IDeviceInstallationService DeviceInstallationService
        => _deviceInstallationService ??
            (_deviceInstallationService =
            ServiceContainer.Resolve<IDeviceInstallationService>());
    
  7. Добавьте метод RegisterForRemoteNotifications для регистрации параметров уведомлений пользователя, а затем для удаленных уведомлений с помощью APNS.

    void RegisterForRemoteNotifications()
    {
        MainThread.BeginInvokeOnMainThread(() =>
        {
            var pushSettings = UIUserNotificationSettings.GetSettingsForTypes(
                UIUserNotificationType.Alert |
                UIUserNotificationType.Badge |
                UIUserNotificationType.Sound,
                new NSSet());
    
            UIApplication.SharedApplication.RegisterUserNotificationSettings(pushSettings);
            UIApplication.SharedApplication.RegisterForRemoteNotifications();
        });
    }
    
  8. Добавьте метод CompleteRegistrationAsync , чтобы задать IDeviceInstallationService.Token значение свойства. Обновите регистрацию и кэшируйте маркер устройства, если он был обновлен с момента последнего хранения.

    Task CompleteRegistrationAsync(NSData deviceToken)
    {
        DeviceInstallationService.Token = deviceToken.ToHexString();
        return NotificationRegistrationService.RefreshRegistrationAsync();
    }
    
  9. Добавьте метод ProcessNotificationActions для обработки данных уведомления NSDictionary и условного вызова NotificationActionService.TriggerAction.

    void ProcessNotificationActions(NSDictionary userInfo)
    {
        if (userInfo == null)
            return;
    
        try
        {
            var actionValue = userInfo.ObjectForKey(new NSString("action")) as NSString;
    
            if (!string.IsNullOrWhiteSpace(actionValue?.Description))
                NotificationActionService.TriggerAction(actionValue.Description);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }
    
  10. Переопределите метод RegisteredForRemoteNotifications , передав аргумент deviceToken методу CompleteRegistrationAsync .

    public override void RegisteredForRemoteNotifications(
        UIApplication application,
        NSData deviceToken)
        => CompleteRegistrationAsync(deviceToken).ContinueWith((task)
            => { if (task.IsFaulted) throw task.Exception; });
    
  11. Переопределите метод ReceivedRemoteNotification , передав аргумент userInfo методу ProcessNotificationActions .

    public override void ReceivedRemoteNotification(
        UIApplication application,
        NSDictionary userInfo)
        => ProcessNotificationActions(userInfo);
    
  12. Переопределите метод FailedToRegisterForRemoteNotifications , чтобы зарегистрировать ошибку.

    public override void FailedToRegisterForRemoteNotifications(
        UIApplication application,
        NSError error)
        => Debug.WriteLine(error.Description);
    

    Примечание

    Это в значительной степени заполнитель. Вам потребуется реализовать надлежащее ведение журнала и обработку ошибок для рабочих сценариев.

  13. Обновите метод FinishedLaunching для вызова Bootstrap.Begin сразу после вызова для Forms.Init передачи зависят от платформы реализации IDeviceInstallationService.

    Bootstrap.Begin(() => new DeviceInstallationService());
    
  14. В том же методе условно запрашивайте авторизацию и регистрируйте удаленные уведомления сразу после Bootstrap.Begin.

    if (DeviceInstallationService.NotificationsSupported)
    {
        UNUserNotificationCenter.Current.RequestAuthorization(
                UNAuthorizationOptions.Alert |
                UNAuthorizationOptions.Badge |
                UNAuthorizationOptions.Sound,
                (approvalGranted, error) =>
                {
                    if (approvalGranted && error == null)
                        RegisterForRemoteNotifications();
                });
    }
    
  15. По-прежнему в FinishedLaunching вызовите ProcessNotificationActions сразу после вызова LoadApplication , если аргумент options содержит UIApplication.LaunchOptionsRemoteNotificationKey , передавая полученный объект userInfo .

    using (var userInfo = options?.ObjectForKey(
        UIApplication.LaunchOptionsRemoteNotificationKey) as NSDictionary)
            ProcessNotificationActions(userInfo);
    

Тестирование решения

Теперь вы можете проверить отправку уведомлений через серверную службу.

Отправка тестового уведомления

  1. Откройте новую вкладку в Postman.

  2. Задайте для запроса значение POST и введите следующий адрес:

    https://<app_name>.azurewebsites.net/api/notifications/requests
    
  3. Если вы решили выполнить проверку подлинности клиентов с помощью ключа API , обязательно настройте заголовки запросов, чтобы включить значение apikey .

    Ключ Значение
    apikey <your_api_key>
  4. Выберите необработанный параметр для текста, затем выберите JSON из списка параметров формата и включите заполнитель содержимого JSON :

    {
        "text": "Message from Postman!",
        "action": "action_a"
    }
    
  5. Нажмите кнопку Код , которая находится под кнопкой Сохранить в правом верхнем углу окна. Запрос должен выглядеть примерно так, как в следующем примере, если он отображается для HTML (в зависимости от того, включен ли заголовок apikey ):

    POST /api/notifications/requests HTTP/1.1
    Host: https://<app_name>.azurewebsites.net
    apikey: <your_api_key>
    Content-Type: application/json
    
    {
        "text": "Message from backend service",
        "action": "action_a"
    }
    
  6. Запустите приложение PushDemo на одной или обеих целевых платформах (Android и iOS).

    Примечание

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

  7. В приложении PushDemo нажмите кнопку Зарегистрировать .

  8. Вернитесь в Postman, закройте окно Создание фрагментов кода (если вы еще этого не сделали), а затем нажмите кнопку Отправить .

  9. Убедитесь, что вы получили ответ 200 OK в Postman , и оповещение появится в приложении с сообщением ActionA получено действие.

  10. Закройте приложение PushDemo и снова нажмите кнопку Отправить в Postman.

  11. Убедитесь, что вы получили ответ 200 ОК в Postman еще раз. Убедитесь, что в области уведомлений для приложения PushDemo отображается уведомление с правильным сообщением.

  12. Коснитесь уведомления, чтобы убедиться, что приложение открывается и отображается оповещение ActionA получено .

  13. Вернитесь в Postman, измените предыдущий текст запроса, чтобы отправить автоматическое уведомление, указывающее action_b вместо action_a для значения действия .

    {
        "action": "action_b",
        "silent": true
    }
    
  14. Пока приложение все еще открыто, нажмите кнопку Отправить в Postman.

  15. Убедитесь, что вы получили ответ 200 OK в Postman и что в приложении отображается оповещение о получении действия ActionB, а не действие ActionA.

  16. Закройте приложение PushDemo и снова нажмите кнопку Отправить в Postman.

  17. Убедитесь, что вы получили ответ 200 OK в Postman и что автоматическое уведомление не отображается в области уведомлений.

Устранение неполадок

Нет ответа от серверной службы

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

При тестировании в приложении API Azure проверка служба запущена, развернута и запущена без ошибок.

Не забудьте проверка вы правильно указали базовый адрес в Postman или в конфигурации мобильного приложения при тестировании через клиент. При локальном тестировании базовым адресом должен быть https://<api_name>.azurewebsites.net/ или https://localhost:5001/ .

Отсутствие уведомлений в Android после запуска или остановки сеанса отладки

Убедитесь, что вы снова зарегистрируетесь после запуска или остановки сеанса отладки. Отладчик приведет к созданию нового токена Firebase . Также необходимо обновить установку центра уведомлений.

Получение кода состояния 401 из серверной службы

Убедитесь, что вы задали заголовок запроса apikey и это значение совпадает с тем, которое вы настроили для серверной службы.

Если эта ошибка возникает при локальном тестировании, убедитесь, что значение ключа, определенное в конфигурации клиента, совпадает со значением user-setting Authentication:ApiKey , используемым API.

При тестировании с помощью приложения API убедитесь, что значение ключа в файле конфигурации клиента соответствует параметру приложения Authentication:ApiKey , используемому в приложении API.

Примечание

Если вы создали или изменили этот параметр после развертывания серверной службы, необходимо перезапустить службу, чтобы она вступила в силу.

Если вы решили не выполнять проверку подлинности клиентов с помощью ключа API , убедитесь, что вы не применили атрибут Authorize к классу NotificationsController .

Получение кода состояния 404 из серверной службы

Убедитесь, что конечная точка и метод HTTP-запроса верны. Например, конечные точки должны быть примерно такими:

  • [PUT]https://<api_name>.azurewebsites.net/api/notifications/installations
  • [DELETE]https://<api_name>.azurewebsites.net/api/notifications/installations/<installation_id>
  • [POST]https://<api_name>.azurewebsites.net/api/notifications/requests

Или при локальном тестировании:

  • [PUT]https://localhost:5001/api/notifications/installations
  • [DELETE]https://localhost:5001/api/notifications/installations/<installation_id>
  • [POST]https://localhost:5001/api/notifications/requests

При указании базового адреса в клиентском приложении убедитесь, что он заканчивается /на . При локальном тестировании базовым адресом должен быть https://<api_name>.azurewebsites.net/ или https://localhost:5001/ .

Не удается зарегистрироваться и отображается сообщение об ошибке центра уведомлений

Убедитесь, что на тестовом устройстве установлено сетевое подключение. Затем определите код состояния HTTP-ответа, задав точку останова для проверки значения свойства StatusCode в HttpResponse.

Просмотрите предыдущие рекомендации по устранению неполадок, если это применимо, на основе кода состояния.

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

Убедитесь, что серверная служба работает должным образом с помощью Postman , используя соответствующие полезные данные. Используйте фактические полезные данные, созданные клиентским кодом для рассматриваемой платформы.

Просмотрите разделы конфигурации для конкретной платформы, чтобы убедиться, что никакие шаги не были пропущены. Убедитесь, что для переменных и token для соответствующей платформы разрешаются installation id подходящие значения.

Не удается разрешить идентификатор для сообщения об ошибке устройства

Просмотрите разделы конфигурации для конкретной платформы, чтобы убедиться, что никакие шаги не были пропущены.

Дальнейшие действия

Теперь у вас должно быть базовое приложение Xamarin.Forms, подключенное к центру уведомлений через серверную службу, которое может отправлять и получать уведомления.

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

Центр приложений Visual Studio можно быстро включить в мобильные приложения, предоставляющие аналитику и диагностика для устранения неполадок.