push api v3_充分利用Push API的指南

push api v3

Interested in learning JavaScript? Get my ebook at jshandbook.com

有兴趣学习JavaScript吗? 在jshandbook.com上获取我的电子书

The Push API allows a web app to receive messages pushed by a server, even if the web app is not currently open in the browser or not running on the device.

即使Web应用程序当前未在浏览器中打开或未在设备上运行,Push API仍允许Web应用程序接收服务器推送的消息。

The Push API is a recent addition to browser APIs, and it’s currently supported by Chrome (Desktop and Mobile), Firefox, and Opera since 2016.

推送API是浏览器API的最新功能,自2016年以来,Chrome(台式机和移动设备),Firefox和Opera目前已支持它。

IE and Edge do not support it yet, and Safari has its own implementation of it. Since Chrome and Firefox support it, approximately 60% of users browsing on their desktops have access to it, so it’s quite safe to use.

IE和Edge尚不支持它,而Safari 具有自己的实现 。 由于Chrome和Firefox支持它,因此大约60%的用户在其台式机上浏览时都可以访问它,因此使用起来非常安全。

你能做什么 (What can you do with it)

You can send messages to your users, pushing them from the server to the client, even when the user is not browsing the site.

您可以向用户发送消息,即使用户没有浏览站点,也可以将消息从服​​务器推送到客户端。

This lets you deliver notifications and content updates, giving you the ability to engage more with your audience.

这使您可以发送通知和内容更新,使您能够与更多的受众互动。

This is huge, because one of the missing pillars of the mobile web, compared to native apps, used to be the ability to receive notifications, along with offline support.

这是巨大的,因为与本机应用程序相比,移动网络缺少的Struts之一曾经是能够接收通知以及脱机支持的功能。

这个怎么运作 (How it works)

When a user visits your web app, you can trigger a panel asking permission to send updates. A Service Worker is installed, and operates in the background listening for a Push Event.

当用户访问您的Web应用程序时,您可以触发一个面板,询问是否允许发送更新。 Service Worker已安装,并在后台运行,以监听Push Event

Push and Notifications are two separate concepts and APIs. They’re sometimes mixed up because of the push notifications term used in iOS. Basically, the Notifications API is invoked when a push event is received using the Push API.

推送和通知是两个独立的概念和API。 由于iOS中使用的推送通知术语,有时会混淆它们。 基本上,当使用Push API接收到push事件时,将调用Notifications API。

Your server sends the notification to the client, and the Service Worker, if given permission, receives a push event. The Service Worker reacts to this event by triggering a notification.

您的服务器将通知发送给客户端,并且如果获得许可,则Service Worker会收到push事件 。 服务人员通过触发通知来对此事件做出React。

获得用户的许可 (Getting the user’s permission)

The first step in working with the Push API is getting the user’s permission to receive data from you.

使用Push API的第一步是获得用户的许可,以从您接收数据。

Many sites implement this panel badly, showing it on the first page load. The user is not yet convinced your content is good, and they will deny the permission. So do it wisely.

许多站点在实施此面板时效果很差,在首页加载时就显示出来。 用户尚未确信您的内容很好,他们将拒绝该许可。 明智地这样做。

There are six steps to getting permission from your user:

要获得用户许可,有六个步骤:

  1. Check if Service Workers are supported

    检查服务工作者是否受支持
  2. Check if the Push API is supported

    检查是否支持Push API
  3. Register a Service Worker

    注册服务人员
  4. Request permission from the user

    向用户请求权限
  5. Subscribe the user and get the PushSubscription object

    订阅用户并获取PushSubscription对象
  6. Send the PushSubscription object to your server

    将PushSubscription对象发送到您的服务器

Let’s go through them one by one.

让我们一一介绍。

检查服务工作者是否受支持 (Check if Service Workers are supported)

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

检查是否支持Push API (Check if the Push API is supported)

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

注册服务人员 (Register a Service Worker)

This code registers the Service Worker located in the worker.js file placed in the domain root:

此代码在位于域根目录中的worker.js文件中注册了Service Worker:

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)  })})

To know more about how Service Workers work in detail, check out the Service Workers guide.

要了解有关服务人员详细工作方式的更多信息,请查阅《 服务人员指南》

向用户请求权限 (Request permission from the user)

Now that the Service worker is registered, you can request the permission.

现在,服务工作者已注册,您可以请求权限。

The API to do this has changed over time, and it went from accepting a callback function as a parameter to returning a Promise, breaking the backward and forward compatibility. And note that we need to do both, as we don’t know which approach is implemented by the user’s browser.

执行此操作的API随时间变化,从接受回调函数作为参数到返回Promise ,打破了向后和向前的兼容性。 请注意,我们需要同时执行这两项操作,因为我们不知道用户的浏览器实现了哪种方法。

The code is the following, calling Notification.requestPermission().

代码如下,调用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 value is a string, that can have the value of: - granted - default - denied

permissionResult值是一个字符串,其值可以是:- granted - default - denied

This code causes the browser to show the permission dialogue:

此代码使浏览器显示权限对话框:

If the user clicks Block, you won’t be able to ask for the user’s permission any more, unless they manually go and unblock the site in an advanced settings panel in the browser (very unlikely to happen).

如果用户单击“阻止”,则您将无法再请求该用户的许可 ,除非他们在浏览器的高级设置面板中手动进行并取消阻止该站点(极不可能发生)。

If the user gave us permission, we can subscribe them by calling registration.pushManager.subscribe().

如果用户授予我们许可,我们可以通过调用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 is a string — called Application Server Key or VAPID key that identifies the applications’s public key, part of a public / private key pair.

APP_SERVER_KEY是一个字符串-称为应用程序服务器密钥VAPID密钥 - 标识应用程序的公钥,公钥/私钥对的一部分。

It will be used as part of the validation that, for security reasons, comes up to make sure you (and only you, not someone else) can send a push message back to the user.

出于安全原因,它将用作验证的一部分,以确保您(只有您自己,而不是其他人)可以将推送消息发送回用户。

将PushSubscription对象发送到您的服务器 (Send the PushSubscription object to your server)

In the previous snippet we got the pushSubscription object, which contains all we need to send a push message to the user. We need to send this information to our server so we’re able to send notifications later on.

在上一个代码段中,我们获得了pushSubscription对象,该对象包含将推送消息发送给用户所需的全部内容。 我们需要将此信息发送到我们的服务器,以便稍后能够发送通知。

We first create a JSON representation of the object

我们首先创建对象的JSON表示形式

const subscription = JSON.stringify(pushSubscription)

and we can post it to our server using the Fetch API:

我们可以使用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)

Server-side, the /api/subscription endpoint receives the POST request and can store the subscription information into its storage.

在服务器端, /api/subscription端点接收POST请求,并将订阅信息存储到其存储中。

服务器端如何工作 (How the Server side works)

So far we only talked about the client-side part: getting a user’s permission to be notified in the future.

到目前为止,我们仅讨论了客户端部分:获取用户的许可以在将来进行通知。

What about the server? What should it do, and how should it interact with the client?

那服务器呢? 它应该做什么,以及如何与客户互动?

These server-side examples use Express.js as the base HTTP framework, but you can write a server-side Push API handler in any language or framework

这些服务器端示例使用Express.js作为基本HTTP框架,但是您可以使用任何语言或框架编写服务器端Push API处理程序

注册新的客户订阅 (Registering a new client subscription)

When the client sends a new subscription, remember that we used the /api/subscription HTTP POST endpoint, sending the PushSubscription object details in JSON format, in the body.

当客户端发送新的订阅时,请记住,我们使用了/api/subscription HTTP POST端点,在正文中以JSON格式发送了PushSubscription对象的详细信息。

We initialize Express.js:

我们初始化Express.js:

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

This utility function makes sure the request is valid and has a body and an endpoint property, otherwise it returns an error to the client:

此实用程序功能可确保请求有效,并且具有正文和终结点属性,否则它将向客户端返回错误:

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 next utility function saves the subscription to the database, returning a promise resolved when the insertion completed (or failed). The insertToDatabase function is a placeholder — we’re not going into those details here:

下一个实用程序功能将预订保存到数据库,并在插入完成(或失败)时返回已解决的promise。 insertToDatabase函数是一个占位符-我们在这里不再赘述:

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

We use those functions in the POST request handler below. We check if the request is valid, then we save the request and return a data.success: true response back to the client, or an error:

我们在下面的POST请求处理程序中使用这些功能。 我们检查请求是否有效,然后保存请求并返回data.success: true返回给客户端的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')})

发送推送消息 (Sending a Push message)

Now that the server has registered the client in its list, we can send it Push messages. Let’s see how that works by creating an example code snippet that fetches all subscriptions and sends a Push message to all of them at the same time.

现在,服务器已在其列表中注册了客户端,我们可以向其发送Push消息。 让我们通过创建一个示例代码片段来查看其工作原理,该示例代码片段提取所有订阅并同时向所有订阅发送一个Push消息。

We use a library because the Web Push protocol is complex, and a lib allows us to abstract away a lot of low level code that makes sure we can work safely and can correctly handle any edge case.

我们使用一个库是因为Web Push协议很复杂,而一个lib使我们可以抽象出很多低级代码,以确保我们可以安全地工作并可以正确处理任何边缘情况。

This example uses the web-push Node.js library to handle sending the Push message.

本示例使用web-push Node.js 处理发送Push消息。

We first initialize the web-push lib, and we generate a tuple of private and public keys, and set them as the VAPID details:

我们首先初始化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)

Then we set up a triggerPush() method, responsible for sending the push event to a client. It just calls webpush.sendNotification() and catches any error. If the return error HTTP status code is 410, which means gone, we delete that subscriber from the database.

然后,我们设置一个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)    }  })}

We don’t implement getting the subscriptions from the database, but we leave it as a stub:

我们没有实现从数据库获取订阅,但是将其保留为存根:

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

The meat of the code is the callback of the POST request to the /api/push endpoint:

代码的本质是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}`      }    }))  })})

The above code gets all the subscriptions from the database, then it iterates on them, and it calls the triggerPush() function we explained before.

上面的代码从数据库中获取所有订阅,然后对其进行迭代,然后调用我们之前介绍的triggerPush()函数。

Once the subscriptions are done, we return a successful JSON response. Unless an error occurred, and then we return a 500 error.

订阅完成后,我们将返回成功的JSON响应。 除非发生错误,否则我们将返回500错误。

在现实世界… (In the real world…)

It’s unlikely that you’ll set up your own Push server unless you have a very special use case, or you just want to learn the tech or you like to DIY.

除非您有非常特殊的用例,或者您只是想学习技术或喜欢DIY,否则不太可能设置自己的Push服务器。

Instead, you’ll usually want to use platforms such as OneSignal which transparently handle Push events to all kind of platforms, Safari and iOS included, for free.

取而代之的是,您通常要使用OneSignal这样的平台,该平台可以透明地免费处理所有平台(包括Safari和iOS)的Push事件。

接收推送事件 (Receive a Push event)

When a Push event is sent from the server, how does the client get it?

从服务器发送Push事件时,客户端如何获取它?

It’s a normal JavaScript event listener, on the push event, which runs inside a Service Worker:

这是普通JavaScript事件侦听器,用于push事件,该事件在Service Worker内部运行:

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

event.data contains the PushMessageData object which exposes methods to retrieve the push data sent by the server, in the format you want:

event.data包含PushMessageData对象,该对象以所需的格式公开用于检索服务器发送的推送数据的方法:

  • arrayBuffer() : as an ArrayBuffer object

    arrayBuffer() :作为ArrayBuffer对象

  • blob(): as a Blob object

    blob() :作为Blob对象

  • json(): parsed as JSON

    json() :解析为JSON

  • text(): plain text

    text() :纯文本

You’ll normally use event.data.json().

通常,您将使用event.data.json()

显示通知 (Displaying a notification)

Here we intersect a bit with the Notifications API, but for a good reason, as one of the main use cases of the Push API is to display notifications.

在这里,我们与Notifications API进行了一些交叉,但这是有充分的理由的,因为Push API的主要用例之一是显示通知。

Inside our push event listener in the Service Worker, we need to display the notification to the user. We also need to tell the event to wait until the browser has shown it before the function can terminate. We extend the event lifetime until the browser has finished displaying the notification (until the promise has been resolved), otherwise the Service Worker could be stopped in the middle of your processing:

在Service Worker中的push事件侦听器内部,我们需要向用户显示通知。 我们还需要告诉事件,直到浏览器显示该事件,然后函数才能终止。 我们延长事件的生存时间,直到浏览器完成显示通知为止(直到兑现承诺已解决),否则服务工作者可能会在处理过程中停止:

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

Interested in learning JavaScript? Get my ebook at jshandbook.com

有兴趣学习JavaScript吗? 在jshandbook.com上获取我的电子书

翻译自: https://www.freecodecamp.org/news/a-guide-to-getting-the-most-out-of-the-push-api-72a139bfeb44/

push api v3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值