Руководство по получению максимальной отдачи от Push API

1656642972 rukovodstvo po polucheniyu maksimalnoj otdachi ot push api

Хотите изучить JavaScript? Получите мою электронную книгу на jshandbook.com

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

API Push является недавним дополнением к API браузеру, и сейчас он поддерживается Chrome (для настольных и мобильных устройств), Firefox и Opera с 2016 года.

IE и Edge еще не поддерживают его, а Safari имеет свою реализацию. Поскольку Chrome и Firefox поддерживают его, примерно 60% пользователей, просматривающих веб-страницы на своих настольных компьютерах, имеют доступ к нему, поэтому пользоваться им вполне безопасно.

Что с ним можно сделать

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

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

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

Как это работает

Когда пользователь посещает веб-приложение, вы можете вызвать панель с запросом разрешения на отправку обновлений. Установлен Service Worker, который работает в фоновом режиме, прослушивая Push-событие.

Push и Notifications – это две отдельные концепции и API. Иногда их путают из-за push-сообщение термин, используемый в iOS. По существу, Notifications API вызывается, когда поступает push-событие с помощью Push API.

ваш сервер посылает уведомление клиенту, а Service Worker, если ему предоставлено разрешение, получает a push событие. Service Worker реагирует на это событие активация оповещения.

Получение разрешения пользователя

Первым шагом в работе с Push API является получение разрешения пользователя на получение данных от вас.

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

Есть шесть шагов, чтобы получить разрешение от вашего пользователя:

  1. Проверьте, поддерживаются ли Service Workers
  2. Убедитесь, что Push API поддерживается
  3. Зарегистрируйте сервисного работника
  4. Запрашивать разрешение у пользователя
  5. Подпишитесь на пользователя и получите объект PushSubscription
  6. Отправьте объект PushSubscription на свой сервер

Давайте посмотрим их один за другим.

Проверьте, поддерживаются ли Service Workers

if (!('serviceWorker' in navigator)) {  // Service Workers are not supported. Return  return}

Убедитесь, что Push API поддерживается

if (!('PushManager' in window)) {  // The Push API is not supported. Return  return}

Зарегистрируйте сервисного работника

Этот код регистрирует Service Worker, расположенный в worker.js файл, размещенный в корне домена:

window.addEventListener('load', () => {  navigator.serviceWorker.register('/worker.js')  .then((registration) => {    console.log('Service Worker registration completed with scope: ',      registration.scope)  }, (err) => {    console.log('Service Worker registration failed', err)  })})

Дополнительные сведения о том, как работают Service Workers, см. в руководстве Service Workers.

Запрашивать разрешение у пользователя

Теперь, когда Service Worker зарегистрирован, вы можете запросить разрешение.

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

Код вызова следующий Notification.requestPermission().

const askPermission = () => {  return new Promise((resolve, reject) => {    const permissionResult = Notification.requestPermission(      (result) => {        resolve(result)      }    )    if (permissionResult) {      permissionResult.then(resolve, reject)    }  })  .then((permissionResult) => {    if (permissionResult !== 'granted') {      throw new Error('Permission denied')    }  })}

The permissionResult Значение – это строка, которая может иметь значение: – granteddefaultdenied

Этот код заставляет браузер показывать диалоговое окно разрешения:

S3HKfHLPj2Vg24jR3u0m0letQehHJjKwcqZ0

Если пользователь нажмет «Заблокировать», вы больше не сможете запрашивать разрешение пользователяесли они вручную не разблокируют сайт на панели расширенных настроек в браузере (очень маловероятно).

Если пользователь дал нам разрешение, мы можем подписаться на него, позвонив по телефону registration.pushManager.subscribe().

const APP_SERVER_KEY = 'XXX'window.addEventListener('load', () => {  navigator.serviceWorker.register('/worker.js')  .then((registration) => {    askPermission().then(() => {      const options = {        userVisibleOnly: true,        applicationServerKey: urlBase64ToUint8Array(APP_SERVER_KEY)      }      return registration.pushManager.subscribe(options)    }).then((pushSubscription) => {      // we got the pushSubscription object    }  }, (err) => {    console.log('Service Worker registration failed', err)  })})

APP_SERVER_KEY это строка — вызывается Ключ сервера приложений или Ключ VAPID который идентифицирует открытый ключ программы, часть пары открытый/частный ключ.

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

Отправьте объект PushSubscription на свой сервер

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

Сначала мы создаем JSON-представление объекта

const subscription = JSON.stringify(pushSubscription)

и мы можем опубликовать его на нашем сервере с помощью Fetch API:

const sendToServer = (subscription) => {  return fetch('/api/subscription', {    method: 'POST',    headers: {      'Content-Type': 'application/json'    },    body: JSON.stringify(subscription)  })  .then((res) => {    if (!res.ok) {      throw new Error('An error occurred')    }    return res.json()  })  .then((resData) => {    if (!(resData.data && resData.data.success)) {      throw new Error('An error occurred')    }  })}sendToServer(subscription)

На стороне сервера, /api/subscription конечная точка получает запрос POST и может хранить информацию о подписке в своем хранилище.

Как работает сторона сервера

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

А как насчет сервера? Что он должен делать и как он должен взаимодействовать с клиентом?

Эти примеры на стороне сервера используют Express.js как базовую структуру HTTP, но вы можете написать обработчик Push API на стороне сервера на любом языке или структуре.

Регистрация новой клиентской подписки

Когда клиент посылает новую подписку, помните, что мы использовали /api/subscription Конечная точка HTTP POST, отправляющая детали объекта PushSubscription в формате JSON в теле.

Инициализируем Express.js:

const express = require('express')const app = express()

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

const isValidSaveRequest = (req, res) => {  if (!req.body || !req.body.endpoint) {    res.status(400)    res.setHeader('Content-Type', 'application/json')    res.send(JSON.stringify({      error: {        id: 'no-endpoint',        message: 'Subscription must have an endpoint'      }    }))    return false  }  return true}

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

const saveSubscriptionToDatabase = (subscription) => {  return new Promise((resolve, reject) => {    insertToDatabase(subscription, (err, id) => {      if (err) {        reject(err)        return      }      resolve(id)    })  })}

Мы используем эти функции в обработчике запросов POST ниже. Мы проверяем, действителен ли запрос, затем сохраняем запрос и возвращаем a data.success: true ответ клиенту или ошибка:

app.post('/api/subscription', (req, res) => {  if (!isValidSaveRequest(req, res)) {    return  }  saveSubscriptionToDatabase(req, res.body)  .then((subscriptionId) => {    res.setHeader('Content-Type', 'application/json')    res.send(JSON.stringify({ data: { success: true } }))  })  .catch((err) => {    res.status(500)    res.setHeader('Content-Type', 'application/json')    res.send(JSON.stringify({      error: {        id: 'unable-to-save-subscription',        message: 'Subscription received but failed to save it'      }    }))  })})app.listen(3000, () => {  console.log('App listening on port 3000')})

Отправка Push-сообщения

Теперь, когда сервер зарегистрировал клиента в своем списке, мы можем посылать ему Push-сообщение. Давайте посмотрим, как это работает, создав пример фрагмента кода, который получает все подписки и отправляет Push-сообщения всем им одновременно.

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

В этом примере используется web-push Библиотека Node.js для обработки отправки Push-сообщения.

Сначала мы инициализируем web-push lib, и мы генерируем кортеж частных и открытых ключей и устанавливаем их как детали VAPID:

const webpush = require('web-push')const vapidKeys = webpush.generateVAPIDKeys()const PUBLIC_KEY = 'XXX'const PRIVATE_KEY = 'YYY'const vapidKeys = {  publicKey: PUBLIC_KEY,  privateKey: PRIVATE_KEY}webpush.setVapidDetails(  'mailto:my@email.com',  vapidKeys.publicKey,  vapidKeys.privateKey)

Затем мы создали a triggerPush() метод, ответственный за отправку события push клиенту. Просто звонит webpush.sendNotification() и обнаруживает любую ошибку. Если ошибка возвращает код статуса HTTP 410, это означает ушелмы удаляем этого абонента из базы данных.

const triggerPush = (subscription, dataToSend) => {  return webpush.sendNotification(subscription, dataToSend)  .catch((err) => {    if (err.statusCode === 410) {      return deleteSubscriptionFromDatabase(subscription._id)    } else {      console.log('Subscription is no longer valid: ', err)    }  })}

Мы не реализуем получение подписок из базы данных, но оставляем это как заглушку:

const getSubscriptionsFromDatabase = () => {  //stub}

Основой кода является обратный вызов запросу POST в /api/push конечная точка:

app.post('/api/push', (req, res) => {  return getSubscriptionsFromDatabase()  .then((subscriptions) => {    let promiseChain = Promise.resolve()    for (let i = 0; i < subscriptions.length; i++) {      const subscription = subscriptions[i]      promiseChain = promiseChain.then(() => {        return triggerPush(subscription, dataToSend)      })    }    return promiseChain  })  .then(() => {    res.setHeader('Content-Type', 'application/json')    res.send(JSON.stringify({ data: { success: true } }))  })  .catch((err) => {    res.status(500)    res.setHeader('Content-Type', 'application/json')    res.send(JSON.stringify({      error: {        id: 'unable-to-send-messages',        message: `Failed to send the push ${err.message}`      }    }))  })})

Приведенный выше код получает все подписки из базы данных, затем выполняет их итерацию и вызывает triggerPush() функцию, которую мы объясняли ранее.

После завершения подписки мы возвращаем успешный ответ JSON. Если не произошла ошибка, мы возвращаем ошибку 500.

В реальном мире…

Маловероятно, что вы настроите свой собственный сервер Push, если у вас нет особого случая использования или вы просто хотите изучить технологию, или вам нравится DIY.

Вместо этого вы обычно хотите использовать такие платформы, как OneSignal, которые прозрачно бесплатно обрабатывают Push-события для всех типов платформ, включая Safari и iOS.

Получить событие

Когда событие Push отправляется с сервера, как клиент получает его?

Это обычный прослушиватель JavaScript, на push событие, выполняемое внутри Service Worker:

self.addEventListener('push', (event) => {  // data is available in event.data})

event.data содержит PushMessageData объект, который предоставляет методы для получения push-данных, отправленных сервером, в нужном формате:

  • arrayBuffer() : как объект ArrayBuffer
  • blob(): как объект Blob
  • json(): анализируется как JSON
  • текст(): простой текст

Вы обычно используете event.data.json().

Отображение уведомления

Здесь мы немного пересекаемся с Notifications API, но по уважительной причине, поскольку одним из основных случаев использования Push API является отображение уведомлений.

Внутри нашего push слушателя событий в Service Worker, нам нужно отобразить уведомление пользователя. Нам также нужно сообщить событие дождаться, пока браузер не покажет его, прежде чем функция сможет завершить работу. Мы продлеваем срок действия события, пока браузер не завершит отображение уведомления (пока обещание не будет решено), иначе Service Worker может быть остановлен во время вашей обработки:

self.addEventListener('push', (event) => {  const promiseChain = self.registration.showNotification('Hey!')  event.waitUntil(promiseChain)})

Хотите изучить JavaScript? Получите мою электронную книгу на jshandbook.com

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *