- PVSM.RU - https://www.pvsm.ru -
Сегодня публикуем перевод девятой части серии статей, посвящённых применению веб-технологий и JavaScript. В этом материале мы исследуем веб push-уведомления. А именно, поговорим о механизмах, лежащих в их основе, и о том, как осуществляется подписка на уведомления, как устроены процессы их отправки и получения.
Сложилось так, что push-уведомления, весьма распространённые в мире мобильных приложений, довольно поздно добрались до веба, хотя они являются одной из тех возможностей, которыми хотели бы пользоваться многие разработчики.
Технология, о которой мы тут говорим, позволяет пользователям подписываться на периодические уведомления веб-приложений, которые направлены на то, чтобы сообщать подписчикам о появлении новых материалов, или возникновении событий, которые могут представлять для них интерес. С точки зрения самого веб-ресурса это означает наличие повода и возможности пригласить пользователей, подписавшихся на push-уведомления, посетить этот ресурс.
Одним из механизмом, обеспечивающих работу push-уведомлений, являются сервис-воркеры [2]. Сервис-воркеры, обрабатывающие push-уведомления, экономно расходуют системные ресурсы, так как их код выполняется только тогда, когда в браузер поступает новое уведомление, за работу с которым ответственен конкретный сервис-воркер.
То, что мы называем тут «веб push-уведомлениями», на самом деле, представлено двумя технологиями. Это — Push API [3], которое используется, когда сервер передаёт сообщение сервис-воркеру, и Notifications API [4], которое применяется, когда сервис-воркер, или скрипт в самом веб-приложении, намеревается показать пользователю уведомление.
Для реализации механизма push-уведомлений нужно выполнить следующие три шага:
Рассмотрим всё это подробнее.
Для начала надо узнать, поддерживает ли текущий браузер push-уведомления. Сделать это можно, выполнив следующие проверки:
serviceWorker
в объекте navigator
.PushManager
в объекте window
.Вот как это выглядит:
if (!('serviceWorker' in navigator)) {
// Браузер не поддерживает сервис-воркеры.
return;
}
if (!('PushManager' in window)) {
// Браузер не поддерживает push-уведомления.
return;
}
Если браузер пользователя не поддерживает технологии, необходимые для работы веб push-уведомлений, нет смысла предлагать пользователю на них подписаться.
Если после проверки браузера оказалось, что он поддерживает то, что нам нужно, можно переходить к регистрации сервис-воркера. О том, как это сделать, мы уже говорили [2].
После того, как сервис-воркер будет зарегистрирован, можно запустить процедуру подписки пользователя на уведомления. Для того чтобы это сделать, нужно получить разрешение пользователя на отправку ему push-сообщений.
API для получения разрешений устроено сравнительно просто. Единственное, на что тут надо обратить внимание, заключается в том, что сейчас применяются две версии этого API [5].
В более старой его версии оно принимало функцию обратного вызова, теперь оно возвращает промис. Это и является источником проблемы, так как нельзя заранее узнать, какая версия API реализована в текущем браузере. Поэтому нужно поддерживать оба этих подхода.
Выглядит это так:
function requestPermission() {
return new Promise(function(resolve, reject) {
const permissionResult = Notification.requestPermission(function(result) {
// Поддержка устаревшей версии с функцией обратного вызова.
resolve(result);
});
if (permissionResult) {
permissionResult.then(resolve, reject);
}
})
.then(function(permissionResult) {
if (permissionResult !== 'granted') {
throw new Error('Permission not granted.');
}
});
}
Вызов Notification.requestPermission()
приведёт к показу следующего окна.
Запрос разрешения на показ уведомлений
После того, как пользователь отреагирует на запрос разрешения, нажав на кнопку Allow
(Разрешить), Block
(Блокировать), или закрыв окно, мы получим результат в виде строки, в которой, в соответствии с выбором пользователя, будет содержаться ‘granted’
, ‘denied’
, или ‘default’
.
Помните о том, что если пользователь нажмёт на кнопку Block
, ваше приложение не сможет больше запрашивать у него разрешение на показ уведомлений до тех пор, пока он самостоятельно не «разблокирует» его. Для того чтобы это сделать, пользователю придётся покопаться в настройках браузера.
После того, как мы убедились в возможности регистрации сервис-воркера и получили разрешение пользователя на показ уведомлений, можно оформить подписку, вызвав, во время регистрации сервис-воркера, метод registration.pushManager.subscribe()
.
Вот как всё это, включая регистрацию сервис-воркера, выглядит:
function subscribeUserToPush() {
return navigator.serviceWorker.register('service-worker.js')
.then(function(registration) {
var subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: btoa(
'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U'
)
};
return registration.pushManager.subscribe(subscribeOptions);
})
.then(function(pushSubscription) {
console.log('PushSubscription: ', JSON.stringify(pushSubscription));
return pushSubscription;
});
}
Метод registration.pushManager.subscribe(options)
принимает объект options
, который содержит ряд параметров, некоторые из которых необязательны:
userVisibleOnly
. Это логическое значение указывает на то, что сформированная подписка будет использована лишь для показа сообщений. Этот параметр должен быть установлен в true
, в противном случае мы столкнёмся с ошибкой (у этого есть исторические причины).applicationServerKey
. Это — DOMString
в кодировке Base64, или объект ArrayBuffer
, который содержит открытый ключ, который push-сервер будет использовать для аутентификации сервера приложения.Сервер веб-приложения должен сгенерировать пару уникальных для сервера ключей приложения (их ещё называют VAPID-ключами). В эту пару входят открытый и закрытый ключи. Закрытый ключ сервер хранит в тайне, а открытый передаёт клиенту. Ключи позволяют сервису push-уведомлений знать о том, какой сервер приложения подписал пользователя, и быть уверенным в том, что это — тот же самый сервер, который отправляет уведомления конкретному пользователю.
Пару ключей для приложения нужно создать лишь один раз. Для того чтобы сгенерировать ключи, можете заглянуть сюда [6].
Браузер передаёт applicationServerKey
(открытый ключ) push-сервису в ходе оформления подписки. Это означает, что push-сервис сможет связать открытый ключ приложения с подпиской, PushSubscription
.
Вот что здесь происходит:
subscribe()
, передавая серверный ключ.
PushSubscription
, который возвращается через промис subscribe()
.
Позже, когда требуется отправить push-уведомление, нужно создать заголовок Authorization
, который содержит информацию, подписанную закрытым серверным ключом приложения. Когда push-сервис получит запрос на отправку уведомления, он проверит заголовок, использовав открытый ключ, который уже связан с точкой входа в API на втором шаге описанного выше процесса.
Объект PushSubscription
содержит всю информацию, необходимую для отправки push-уведомлений на устройство пользователя. Вот как он выглядит:
{
"endpoint": "https://domain.pushservice.com/some-id",
"keys": {
"p256dh":
"BIPUL12DLfytvTajnryr3PJdAgXS3HGMlLqndGcJGabyhHheJYlNGCeXl1dn18gSJ1WArAPIxr4gK0_dQds4yiI=",
"auth":"FPssMOQPmLmXWmdSTdbKVw=="
}
}
Свойство endpoint
представляет собой URL сервиса push-уведомлений, точку входа в API. Для того, чтобы отправить уведомление, надо выполнить POST-запрос по этому URL.
Объект keys
содержит сведения, используемые для шифрования данных сообщения, отправляемых в push-уведомлении.
Когда пользователь будет подписан на уведомления, и у вас будет объект PushSubscription
, нужно отправить его на сервер. На сервере содержимое этого объекта нужно сохранить, в базе данных, например, и с этого момента эту информацию можно использовать для отправки push-уведомлений соответствующему пользователю.
Получение разрешений, формирование объекта PushSubscription и отправка его на сервер
При отправке пользователю push-сообщения нужно сообщить push-сервису (посредством вызова соответствующего метода API) о том, какие данные надо отправить, кому их надо отправить, и любые дополнительные сведения о сообщении. Обычно этот вызов выполняется с сервера веб-приложения.
Push-сервис — это система, которая получает запросы на отправку push-уведомлений, проверяет их и доставляет уведомления в соответствующий браузер.
Обратите внимание на то, что push-сервисы — это сторонние службы, которые вы, как разработчик веб-приложения, не контролируете. Ваши серверы — это те серверы, которые взаимодействуют с push-сервисами через API. В качестве примера push-сервиса можно привести Google FСM [7].
Push-сервис берёт на себя выполнение множества сложных задач. Например, если браузер не в сети, push-сервис поставит сообщения в очередь и будет ждать, перед отправкой сообщения, до тех пор, пока браузер не окажется доступным.
Каждый браузер может использовать любой push-сервис, разработчик веб-приложения не влияет на выбор push-сервиса. Все push-сервисы, однако, имеют одинаковые API, поэтому разнообразие таких сервисов не создаёт проблем с реализацией механизмов push-уведомлений. Для того, чтобы получить URL, который будет обрабатывать запросы на отправку ваших push-сообщений, нужно обратиться к сохранённому ранее значению параметра endpoint
объекта PushSubscription
.
API push-сервисов предоставляет инструменты для отправки сообщений пользователям. Оно представлено протоколом Web Push Protocol [8], который является стандартом IETF, определяющим порядок работы с push-сервисами.
Данные, которые отправляют в push-сообщении, должны быть зашифрованы. Так разработчик не даёт push-сервисам просматривать данные, отправляемые пользователям. Это важно, так как именно браузер решает, какой push-сервис использовать (это могут быть push-сервисы, которые недостаточно безопасны).
Для каждого push-сообщения задаются следующие свойства:
TTL
— определяет срок, который недоставленное push-уведомление может провести в очереди до его удаления.
Priority
— задаёт приоритет сообщения, что позволяет push-сервису отправлять только высокоприоритетные сообщения в том случае, если нужно экономить заряд батареи устройства пользователя.
Topic
— назначает push-уведомлению имя темы, что приведёт к замене ожидающих доставки сообщений с той же темой. В результате, как только устройство пользователя окажется активным, пользователь получит актуальное сообщение.
Сервер разработчика веб-приложения, push-сервер, и браузер, в который поступает сообщение
Как только сообщение будет отправлено push-сервису, оно будет пребывать в состоянии ожидания до тех пор, пока не произойдёт одно из следующих событий:
Когда push-сервис доставляет сообщение, браузер получает его, расшифровывает, и вызывает событие push в зарегистрированном ранее сервис-воркере.
Самое интересное здесь то, что браузер может вызывать сервис-воркер даже в том случае, если соответствующая ему веб-страница не открыта. Тут происходит следующее:
Код, который используется для настройки обработчика события push, выглядит так же, как код любого другого обработчика событий, написанного на JavaScript:
self.addEventListener('push', function(event) {
if (event.data) {
console.log('This push event has data: ', event.data.text());
} else {
console.log('This push event has no data.');
}
});
Если говорить об особенностях сервис-воркеров, то надо отметить, что разработчик обладаем минимальным уровнем контроля над тем, сколько времени будет выполняться сервис-воркер, так как именно браузер решает, когда нужно его активировать, а когда — остановить.
Конструкция сервис-воркера event.waitUntil(promise)
сообщает браузеру о том, что, до разрешения промиса, сервис-воркер занят обработкой уведомления, и браузеру не следует завершать работу сервис-воркера до завершения этой работы.
Вот пример кода для обработки события push:
self.addEventListener('push', function(event) {
var promise = self.registration.showNotification('Push notification!');
event.waitUntil(promise);
});
Вызов self.registration.showNotification()
приводит к выводу уведомления, которое может увидеть пользователь, этот вызов возвращает промис, который будет разрешён как только уведомление будет показано.
Метод showNotification(title, options)
позволяет настроить внешний вид уведомления в соответствии с нуждами разработчика. Так, параметр title
— это строка, а параметр options
— это объект примерно следующего содержания:
{
"//": "Visual Options",
"body": "<String>",
"icon": "<URL String>",
"image": "<URL String>",
"badge": "<URL String>",
"vibrate": "<Array of Integers>",
"sound": "<URL String>",
"dir": "<String of 'auto' | 'ltr' | 'rtl'>",
"//": "Behavioural Options",
"tag": "<String>",
"data": "<Anything>",
"requireInteraction": "<boolean>",
"renotify": "<Boolean>",
"silent": "<Boolean>",
"//": "Both Visual & Behavioural Options",
"actions": "<Array of Strings>",
"//": "Information Option. No visual affect.",
"timestamp": "<Long>"
}
Здесь [9] можно почитать подробности о настройке внешнего вида уведомлений.
Push-уведомления способны принести пользу и ресурсам, на которых они применяются, и пользователям этих ресурсов. Пожалуй, главное, о чём стоит помнить владельцам веб-ресурсов, заключается в том, что их push-уведомления должны содержать что-то такое, что действительно интересно и нужно пользователям.
Автор этого материала говорит, что в его компании, SessionStack [10], планируют использовать push-уведомления для того, чтобы сообщать пользователям о сбоях, проблемах или аномалиях в их проектах. Это позволит им мгновенно узнавать о внештатных ситуациях и принимать соответствующие меры.
Предыдущие части цикла статей:
Часть 1: Как работает JS: обзор движка, механизмов времени выполнения, стека вызовов [11]
Часть 2: Как работает JS: о внутреннем устройстве V8 и оптимизации кода [12]
Часть 3: Как работает JS: управление памятью, четыре вида утечек памяти и борьба с ними [13]
Часть 4: Как работает JS: цикл событий, асинхронность и пять способов улучшения кода с помощью async / await [14]
Часть 5: Как работает JS: WebSocket и HTTP/2+SSE. Что выбрать? [15]
Часть 6: Как работает JS: особенности и сфера применения WebAssembly [16]
Часть 7: Как работает JS: веб-воркеры и пять сценариев их использования [17]
Часть 8: Как работает JS: сервис-воркеры [2]
Уважаемые читатели! Пользуетесь ли вы push-уведомлениями в своих проектах?
Автор: ru_vds
Источник [18]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/274514
Ссылки в тексте:
[1] Image: https://habrahabr.ru/company/ruvds/blog/350486/
[2] сервис-воркеры: https://habrahabr.ru/company/ruvds/blog/349858/
[3] Push API: https://developer.mozilla.org/en-US/docs/Web/API/Push_API
[4] Notifications API: https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API
[5] этого API: https://developer.mozilla.org/en-US/docs/Web/API/Notification/requestPermission
[6] сюда: https://web-push-codelab.glitch.me/
[7] Google FСM: https://firebase.google.com/docs/cloud-messaging/
[8] Web Push Protocol: https://tools.ietf.org/html/draft-ietf-webpush-protocol-12
[9] Здесь: https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/showNotification
[10] SessionStack: https://www.sessionstack.com/
[11] Как работает JS: обзор движка, механизмов времени выполнения, стека вызовов: https://habrahabr.ru/company/ruvds/blog/337042/
[12] Как работает JS: о внутреннем устройстве V8 и оптимизации кода: https://habrahabr.ru/company/ruvds/blog/337460/
[13] Как работает JS: управление памятью, четыре вида утечек памяти и борьба с ними: https://habrahabr.ru/company/ruvds/blog/338150/
[14] Как работает JS: цикл событий, асинхронность и пять способов улучшения кода с помощью async / await: https://habrahabr.ru/company/ruvds/blog/340508/
[15] Как работает JS: WebSocket и HTTP/2+SSE. Что выбрать?: https://habrahabr.ru/company/ruvds/blog/342346/
[16] Как работает JS: особенности и сфера применения WebAssembly: https://habrahabr.ru/company/ruvds/blog/343568/
[17] Как работает JS: веб-воркеры и пять сценариев их использования: https://habrahabr.ru/company/ruvds/blog/348424/
[18] Источник: https://habrahabr.ru/post/350486/?utm_source=habrahabr&utm_medium=rss&utm_campaign=350486
Нажмите здесь для печати.