Поиск по сайту:

Как обрабатывать веб-push-уведомления на веб-сайтах и в PWA


Push-уведомления — обычное явление в современной сети. Они позволяют вам сообщать пользователю своевременную информацию, даже если ваш сайт на самом деле не открыт. Браузер пользователя обрабатывает входящие push-события и отображает уведомления с использованием системных пользовательских интерфейсов, таких как Центр действий Windows и экран блокировки Android.

Внедрение Web Push на ваш сайт или PWA требует комбинации двух разных API-интерфейсов браузера. Код, отвечающий за подписку и получение уведомлений, использует компонент Push API сервис-воркеров. Этот код постоянно работает в фоновом режиме и будет вызываться браузером, когда необходимо обработать новое уведомление.

Когда событие получено, сервис-воркер должен использовать Notification API для фактического отображения уведомления. Это создает визуальное оповещение через интерфейсы уровня ОС.

Вот полное руководство по настройке Web Push на вашем сайте. Мы предполагаем, что у вас уже есть серверный компонент, который может регистрировать push-подписки и отправлять оповещения.

Сервисный работник

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

Для Web Push требуется одно основное событие: push. Это получает объект PushEvent, который позволяет вам получить доступ к полезной нагрузке, отправленной с сервера.

self.addEventListener("push", e => {
 
    const payload = JSON.parse(e.data.text());
 
    e.waitUntil(self.registration.showNotification(
        payload.title,
        {
            body: payload.body,
            icon: "/icon.png"
        }
    ));
 
});

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

{
    "title": "Title text for the notification",
    "body": "This is the longer text of the notification."
}

При получении push-события сервис-воркер отображает уведомление браузера, вызывая функцию showNotification(), доступную в его свойстве self.registration. Функция заключена в вызов waitUntil(), поэтому браузер ожидает отображения уведомления, прежде чем завершать работу сервис-воркера.

Функция showNotification() принимает два аргумента: текст заголовка уведомления и объект параметров. В этом примере передаются два параметра: более длинный основной текст и значок для отображения в уведомлении. Доступно множество других опций, которые позволяют настраивать шаблоны вибрации, пользовательские значки и требования к взаимодействию. Не все браузеры и операционные системы поддерживают все возможности API.

Завершите часть кода сервисного работника, зарегистрировав его обратно в своем основном JavaScript:

if (navigator.serviceWorker) {
    // replace with the path to your service worker file
    navigator.serviceWorker.register("/sw.js").catch(() => {
        console.error("Couldn't register the service worker.")
    });
}

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

Регистрация для Push-подписки

Теперь вам нужно подписаться в браузере на push-уведомления. Следующий код принадлежит вашему основному файлу JavaScript, за пределами сервис-воркера.

async function subscribeToPush() {
    if (navigator.serviceWorker) {
 
        const reg = await navigator.serviceWorker.getRegistration();
 
        if (reg && reg.pushManager) {
 
            const subscription = await reg.pushManager.getSubscription();
 
            if (!subscription) {
 
                const key = await fetch("https://example.com/vapid_key");
                const keyData = await key.text();
 
                const sub = await reg.pushManager.subscribe({
                    applicationServerKey: keyData,
                    userVisibleOnly: true
                });
 
                await fetch("https://example.com/push_subscribe", {
                    method: "POST",
                    headers: {"Content-Type": "application/json"},
                    body: JSON.stringify({
                        endpoint: sub.endpoint,
                        expirationTime: sub.expirationTime,
                        keys: sub.toJSON().keys
                    })
                });
 
            }
 
        }
 
    }
}

Затем вызовите свою функцию, чтобы подписаться в браузере на push-уведомления:

await subscribeToPush();

Давайте рассмотрим, что делает код подписки. Первые несколько строк проверяют наличие работника службы, извлекают его регистрацию и обнаруживают поддержку push-уведомлений. pushManager не будет установлен в браузерах, не поддерживающих Web Push.

Вызов pushManager.getSubscription() возвращает обещание, которое преобразуется в объект, описывающий текущую push-подписку браузера для вашего сайта. Если это уже установлено, нам не нужно повторно подписывать пользователя.

Реальный процесс подписки начинается с запроса на получение ключей VAPID сервера. Спецификация VAPID — это механизм, который позволяет браузеру проверять, действительно ли push-события поступают с вашего сервера. Вы должны предоставить конечную точку API сервера, которая предоставляет ключ VAPID. Это передается функции pushManager.subscribe(), чтобы браузер знал ключ, которому можно доверять. Отдельный параметр userVisibleOnly указывает, что мы будем отображать только те уведомления, которые видны на экране.

Вызов pushManager.subscribe() возвращает объект PushSubscription, описывающий вашу новую подписку. Эти данные отправляются на сервер в другом запросе на выборку. В реальном приложении вы также отправляете идентификатор активного пользователя, чтобы связать push-подписку с его устройством.

Ваш серверный код для отправки push-уведомления пользователю должен выглядеть примерно так:

  1. Запросите в хранилище данных все push-подписки, связанные с целевым пользователем.
  2. Отправьте полезные данные уведомлений в конечную точку, указанную для каждой подписки, обязательно включив ключи аутентификации подписки (keys в данные, отправляемые браузером при подписке). Подпишите событие тем же ключом VAPID, который вы отправили в браузер.

endpoint каждой подписки будет ссылаться на платформу доставки уведомлений поставщика браузера. Этот URL-адрес уже содержит уникальный идентификатор подписки. Когда вы отправляете полезную нагрузку на конечную точку, фоновый процесс браузера в конечном итоге получит данные и вызовет вашего сервисного работника. Для Chrome на Android процесс браузера напрямую интегрирован с демоном системных уведомлений.

Когда подписывать пользователя?

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

Вы получаете наилучшие шансы на успешную регистрацию, связывая запросы на подписку с прямым действием пользователя. Рассмотрите возможность создания баннера в приложении, который объясняет преимущества включения уведомлений и предлагает кнопку «Включить сейчас». Вы можете проверить, подписался ли уже пользователь, и скрыть баннер с помощью функции pushManager.getSubscription(), показанной выше.

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

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

Вот простая реализация отписки:

async function unsubscribePush() {
 
    const reg = await navigator.serviceWorker.getRegistration();
    const subscription = await reg.pushManager.getSubscription();
 
    if (subscription) {
        await subscription.unsubscribe();
        await fetch(`https://example.com/push_unsubscribe/${subscription.endpoint}`, {method: "DELETE"});
    }
    else {
        // already subscribed
    }
 
}

Вызов unsubscribe() для PushSubscription отменяет подписку, возвращая браузер в состояние по умолчанию. Ваш сервис-воркер перестанет получать события push. Конечная точка подписки отправляется на ваш сервер, чтобы вы могли удалить ее из своего хранилища данных и избежать отправки данных на то, что теперь является мертвым URL-адресом.

Обработка истечения срока действия и продления

Возможно, вы заметили свойство expirationTime в объекте PushSubscription, созданном браузером. Это не всегда будет установлено; если это так, устройство перестанет получать уведомления по истечении этого времени.

На практике expirationTime в настоящее время не используется в основных браузерах. Срок действия токенов, созданных Chrome, не истекает до тех пор, пока вы не отмените подписку вручную, поэтому expirationTime всегда равно null. Firefox также не устанавливает expirationTime, но его служба уведомлений может заменять подписки в течение их срока действия.

Вы можете отреагировать на изменение браузером активной push-подписки, реализовав событие pushsubscriptionchange в сервис-воркере. К сожалению, существует две версии этого события: исходная реализация, используемая в настоящее время в Firefox, и новая версия v2, пока не поддерживаемая ни в одном браузере.

Первоначальная спецификация имеет серьезные проблемы с удобством использования, из-за чего сложно реагировать на событие. Когда вы получаете событие v1, браузер удалил исходную подписку, и вам нужно вручную создать новую. Проблема в том, что без доступа к подписке с истекшим сроком действия вы не можете отправить запрос на «замену» на свой сервер — у вас нет доступа к старому URL-адресу endpoint.

Спецификация v2 решает эту проблему, предоставляя событие со свойствами oldSubscription и newSubscription. Когда вы получаете событие, старая подписка была отменена, но вы все еще можете получить доступ к ее свойствам. Теперь новая подписка создана для вас браузером.

Вот пример реализации pushsubscriptionchange с новой спецификацией:

self.addEventListener("pushsubscriptionchange", e => {
    e.waitUntil(async () => {
        await fetch("https://example.com/push_change", {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify({
                auth: (e.newSubscription.toJSON().keys?.auth || null),
                endpoint: e.newSubscription.endpoint,
                endpointOld: e.oldSubscription.endpoint,
                expirationTime: e.newSubscription.expirationTime,
                p256dh: (e.newSubscription.toJSON().keys?.p256dh || null)
            })
        });
    });
});

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

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

Добавление кнопок действий

Push-уведомления могут включать интерактивные кнопки, которые позволяют пользователю выполнять немедленные действия. Вот вызов showNotification(), который его создает:

self.registration.showNotification(
    "Notification with actions",
    {
        body: "This notification has a button.",
        actions: [
            {
                action: "/home",
                title: "Go to Homescreen",
                icon: "/home.png"
            }
        ]
    }
);

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

Когда пользователь нажимает на действие, ваш сервисный работник получает событие notificationclick:

self.addEventListener("notificationclick", e => {
    const uri = e.action;
    const notification = e.notification;
    notification.close();
    clients.openWindow(`${self.location.origin}${action}`);
});

Мы используем свойство action для объявления URI, на который пользователь может перейти. Новая вкладка открывается для URI при нажатии уведомления. Вызов notification.close() гарантирует, что уведомление также будет отклонено. В противном случае некоторые платформы заставят пользователя смахнуть его вручную.

Краткое содержание

Внедрение Web Push может показаться сложной задачей, если вы раньше не работали с соответствующими API. Помимо технических проблем, вы должны держать в центре внимания удобство работы с пользователем и обязательно сообщать, почему стоит включать уведомления.

Подписка и отмена подписки на push-уведомления происходят в основном коде JavaScript вашего приложения с использованием API navigator.serviceWorker. Код, который отвечает на новые push-события и отображает уведомления браузера, находится в самом сервис-воркере.

Web Push теперь поддерживается большинством основных веб-браузеров, за исключением Safari. Помните, что уведомления будут отображаться по-разному в каждом браузере и семействе операционных систем, поэтому не думайте, что конкретная функция API showNotification() будет доступна повсеместно.




Все права защищены. © Linux-Console.net • 2019-2024