Вступление

Это второй пост о создании прогрессивных веб-приложений (PWA) с использованием нового интерфейса командной строки Vue 3. Пока что в качестве первого шага к push-уведомлениям мы создали PWA, который отправляет (обычное) уведомление, когда пользователь нажимает кнопку. Если вы не читали эту часть, она доступна здесь.

В этом посте будут представлены push-уведомления. Терминология немного сложна, потому что мы будем использовать уведомления, сообщения и т. Д., И в этом легко заблудиться. Позвольте мне попытаться уточнить немного больше, показав диаграмму, наглядно демонстрирующую, как работают push-уведомления.

Итак, предположим, что новое сообщение было добавлено на веб-сайт блога (например, Medium.com). Кроме того, предположим, что вы подписаны на автора и включили уведомления для новых сообщений.

В момент публикации нового сообщения автором (1) все подписчики этого автора должны получать уведомления с помощью push-уведомлений. Следовательно, код (работающий на сервере), который обрабатывает отправку нового сообщения, ищет всех подписчиков (2a) и отправляет push-уведомление каждому из них (2b). Каждое push-уведомление сначала попадает к поставщику браузера, и именно этот поставщик браузера отправляет фактическое событие push в браузер пользователя (3). Сервисный работник прослушивает это push-событие и создает уведомление, используя API уведомлений браузера, чтобы показать сообщение типа «Привет, у нас есть для вас новое сообщение» (4).

Небольшая заметка о производителе браузера. Мне нравится думать об этом как о сервере из конструктора браузера. Так что для браузера Chrome это как сервер от Google. Таким образом, производитель браузера и браузер пользователя здесь - две разные вещи.

Что в этом посте?

В этом посте мы построим упрощенную версию этого потока: нажатие кнопки будет действовать как триггер для отправки push-уведомления (сравнимо с публикацией нового сообщения). Чтобы сделать уведомление более динамичным, мы добавим текстовое поле. Таким образом, вы можете отправить собственное сообщение в push-уведомлении.

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

Для этого мы настроим серверную часть (API) для демонстрационного приложения, которое мы создали ранее. Мы создаем этот API с помощью Laravel. Чтобы push-уведомления работали, мы будем использовать пакет webpush для Laravel, который расширяет встроенные каналы уведомлений.

Однако поработать нужно и над фронтендом. Чтобы иметь возможность получать push-уведомления, пользователь должен быть подписан на push-уведомления. Это делается с помощью Push API браузера.

Это будет довольно долгая поездка, поэтому давайте подведем итоги:

  1. Установить Laravel API
  2. Создайте конечную точку API для создания пользователя
  3. Установите пакет Webpush, который расширяет каналы уведомлений Laravel.
  4. Создайте еще две конечные точки API для хранения и получения подписок.
  5. Создайте подписку на веб-интерфейсе с помощью Push API браузера.
  6. Добавить функцию для запуска push-уведомления
  7. Обработка push-уведомления на сервере
  8. Добавить слушателя для события push
  9. Показать уведомление пользователю

Код доступен на Github (Laravel API, Vue PWA). Еще у нас есть демонстрационная установка!

Установить Laravel API

Чтобы создать API в Laravel, начните с создания новой установки Laravel, как описано в документации.

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

Schema::create('users', function (Blueprint $table) { 
    $table->increments('id'); 
    $table->string('name'); 
    $table->timestamps(); 
});

Чтобы создать подписку для пользователя, нам нужны пользователи. Как упоминалось ранее, хороший момент для создания пользователя - это когда он нажимает кнопку «включить уведомления» в вашем приложении. В Laravel API необходимо отправить запрос на создание нового пользователя. После создания API должен ответить вновь созданным пользователем.

Создайте конечную точку API для создания пользователя

В нашем Larevel API мы создадим следующую конечную точку в routes / api.php

Route::post('user', 'UserController@createOrRetrieve');

Мы создаем UserController с помощью команды artisan

php artisan make:controller UserController

Функция store просто создает (и возвращает) нового пользователя с уникальным случайным строковым именем.

/** 
 * If the request contains a username, retrieve this user from
 * the database and return it.
 * Otherwise, create a new user with a random name and return it.
 * @return new user object as json
 */ 
public function createOrRetrieve(Request $request) { 
    if ($request->has('username') && !is_null($request->username)) {     
        $user = User::where(['name' => $request->username])->first(); } 
    else { 
        // create a new user instance. 
        // name of the user is just a hashed string 
        $user = User::create(['name' => Str::uuid()]); 
    } 
    return response()->json(compact('user')); 
}

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

Установка пакета Webpush

Следующим шагом является настройка подписок, чтобы люди получали уведомления, когда что-то происходит. Подписки являются частью канала веб-push-уведомлений для Laravel, поэтому мы полностью следуем их руководству по установке до (включительно) генерации ключей VAPID. Ключи VAPID - это де-факто способ шифрования ваших push-уведомлений, и без них невозможно отправлять дополнительные данные с вашими уведомлениями. Я расскажу об этом подробнее чуть позже.

После установки, запуска миграций и генерации ключей VAPID у нас есть новая таблица push_subscriptions в базе данных. Пакет предлагает два метода записи в эту таблицу (updatePushSubscription и deletePushSubscription), мы воспользуемся этими методами на следующем шаге.

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

// create or update a subscription for a user Route::post('subscription', 'SubscriptionController@store'); 
// delete a subscription for a user Route::post('subscription/delete', SubscriptionController@destroy');

С этими двумя конечными точками мы завершаем бэкэнд-часть. Пришло время настроить подписку во фронтенде. То есть мы собираемся поговорить с конечной точкой `` пользователь '', чтобы создать пользователя, затем мы создадим подписку на веб-интерфейсе (используя открытый ключ VAPID) и, наконец, мы поговорим с конечной точкой `` подписка '', чтобы создать подписка на бэкэнде.

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

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

В предыдущем посте я описал, как начал разработку интерфейса. Мы реализовали довольно простое приложение, которое запускает уведомления, когда пользователь нажимает кнопку. До сих пор мы обрабатывали нажатие зеленой кнопки «Включить уведомления» в компоненте «Главная страница». Для этого компонента были определены два метода: askPermission и showNotification. Здесь мы расширим поведение. Позвольте мне объяснить, что нам здесь нужно.

Пользователи, посещающие страницу, либо посещают ее впервые, либо возвращаются и включали (или не включали) уведомления во время более раннего посещения. Следовательно, для возвращающихся пользователей мы должны проверить, включены ли у них уведомления. Мы сделаем это с помощью обработанного крючка компонента Home, который срабатывает, когда компонент монтируется на странице. Если уведомления включены, текст на кнопке необходимо изменить с «Включить уведомления» (по умолчанию) на «Отключить уведомления». Таким образом, пользователь может отключить уведомления. Если уведомления не включены, пользователь рассматривается как первый посетитель.

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

  1. Мы должны проверить, поддерживаются ли браузером уведомления и сервис-воркеры;
  2. Мы должны запросить у пользователя разрешение на отображение уведомлений;
  3. Мы должны создать подписку с помощью PushManager;
  4. Нам нужно создать пользователя и сохранить подписку на бэкэнде;
  5. Мы должны показать пользователю уведомление о том, что произошло.

Эти шаги реализованы в методе toggleSubscription компонента Home, показанном ниже.

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

  1. Поддержка уведомлений и сервис-воркеров реализована в хуке created компонента (строки 162–164). Мы просто переключаем флаг notificationsSupported в зависимости от поддержки браузера.
  2. Запрос разрешения пользователя - это то, что у нас уже было в исходной версии этого кода. Это не что иное, как ожидание результата обещания Notification.requestPermission () (строка 36). Мы можем продолжить только тогда, когда результат будет признан «должным».
  3. Создание подписки для пользователя требует немного больше работы. Я помещаю код в собственный метод createSubscription (строки 88–99).
    В этом методе нам сначала нужно получить регистрацию активного сервисного работника, вызвав основанную на обещаниях функцию navigator.serviceWorker.ready. Обратите внимание, что эта функция работает только в производственной среде. То есть сервис-воркер зарегистрирован только в производственных сборках. Когда обещание выполняется, он дает нам активного работника службы. Я добавляю переменную данных в свой компонент, чтобы потом не пришлось снова ее извлекать. Фактически, мы проверяем, установлена ​​ли уже переменная данных, прежде чем начинать ожидание обещания.
    Когда у нас есть активный работник службы, мы вызываем метод подписки (строки 104–116). Это момент, когда в игру вступают ключи VAPID, которые мы настроили ранее на бэкэнде. Здесь нам нужен доступ к публичному ключу VAPID. Поэтому я помещаю его в свой файл env на стороне клиента (в моем случае .env.production.local). Чтобы можно было использовать ключ, он должен начинаться с VUE_APP. мой ключ называется VUE_APP_VAPID_PUBLIC_KEY. Мы получаем его с помощью process.env.VUE_APP_VAPID_PUBLIC_KEY. Вы можете узнать больше о режимах и переменных окружения здесь.
    В pushManager активной регистрации сервис-воркера есть метод подписки (строка 110). В целях безопасности нам необходимо передать в метод две опции, которые более подробно описаны здесь. Если мы не настроим подписку таким образом, мы не сможем отправлять дополнительные данные с нашими push-сообщениями, и без этого они, на мой взгляд, бессмысленны. Первый вариант - это простое логическое значение userVisibleOnly: true. Однако второй, applicationServerKey, должен быть строкой DOMString в кодировке Base64 или« ArrayBuffer , содержащей открытый ключ ECDSA P-256, который сервер push-уведомлений будет использовать для аутентификации вашего сервера приложений». По сути, это означает, что мы должны преобразовать наш открытый ключ VAPID. Я опущу все детали, вы найдете код в методе urlBase64ToUint8Array (строки 146–159) внутри компонента. После этого пользователь подписывается на pushManager. Опять же, это обещание. После разрешения мы получаем подписку, состоящую из конечной точки. Именно на эту конечную точку мы будем отправлять push-сообщения. Конечная точка для браузера Chrome выглядит следующим образом: https://fcm.googleapis.com/fcm/send/dNZuZWNGTXM:APA91bHeSVSHi29sdTI9_igvIwUN-LhUWVbsdnftom4nMRc51QSAg5RElfiSoCRo3XFCkUOR7YY9jrcYa2emHjqvkOKpUsn-wygwduRhqBvPn8DNvekHPyaXh2-A4LyiESwPLApyp3r4.
  4. Мы храним подписку локально в компоненте, и пора также сохранить подписку на бэкэнде (строки 41–58). Как мы узнали ранее в этом посте, на бэкэнде нам нужен пользователь для подключения подписки. Следовательно, мы создаем пользователя (и сохраняем сгенерированное имя пользователя в localStorage). Имея в руках пользователя, мы храним подписку на бэкэнде. Здесь мы используем наши конечные точки API, созданные ранее.
  5. Наконец, пользователь подписывается на получение push-сообщений, и мы показываем уведомление (строки 59–63).

Возвращающийся пользователь и отключение уведомлений

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

  1. Проверить, доступна ли подписка (на веб-интерфейсе с помощью pushManager);
  2. Если подписка существует, измените текст кнопки на «Отключить уведомления»;
  3. Если подписка не найдена, ничего не происходит.

Интереснее становится, когда пользователь нажимает «отключить уведомления». В этом случае нам нужно отказаться от подписки как на клиенте, так и на сервере. Это также закодировано в методе toggleSubscription компонента:

  1. Подписка, найденная с помощью Pushmanager, используется для идентификации пользователя на бэкэнде. Мы используем поле «конечная точка» в подписке в качестве ключа, поскольку такая конечная точка уникальна для каждой подписки. Мы называем конечную точку API «подпиской / удалением», передавая поле «конечная точка».
  2. За этим следует вызов метода отмены подписки pushManager при регистрации активного сервис-воркера.
  3. Как только это будет сделано, текст кнопки обновится соответствующим образом.

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

Примечание о подписках на стороне клиента

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

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

Запустить push-уведомление

У нас есть подписки, давайте перейдем к самой интересной части этой публикации: отправке push-сообщений. Нам нужен способ вызвать push-уведомление. В этой простой настройке мы просто добавляем еще одну кнопку, которая отображается, как только для пользователя найдена действительная подписка. В реальном живом приложении push-уведомление будет срабатывать, когда кто-то добавляет новый пост в блог или кто-то комментирует пост. При нажатии этой кнопки сервер отправляет push-сообщение. Сервер использует конечную точку, найденную в подписке (которая хранилась в базе данных на бэкэнде, т. Е. Https://fcm.googleapis.com/fcm/send/dkjhsdkfj), и создает новое уведомление. Уведомление будет отправлено на конечную точку, которая, в свою очередь, отправит его клиенту (как событие push). На клиенте именно сервис-воркер должен прослушивать это push-событие, чтобы иметь возможность создать уведомление для пользователя. Давайте посмотрим на это в действии.

В компоненте Home мы добавляем дополнительный блок (строки 6–11), который отображается только при включенных уведомлениях. Это дает пользователю возможность ввести текст в текстовое поле и отправить его в серверную часть, нажав кнопку «Уведомить с помощью push». Наше приложение теперь выглядит так:

Нажатие на «Уведомить с помощью push» запускает метод createPushNotification (строки 135–145). Этот метод просто отправляет имя пользователя (которое было сохранено в localStorage и используется для идентификации правильного пользователя, которого нужно уведомить) и сообщение на сервер, используя новую конечную точку API «notify». Когда обещание разрешается, текстовое поле очищается.

Создание push-уведомления на сервере

Мы должны создать новую конечную точку «notify» и сделать это в routes / api.php в Laravel API. Эта конечная точка обрабатывается NotificationController, который мы также добавляем.

В этом контроллере мы добавляем функцию notify. Эта функция начинается с просмотра переданного имени пользователя. Он является обязательным и не может быть пустым, иначе мы не сможем найти соответствующего пользователя в базе данных. Предполагая, что мы действительно находим этого пользователя, мы создаем сообщение (тело Push-уведомления), просматривая текст, переданный с запросом. Если он пуст, мы сами создаем стандартный текст, в противном случае мы просто используем переданный текст. Наконец, мы используем систему уведомлений в Laravel, чтобы уведомить пользователя.

Сначала мы создаем уведомление (SayHello). Затем мы вызываем метод уведомления для экземпляра пользователя и передаем это уведомление. Уведомление просто создается с помощью ремесленной команды

php artisan make:notification SayHello

Уведомление можно отправлять по различным каналам, и WebPush - лишь один из них. В целях тестирования мы также хотели бы добавить уведомление в базу данных. Однако мы не хотим отправлять его по почте, поэтому избавляемся от этого с помощью метода via. Вот код уведомления SayHello:

Обратите внимание на два оператора использования (строки 10,11) в верхней части файла, касающиеся WebPushMessage и WebPushChannel.

Конструктор принимает сообщение, это сообщение, которое пользователь отправляет с запросом. Как уже говорилось, метод via обновлен для отправки через WebPush и добавления записи в базу данных.

Следовательно, нам нужен метод toDatabase (который просто возвращает сообщение) и метод toWebPush. Здесь мы настраиваем push-уведомление, возвращая новое сообщение WebPushMessage (строки 47–55). В этом примере мы просто добавляем заголовок и тело, но мы можем добавить гораздо более типичные свойства уведомлений, как вы можете видеть в репозитории пакета.

На этом настройка конечной точки API завершена.

Прослушивание push-события

Когда уведомление отправляется на канал WebPush, оно будет перенаправлено во внешний интерфейс. Service worker отвечает за перехват этого события и делает это, реализуя прослушиватель для события push. Следовательно, мы добавляем это в код сервис-воркера:

// Listen to Push 
self.addEventListener('push', (e) => { 
   let data 
   if (e.data) { 
      data = e.data.json() 
   }
   console.log('data for notification', data); 
   const options = { 
      body: data.body, 
      icon: '/img/icons/android-chrome-192x192.png', 
      image: '/img/autumn-forest.png', vibrate: [300, 200, 300], 
      badge: '/img/icons/plint-badge-96x96.png', 
   }
   
   console.log('options passed to Notification', options); 
   e.waitUntil(self.registration.showNotification(data.title, options)) 
})

Входящее событие (e) может содержать данные, которые мы получаем с помощью метода e.data.json (). Затем мы создаем объект параметров уведомления, который передается методу showNotification.

Сборка для производства и тестирования

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

Заключительные замечания

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

  • Как мы обрабатываем обновления кода сервис-воркера
  • А как насчет офлайн-возможностей приложения?
  • Улучшить рейтинг Маяка (сейчас 69)
  • Правильная аутентификация и логин

и многое другое.

Код этого поста можно найти на GitHub, где я сделал второй репозиторий для Laravel API. Что касается внешнего интерфейса, я просто опираюсь на репо, созданное для предыдущего поста. Код для этого поста имеет тег v0.2.

Как и прежде, демо-версия доступна на нашем демо-сайте.

Первоначально опубликовано на www.blog.plint-sites.nl 21 ноября 2018 г.