Статьи

Уведомления Windows 8: Push-уведомления через веб-сайты Windows Azure (часть 2)


В этой статье я расскажу о том, что необходимо для оснащения моего приложения Windows Store Boys of Summer для получения всплывающих уведомлений.
Если вы этого еще не сделали, я предлагаю просмотреть предыдущий пост для контекста .

Чтобы обеспечить базовый «клей» для службы уведомлений Windows, приложение Магазина Windows должно быть связано с профилем приложения, созданным в вашей учетной записи разработчика Магазина Windows. До официального выпуска Windows 8 существовала временная служба, которую можно было использовать, но в будущем вам потребуется учетная запись разработчика в Магазине Windows для разработки и тестирования приложений Магазина Windows, использующих push-уведомления.

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

Свяжите приложение Магазина Windows с магазиномВы можете получить доступ к своей учетной записи через центр разработки или просто через Visual Studio через опцию контекстного меню « Store»> «Связать приложение» с Store… , после чего вам будет предложено войти в центр Dev с учетными данными учетной записи магазина. Для каждого приложения, которое вы объявляете в центре разработки, вы получите контрольный список того, что необходимо для завершения приложения перед отправкой, но для целей этого упражнения вам потребуется настроить только один из шагов.

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

  1. После того, как имя приложения было зарезервировано:

  2. После выбора опции «Дополнительные функции» выше:

  3. После выбора Push-уведомлений и информации об услугах Live Connect.  Идентификатор пакета безопасности и секрет клиента , должны рассматриваться как конфиденциальные данные; воспринимайте их как идентификатор пользователя и пароль, позволяющие службе отправлять уведомления зарегистрированному приложению.

Во время этого процесса вашему приложению также присваивается уникальное имя пакета и издатель. Вы можете получить эти значения на странице « Идентификация вашего приложения…», доступной по ссылке на левой боковой панели выше, и заполнить ее вручную в Package.appxmanifest вашего проекта , или же вы можете позволить Visual Studio связать его для вас с помощью вышеупомянутой опции контекстного меню.

Связать приложение с Магазином Windows

Говоря о манифесте приложения , не забудьте пометить ваше приложение как
поддерживающее Toast на
вкладке «
Интерфейс приложения » и выбрать возможность «
Интернет (клиент)» на
вкладке «
Возможности » манифеста, или вы будете некоторое время ломать голову над вопросом, почему все кажется на месте, но уведомлений не происходит!

Шаг 2: Получить канал push-уведомлений в приложении

Канал push-уведомлений — это URI, который выглядит примерно так (и всегда указывает на домен notify.windows.com)

https://bn1.notify.windows.com/?token=AgYAAADCM0ruyKKQnGeNHSWDfdqWh9aphe244WGh0%…

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

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

Я предполагаю, что мое приложение Windows Store будет достаточно привлекательным, чтобы его можно было открывать хотя бы раз в месяц, поэтому я отказываюсь от фоновой задачи. Чтобы обновить канал при каждом запуске, я включил следующую единственную строку кода в конец методов OnLaunched и OnActivation (это реализация XAML / C #; для JavaScript вы использовали бы активированный прослушиватель событий )

this.CurrentChannel = await PushNotificationChannelManager


                              .CreatePushNotificationChannelForApplicationAsync();

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

Шаг 3. Зарегистрируйте канал уведомлений в облачной службе.

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

Переключатель уведомлений в пользовательском интерфейсе Boys of SummerТостовое уведомление, которое является здесь сценарием, обычно предназначается для подмножества пользователей на основе некоторой комбинации критериев (метаданных, если хотите), связанных с их каналом уведомления. В приложении Boys of Summer эти метаданные довольно очевидны — это команда или группы, в которых пользователь заинтересован быть в курсе событий. А пользователь выражает свою заинтересованность (или незаинтересованность!), Переключая переключатель уведомлений (справа), связанный с данной командой в пользовательском интерфейсе приложения.

Код (ниже) за переключаемым событием ToggleSwitch напрямую взаимодействует с облачной службой (через RESTful API). Я опишу этот сервис более подробно позже, но на высоком уровне код действительно выполняет одну из двух вещей:

  • Записывает интерес текущего пользователя к уведомлениям для связанной команды (Строки 9-25), отправляя канал уведомлений и имя команды в облачную службу, или
  • Выдает HTTP-запрос DELETE (строки 29–36), чтобы сигнализировать той же службе, чтобы она больше не информировала этого клиента о событиях, связанных с данной командой.
 private async void notificationsToggled(object sender, RoutedEventArgs e)
{
    var toggleSwitch = e.OriginalSource as ToggleSwitch;
    var selectedTeam = ((FrameworkElement)sender).DataContext as SampleDataItem;
 
    var channelUri = ((App)Application.Current).CurrentChannel.Uri;
 
    HttpClient clientRequest = new HttpClient();
    if (toggleSwitch.IsOn)
     {
         string postUri = String.Format("{0}/api/registrations/", 
                                        App.Current.Resources["ServiceUri"]);
 
        var JsonPayload = new StringContent(
               Newtonsoft.Json.JsonConvert.SerializeObject(
                   new
                    {
                        TeamId = selectedTeam.UniqueId,
                        Uri = ((App)Application.Current).CurrentChannel.Uri
                    }
                )
        );
        JsonPayload.Headers.ContentType = 
                                 new MediaTypeHeaderValue("application/json");
 
        await clientRequest.PostAsync(postUri, JsonPayload);
    }
    else
    {
        // format DELETE request
        string deleteUri = String.Format("{0}/api/registrations/{1}/{2}",
            App.Current.Resources["ServiceUri"],
            selectedTeam.UniqueId,
            channelUri.ToBase64ish()
        );
 
        await clientRequest.DeleteAsync(deleteUri);
    }
}

Что это за вызов ToBase64ish на линии 33? Это было частью интересного приключения, которое действительно имеет отношение к Windows 8 и push-уведомлениям, поэтому не стесняйтесь просматривать или игнорировать эту выноску.

Поскольку я использую веб-API ASP.NET для реализации облачной службы, я хотел сделать что-то очень RESTful. Следовательно, HTTP-запрос DELETE должен идентифицировать ресурс на сервере, который должен быть удален, как часть самого URI (в отличие от тела HTTP-запроса). В этом приложении этот ресурс однозначно идентифицируется комбинацией URI канала уведомления и имени команды.

Теперь URI канала уведомлений имеют некоторые довольно недружественные символы (например, косую черту и двоеточие), когда речь идет о включении их в качестве элемента пути большего URI. Это не огромный шок, и это одна из причин, почему есть функции UrlEncode (или, как я обнаружил, несколько их вариантов!). Становится ужаснее, когда вы обнаружите, что есть также такие настройки, как DontUnescapePathDotsAndSlashes и, о-о, если у вас есть несколько прямых слешей, они свернутся в один.

Итак, я сдался и решил Base64 кодировать URI уведомления. Base64 использует 52 латинских буквенных символа (нижний и верхний регистр) и цифры (от 0 до 9), но также дополняется двумя другими символами: косой чертой (/) и знаком плюс (+). Оба они также несколько «особенные» с точки зрения URI, но дефис и подчеркивание — нет, поэтому я создал простой метод расширения String (с именем ToBase64ish), который кодирует Base64, а затем заменяет каждый / на дефис, а каждый + на нижнее подчеркивание. (Конечно, есть соответствующий метод FromBase64ish, который облачный сервис использует на другом конце канала).

Шаг 2.5: Обновить ранее записанные URI регистрации канала уведомлений

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

Приложение должно убедиться, что URI канала, зарегистрированные на сервере, точно отражают текущий URI канала, который приложение защитило. Есть несколько способов справиться с этим, и то, как вы это сделаете, зависит от требований вашего приложения. Руководство для приложений Магазина Windows — обеспечить максимально возможную автономную работу, поэтому для приложения Boys of Summer я придумал следующее:

  1. Если приложение находится в автономном режиме, переключатели уведомлений будут отключены, но сохранят свои последние известные настройки. Это означает, что мне нужно сохранить некоторое состояние приложения для настройки уведомлений (вкл или выкл) каждой из команд. Локальное хранилище приложений является хорошим местом для этого, и поэтому мое приложение извлекает эти данные в ViewModel, которая связана со списком команд каждого подразделения.
  2. Прикладная программа также должна сохранять значение последнего URI, который оно использовало для регистрации уведомлений в облачной службе; в противном случае он не знал бы, что этот URI изменился! Локальное хранилище приложений снова на помощь.
  3. Наконец, приложению необходимо проверить, отличается ли только что полученный URI уведомления (при запуске или активации) от последнего, который использовался для регистрации заинтересованных групп в облачной службе. Если это так, то облачной службе необходимо обновить все записи уведомлений, содержащие предыдущий URI, чтобы отразить новый.

Чтобы обработать этот последний шаг, я включил другой метод в класс App и вызвал этот метод сразу после защиты канала push-уведомлений — той строки кода выше, которая вызывает CreatePushNotificationChannelForApplicationAsync . Как и следовало ожидать, если изменился URI канала, мне нужно вызвать другой метод в облачной службе, на этот раз через HTTP PUT, указывающий, что предыдущий URI должен быть обновлен на сервере новым.

private async void RefreshChannelUri(String newUri)

{

    // get last known notification channel id

    String previousUri = 

        (ApplicationData.Current.LocalSettings.Values["ChannelUri"] 

           ?? String.Empty).ToString();

    ApplicationData.Current.LocalSettings.Values["ChannelUri"] = newUri;

 

    // if no previous channel id, nothing to do

    if (String.IsNullOrEmpty(previousUri)) return;

 

    // if channel id hasn't changed, nothing to do

    if (previousUri == newUri) return;

 

    // update current registrations to new URI

    HttpClient clientRequest = new HttpClient();

    string putUri = String.Format("{0}/api/registrations/{1}", 

        App.Current.Resources["ServiceUri"], previousUri.ToBase64ish());

 

    var JsonPayload = new StringContent(

            Newtonsoft.Json.JsonConvert.SerializeObject(

                new

                {

                    Uri = newUri.ToBase64ish()

                }

            )

    );

 

    JsonPayload.Headers.ContentType = new MediaTypeHeaderValue("application/json");

 

    await clientRequest.PutAsync(putUri, JsonPayload);

}

Следующий шаг: внедрение облачной службы и ее развертывание в качестве веб-сайта Windows Azure.