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


Руководство. Отправка 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. На странице приложения Android добавить Firebase выполните следующие действия.

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

      Укажите имя пакета

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

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

      скачивание google-services.json

    4. Выберите Далее.

    5. Выберите Продолжить консоль

      Заметка

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

  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 запустите средство доступа к цепочке ключей. Его можно открыть из папки Служебные программы или папку Other на панели запуска.

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

    Использовать доступ к цепочке ключей для запроса нового сертификата

    Заметка

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

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

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

  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. В access Control + Click на новом push-сертификате, созданном в категории сертификатов. Выберите экспорт, назовите файл, выберите формат p12 и нажмите кнопку Сохранить.

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

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

    Заметка

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

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

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

    Заметка

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

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

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

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

    Заметка

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

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

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

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

    ЗАКРЫТЫЙ КЛЮЧ -----BEGIN-----
    <key_value>
    -----END PRIVATE KEY-----

    Заметка

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

В конце этих действий вам должны быть приведены следующие сведения для последующего использования в Настройка концентратора уведомлений с помощью сведений 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. Выберите политики доступа из списка (в разделе MANAGE).

  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>App>ASP.NET Core>API>Далее.

  3. В диалоговом окне Настройка нового веб- API ASP.NET Core Web API выберите Target Framework.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. Настройте локальные значения конфигурации с помощью средства Secret Manager. Разделение секретов из решения гарантирует, что они не в конечном итоге в системе управления версиями. Откройте терминал перейдите в каталог файла проекта и выполните следующие команды:

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

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

    NotificationHub:Name:
    См. в сводке Essentials в верхней частиобзора .

    NotificationHub:ConnectionString:
    См. defaultFullSharedAccessSignature в политиках доступа

    Заметка

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

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

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

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

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

    Заметка

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

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

  3. Элемент управлениящелкните в папке проверки подлинности , а затем выберите Создать файл... в меню "Добавить".

  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. Добавьте еще один пустой класс в папку проверки подлинности с именем 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. Добавьте еще один пустой класс в 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, выберите создать папку в меню "Добавить", а затем щелкните Добавить с помощью моделей в качестве имени папки.

  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. Добавьте еще один пустой класс в папку моделей с именем 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. Добавьте еще один пустой класс в папк у моделей с именем 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 с именем Services.

  11. Добавьте пустого интерфейса в папку служб с именем 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. Добавьте пустой класс в папку служб с именем 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 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. Выберитекласс контроллера веб-API ASP.NET Core, введите 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
    }
    

    Заметка

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

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

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

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

  8. Запустите отладку (команда + ВВОД) для проверки работы приложения с новым NotificationsController и возвращает состояние 401 Unauthorized.

    Заметка

    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 OK с некоторыми содержимого JSON.

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

  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-адреса в сводке Essentials в верхней частиобзора . Этот 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>

    Заметка

    Это те же параметры, которые вы определили ранее в параметрах пользователя. Их можно скопировать. Параметр проверки подлинности :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 недопустимых запросов.

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

Заметка

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

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

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

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

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

Заметка

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

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

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

    Заметка

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

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

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

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

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

  6. Создайте и запустите приложение на каждой целевой платформе (Command + ВВОД) для тестирования шаблонного приложения на устройстве.

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

  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. Добавьте пустого перечисления в папку моделей с именем 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. Добавьте пустой интерфейс в папку служб с именем 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);
        }
    }
    

    Заметка

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

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

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

    Заметка

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

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

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

    Заметка

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

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

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

    Заметка

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

  10. Добавьте пустого класса в папку служб NotificationRegistrationService.cs служб 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. Добавьте пустого класса в папку служб с именем 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 должен быть . Не забудьте добавить *.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.

    Аргумент конструктора apiKey NotificationRegistrationServiceapiKey требуется только в том случае, если вы решили выполнить проверку подлинности клиентов с помощью раздела ключа 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 и кнопки DeregisterButton события clicked, вызывающие соответствующие методырегистрации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. В конструкторе app устраните реализацию IPushNotificationActionService с помощью ServiceContainer и подпишитесь на событие IPushDemoNotificationAction ServiceActionTriggered.

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

    Заметка

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

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

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

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

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

  3. Установите для минимальной версии Android значение уровня API 8.0 (уровень API 26) и целевой версии Android для последней уровня API.

    Заметка

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

  4. Убедитесь, что разрешения INTERNET и READ_PHONE_STATE включены в обязательных разрешений.

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

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

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

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

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

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

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

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

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

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

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

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

  1. элемент управлениящелкните в проекте , выберите создать папку в меню "Добавить", а затем щелкните Добавить с помощью служб в качестве имени папки.

  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. Добавьте еще один пустой класс в папку служб с именем 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, который проверяет, имеет ли заданное намерение дополнительное значение с именем действие. Условно активируйте это действие с помощью реализации 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 убедитесь, что учетная запись разработчика выбрана в разделе Team. Затем убедитесь, что выбран параметр "Автоматическое управление подписыванием", а профиль подписывания и подготовки автоматически выбраны.

    Заметка

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

  6. В PushDemo.iOSоткройте разрешения.p list и убедитесь, что включить push-уведомления проверяется при просмотре на вкладке прав. Затем убедитесь, что для параметра среды APS задано значение разработки при просмотре на вкладке источника .

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

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

  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 с именем расширения , а затем добавьте в нее пустой класс 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 методу CompleteRegistrationA sync.

    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, если аргумент параметров содержит аргумент 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 OK в Postman еще раз. Убедитесь, что уведомление отображается в области уведомлений для приложения PushDemo с правильным сообщением.

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

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

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

  15. Убедитесь, что в Postman появит ся ответ ОК 200 ОК, а не действие ActionA, полученное.

  16. Закройте приложение pushDemo , а затем нажмите кнопку Отправить еще раз в Postman.

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

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

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

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

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

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

Не получая уведомления в Android после запуска или остановки сеанса отладки

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

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

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

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

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

Заметка

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

Если вы решили не выполнять проверку подлинности клиентов с помощью раздела ключа API, убедитесь, что атрибут авторизации не применяется к классу NotificationsController 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 с помощью соответствующей полезных данных. Используйте фактические полезные данные, созданные клиентским кодом для платформы.

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

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

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

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

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

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

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