Push API 让网络应用从用户代理接收来自服务器发送的消息,无论网络应用是否运行或者在线
网页或者浏览器不在线的时候,推送消息无法被推送到客户端浏览器,此时推送消息就会被 FCM 服务器保存起来,等到网页或者浏览器上线的时候,FCM 服务器才会推送消息到网页或者浏览器
生成服务器公秘钥对
可以使用 web-push
库来生成服务器公秘钥对。
1 2 3 4 5
| const webpush = require('web-push');
const vapidKeys = webpush.generateVAPIDKeys();
const { publicKey, privateKey } = vapidKeys
|
其中私钥放在服务器保存,公钥用于注册推送订阅。
创建消息推送服务
可以利用 ServiceWorkerRegistration 接口的 pushManager 属性获取到当前 ServiceWorker 对应的 PushManager 接口实例。
PushManager 接口的 subscribe()
方法用于订阅一个消息推送服务。
其接收一组可选的配置项,userVisibleOnly
可选参数指定返回的推送是否只用于创建对用户可见的通知(不指定 true 会在一些浏览器中报错),applicationServerKey
可选参数指定服务的公钥(某些浏览器中是必须的参数)。
返回一个 Promise 的 PushSubscription 接口实例,代表当前推送消息。
在当前 ServiceWorker 没有创建消息推送服务时,新的消息推送会被创建。
1 2 3 4 5 6 7 8 9
| window.navigator.serviceWorker.ready.then((registration) => { registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: '', }).then((subscription) => { console.log(subscription) }) })
|
1 2 3 4 5 6
| self.registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: '', }).then((subscription) => { console.log(subscription) })
|
获取推送消息服务
PushManager 接口的 getSubscription()
方法用于获取已经订阅的消息推送服务。
返回一个 Promise 的 PushSubscription 接口实例,代表当前已订阅的消息推送服务,否则返回一个 Promise 的 null。
1 2 3 4 5 6 7 8
| window.navigator.serviceWorker.ready.then((registration) => { registration.pushManager.getSubscription().then((subscription) => { if (subscription != null) { console.log(subscription) } }) })
|
1 2 3 4 5
| self.registration.pushManager.getSubscription().then((subscription) => { if (subscription != null) { console.log(subscription) } })
|
PushSubscription 接口代表了订阅的消息推送。
endpoint
属性代表与消息推送联系的 endpoint。
expirationTime
属性代表订阅的消息推送的到期时间。
options
属性代表创建消息推送时的选项。
getKey()
方法返回一个 ArrayBuffer,代表客户端公钥的密钥,可以将其发送到服务器用于加密推送消息数据。支持传入一个代表用于生成客户端密钥的加密方法的参数,可以是 p256dh 或 auth。
toJSON()
方法标准化地转换消息推送的信息,目前仅包含 endpoint 参数。
unsubscribe()
方法取消消息推送的订阅,返回一个 Promise 的布尔值,代表是否成功取消了消息订阅。
发送推送消息
可以使用 web-push 在服务端来发送推送消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import express from 'express' import webpush from 'web-push'
const vapidKeys = { "publicKey": "BHXrxJPYpQSwGMwcN-HprCaU_Po9POIUvqWFLFq9UUNHP5SNJKxk_Io59y8_twMTOuB5SbpbcPBwHFo2kBUj7vQ", "privateKey": "Yhd4XF08Efh8HNF_8RDJ9VL6pF-Gos-3KOmgyMEUSf8" }
webpush.setGCMAPIKey('<Your GCM API Key Here>')
webpush.setVapidDetails( 'mailto:example@yourdomain.org', vapidKeys.publicKey, vapidKeys.privateKey )
const app = express()
app.get('/push', (req, res) => { const pushSubscription = { "endpoint": "https://fcm.googleapis.com/fcm/send/cSAH1Q7Fa6s:APA91bEgYeKNXMSO1rcGAOPzt3L9fMhyjL-zSPV5JfiKwgqtbx_Q4de_8plEY_QViLnhfe6-0fUgdo7Z3Gqpml3zIBSfO6IISDYdF9kzL2h_dbZ_FE_YKbKOG70gMG_A74xwK1vsocCv", "keys": { "p256dh": "BAqZaMLZn_rtYeR7WsBLqBWG7uMiOGRyCx2uhOqm0ZaJwDdQac-ubAyRRdLXJVZDOrNe-B3mCTy3g0vHCkeyYyo", "auth": "fxDt8RtB92KHpQM7HetBUw" } }
webpush.sendNotification(pushSubscription, 'Hello world') .then(result => { res.send(result); }) })
app.listen(1701, () => { console.log('Server start success at http://localhost:1701'); })
|
若使用 firebase 架构可使用 firebase 来实现消息推送。
监听消息推送
ServiceWorkerGlobalScope 接口的 push
事件在每次收到一条推送消息时触发。
返回一个 PushEvent 事件。
1 2 3 4 5
| self.addEventListener('push', (e) => { console.log(e)
self.registration.showNotification(e.data?.json().title ?? 'New Notification') })
|
PushEvent 接口继承自 ExtendableEvent 接口。
其 data 属性代表该推送消息的内容,是一个 PushMessageData 实例。
PushMessageData 接口包括多种处理推送的消息的方法,类似于 fetch API 中的方法,但允许被调用多次。
其 arrayBuffer()
方法、blob()
方法、json()
方法、text()
方法分别将结果转换成 ArrayBuffer、Blob、JSON 解析结果、字符串。
推送的消息能够自动被加解密,无需做额外的处理。
消息推送权限
PushManager 接口的 permissionState()
方法用于获取当前的请求消息推送权限。
参数同 subscribe 方法的参数。
返回一个 Promise 的 'prompt'
、'denied'
、'granted'
的字符串枚举。
1 2 3 4 5 6 7 8 9 10 11 12 13
| self.registration.pushManager.permissionState({ userVisibleOnly: true, applicationServerKey: '', }).then((state) => { switch(state) { case "denied": break case "granted": break case "prompt": break } })
|
其他
PushManager 接口的 supportedContentEncodings
静态属性返回一组消息推送支持的加密方式。
ServiceWorkerGlobalScope 接口的 pushsubscriptionchange
事件在更新订阅的消息推送时触发(可能原因包括消息推送服务刷新、消息推送服务失效等)。
权限 API
该 API 调用需要用户授予 push
权限,可以调用 Permission.query()
方法或 PushManager.permissionState()
检查用户是否已授予了该权限
示例
类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| type PushEncryptionKeyName = "auth" | "p256dh";
interface PushSubscriptionJSON { endpoint?: string; expirationTime?: EpochTimeStamp | null; keys?: Record<string, string>; }
interface PushSubscriptionOptionsInit { applicationServerKey?: BufferSource | string | null; userVisibleOnly?: boolean; }
interface PushEvent extends ExtendableEvent { readonly data: PushMessageData | null; }
interface PushManager { getSubscription(): Promise<PushSubscription | null>; permissionState(options?: PushSubscriptionOptionsInit): Promise<PermissionState>; subscribe(options?: PushSubscriptionOptionsInit): Promise<PushSubscription>; }
interface PushMessageData { arrayBuffer(): ArrayBuffer; blob(): Blob; json(): any; text(): string; }
interface PushSubscription { readonly endpoint: string; readonly expirationTime: EpochTimeStamp | null; readonly options: PushSubscriptionOptions; getKey(name: PushEncryptionKeyName): ArrayBuffer | null; toJSON(): PushSubscriptionJSON; unsubscribe(): Promise<boolean>; }
interface PushSubscriptionOptions { readonly applicationServerKey: ArrayBuffer | null; readonly userVisibleOnly: boolean; }
interface ServiceWorkerRegistration extends EventTarget { readonly pushManager: PushManager; }
interface ServiceWorkerGlobalScope extends WorkerGlobalScope { onpush: ((this: ServiceWorkerGlobalScope, ev: PushEvent) => any) | null; onpushsubscriptionchange: ((this: ServiceWorkerGlobalScope, ev: Event) => any) | null; }
|
链接