开放 Web 上的推送通知

Matt Gaunt

如果您询问开发者网络中缺少什么移动设备功能,推送通知始终位居答案前列。

借助推送通知,用户可以选择接收来自他们喜爱的网站的及时更新,您还可以使用自定义的富有吸引力的内容重新吸引用户。

从 Chrome 42 版开始,Push APINotification API 可供开发者使用。

Chrome 中的 Push API 依赖于一些不同的技术,包括网络应用清单服务工作线程。在本博文中,我们将介绍这些技术,但只介绍启用和运行推送消息所需的最低限度内容。如需更好地了解清单的其他一些功能以及服务工件的离线功能,请访问上面的链接。

我们还将介绍未来版本的 Chrome 中将添加到该 API 的功能,最后是常见问题解答。

为 Chrome 实现推送消息传递

本部分介绍了您需要完成的每个步骤,以便在 Web 应用中支持推送消息。

注册 Service Worker

您需要有服务工作线程才能为网站实现推送消息。原因在于,收到推送消息时,浏览器可以启动服务工作线程,该服务工作线程在后台运行,无需打开网页,并分派事件,以便您决定如何处理该推送消息。

下面的示例展示了如何在 Web 应用中注册服务工件。注册成功后,我们会调用 initialiseState(),我们稍后会介绍该方法。

var isPushEnabled = false;



window.addEventListener('load', function() {
    var pushButton = document.querySelector('.js-push-button');
    pushButton.addEventListener('click', function() {
    if (isPushEnabled) {
        unsubscribe();
    } else {
        subscribe();
    }
    });

    // Check that service workers are supported, if so, progressively
    // enhance and add push messaging support, otherwise continue without it.
    if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/service-worker.js')
    .then(initialiseState);
    } else {
    console.warn('Service workers aren\'t supported in this browser.');
    }
});

按钮点击处理脚本会为用户订阅或取消订阅推送消息。isPushEnabled 是一个全局变量,只会跟踪当前是否订阅了推送消息。我们将在整个代码段中引用这些值。

然后,我们会先检查是否支持服务工作线程,然后再注册包含处理推送消息逻辑的 service-worker.js 文件。在这里,我们只是告诉浏览器此 JavaScript 文件是我们的网站的服务工作线程。

设置初始状态

Chrome 中已启用和已停用的推送消息用户体验示例。

注册 service worker 后,我们需要设置界面的状态。

用户希望通过简单的界面为您的网站启用或停用推送消息,并且希望界面能够及时了解发生的任何变化。换句话说,如果用户为您的网站启用推送消息,然后离开并在一周后返回,您的界面应突出显示已启用推送消息。

您可以在此文档中找到一些用户体验准则,本文将重点介绍技术方面。

此时,您可能认为只需处理两种状态:启用或停用。不过,您还需要考虑与通知相关的一些其他状态。

一张图表,突出显示了 Chrome 中的不同注意事项和推送状态

在启用按钮之前,我们需要检查多个 API。如果所有 API 都受支持,我们就可以启用界面并设置初始状态,以指示是否订阅了推送消息。

由于其中大多数检查都会导致我们的界面被停用,因此您应将初始状态设为停用。这还有助于避免在网页的 JavaScript 出现问题(例如无法下载 JS 文件或用户停用了 JavaScript)时造成任何混淆。

<button class="js-push-button" disabled>
    Enable Push Messages
</button>

有了这个初始状态,我们就可以在 initialiseState() 方法中执行上述检查,即在服务工作器注册后。

// Once the service worker is registered set the initial state
function initialiseState() {
    // Are Notifications supported in the service worker?
    if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
    console.warn('Notifications aren\'t supported.');
    return;
    }

    // Check the current Notification permission.
    // If its denied, it's a permanent block until the
    // user changes the permission
    if (Notification.permission === 'denied') {
    console.warn('The user has blocked notifications.');
    return;
    }

    // Check if push messaging is supported
    if (!('PushManager' in window)) {
    console.warn('Push messaging isn\'t supported.');
    return;
    }

    // We need the service worker registration to check for a subscription
    navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
    // Do we already have a push message subscription?
    serviceWorkerRegistration.pushManager.getSubscription()
        .then(function(subscription) {
        // Enable any UI which subscribes / unsubscribes from
        // push messages.
        var pushButton = document.querySelector('.js-push-button');
        pushButton.disabled = false;

        if (!subscription) {
            // We aren't subscribed to push, so set UI
            // to allow the user to enable push
            return;
        }

        // Keep your server in sync with the latest subscriptionId
        sendSubscriptionToServer(subscription);

        // Set your UI to show they have subscribed for
        // push messages
        pushButton.textContent = 'Disable Push Messages';
        isPushEnabled = true;
        })
        .catch(function(err) {
        console.warn('Error during getSubscription()', err);
        });
    });
}

以下是这些步骤的简要概述:

  • 我们检查 ServiceWorkerRegistration 原型中是否有 showNotification。如果没有它,我们将无法在收到推送消息时显示来自服务工件的通知。
  • 我们会检查当前的 Notification.permission,以确保它不是 "denied"。如果权限被拒绝,则表示在用户在浏览器中手动更改权限之前,您无法显示通知。
  • 如需检查是否支持推送消息,我们会检查窗口对象中是否有 PushManager
  • 最后,我们使用 pushManager.getSubscription() 检查是否已订阅。如果是,我们会将订阅详情发送到我们的服务器,以确保我们拥有正确的信息,并将界面设置为指示是否已启用推送消息功能。我们将在本文的后面部分介绍订阅对象中存在的详细信息。

我们会等到 navigator.serviceWorker.ready 解析完毕后再检查订阅并启用推送按钮,因为只有在服务 worker 处于活动状态后,您才能实际订阅推送消息。

下一步是处理用户想要启用推送消息的情况,但在此之前,我们需要设置一个 Google Developer Console 项目,并向清单中添加一些参数,以便使用 Firebase Cloud Messaging (FCM)(以前称为 Google Cloud Messaging [GCM])。

在 Firebase 开发者控制台中创建项目

Chrome 使用 FCM 来处理推送消息的发送和传送;不过,如需使用 FCM API,您需要在 Firebase 开发者控制台中设置项目。

以下步骤仅适用于使用 FCM 的 Chrome、Opera for Android 和三星浏览器。我们将在本文后面部分讨论此功能在其他浏览器中的运作方式。

创建新的 Firebase 开发者项目

首先,您需要在 https://console.firebase.google.com/ 上点击“创建新项目”,创建一个新项目。

新 Firebase 项目的屏幕截图

添加项目名称并创建项目,然后您将进入项目信息中心:

Firebase 项目首页

在此信息中心内,点击左上角项目名称旁边的齿轮图标,然后点击“项目设置”。

Firebase 项目设置菜单

在“设置”页面中,点击“Cloud Messaging”标签页。

Firebase 项目 Cloud Messaging 菜单

此页面包含用于推送消息的 API 密钥(我们稍后会用到),以及我们需要在下一部分中添加到 Web 应用清单中的发件人 ID。

添加网络应用清单

对于推送,我们需要添加包含 gcm_sender_id 字段的清单文件,以便成功进行推送订阅。只有 Chrome、Opera for Android 和三星浏览器需要此参数,以便它们可以使用 FCM / GCM。

当这些浏览器使用 FCM 为用户设备订阅时,会使用 gcm_sender_id。这意味着,FCM 可以识别用户的设备,并确保您的发送者 ID 与相应的 API 密钥相匹配,并且用户已允许您的服务器向其发送推送通知。

以下是一个超级简单的清单文件:

{
    "name": "Push Demo",
    "short_name": "Push Demo",
    "icons": [{
        "src": "images/icon-192x192.png",
        "sizes": "192x192",
        "type": "image/png"
        }],
    "start_url": "/index.html?homescreen=1",
    "display": "standalone",
    "gcm_sender_id": "<Your Sender ID Here>"
}

您需要将 gcm_sender_id 值设置为 Firebase 项目中的发件人 ID。

将清单文件保存到项目中(manifest.json 是一个不错的名称)后,请在网页的标头中使用以下标记从 HTML 中引用该文件。

<link rel="manifest" href="/manifest.json">

如果您未添加包含这些参数的 Web 清单,则在尝试让用户订阅推送消息时,会收到包含错误 "Registration failed - no sender id provided""Registration failed - permission denied" 的异常。

订阅推送消息

现在,您已设置好清单,可以返回网站 JavaScript 了。

如需订阅,您必须对 PushManager 对象调用 subscribe() 方法,您可以通过 ServiceWorkerRegistration 访问该对象。

系统会要求用户授权您的来源发送推送通知。如果没有此权限,您将无法成功订阅。

如果 subscribe() 方法返回的 promise 解析,您将获得一个 PushSubscription 对象,其中包含一个 endpoint

您应为每位用户在服务器上保存端点,因为您日后需要使用这些端点发送推送通知。

以下代码会为用户订阅推送消息:

function subscribe() {
    // Disable the button so it can't be changed while
    // we process the permission request
    var pushButton = document.querySelector('.js-push-button');
    pushButton.disabled = true;

    navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
    serviceWorkerRegistration.pushManager.subscribe()
        .then(function(subscription) {
        // The subscription was successful
        isPushEnabled = true;
        pushButton.textContent = 'Disable Push Messages';
        pushButton.disabled = false;

        // TODO: Send the subscription.endpoint to your server
        // and save it to send a push message at a later date
        return sendSubscriptionToServer(subscription);
        })
        .catch(function(e) {
        if (Notification.permission === 'denied') {
            // The user denied the notification permission which
            // means we failed to subscribe and the user will need
            // to manually change the notification permission to
            // subscribe to push messages
            console.warn('Permission for Notifications was denied');
            pushButton.disabled = true;
        } else {
            // A problem occurred with the subscription; common reasons
            // include network errors, and lacking gcm_sender_id and/or
            // gcm_user_visible_only in the manifest.
            console.error('Unable to subscribe to push.', e);
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
        }
        });
    });
}

此时,您的 Web 应用已准备好接收推送消息,但在我们向服务工作器文件添加推送事件监听器之前,不会发生任何事情。

Service Worker 推送事件监听器

收到推送消息后(我们将在下一部分介绍如何实际发送推送消息),服务工作器中会分派一个推送事件,届时您需要显示通知

self.addEventListener('push', function(event) {
    console.log('Received a push message', event);

    var title = 'Yay a message.';
    var body = 'We have received a push message.';
    var icon = '/images/icon-192x192.png';
    var tag = 'simple-push-demo-notification-tag';

    event.waitUntil(
    self.registration.showNotification(title, {
        body: body,
        icon: icon,
        tag: tag
    })
    );
});

此代码会注册一个推送事件监听器,并显示包含预定义标题、正文文本、图标和通知标记的通知。此示例中要重点介绍的一个细节是 event.waitUntil() 方法。此方法接受一个 promise,并延长事件处理程序的生命周期(也可以认为是保持服务工作线程处于活动状态),直到 promise settled;在本例中,传递给 event.waitUntil 的 promise 是从 showNotification() 返回的 promise。

通知标记可用作唯一通知的标识符。如果我们向同一端点发送了两条推送消息,且两者之间存在短暂的延迟,并显示带有相同标签的通知,则浏览器会显示第一条通知,并在收到推送消息时将其替换为第二条通知。

如果您想同时显示多个通知,请使用不同的标签,或不使用任何标签。我们将在本文的后面部分介绍一个更完整的显示通知示例。现在,我们先从简单入手,看看发送推送消息时是否会显示此通知。

发送推送消息

我们已订阅推送消息,我们的服务工作器已准备好显示通知,接下来就可以通过 FCM 发送推送消息了。

这仅适用于使用 FCM 的浏览器。

当您将 PushSubscription.endpoint 变量发送到服务器时,FCM 的端点是特殊的。网址末尾有一个参数,即 registration_id

端点示例如下:

https://fcm.googleapis.com/fcm/send/APA91bHPffi8zclbIBDcToXN_LEpT6iA87pgR-J-MuuVVycM0SmptG-rXdCPKTM5pvKiHk2Ts-ukL1KV8exGOnurOAKdbvH9jcvg8h2gSi-zZJyToiiydjAJW6Fa9mE3_7vsNIgzF28KGspVmLUpMgYLBd1rxaVh-L4NDzD7HyTkhFOfwWiyVdKh__rEt15W9n2o6cZ8nxrP

FCM 网址为:

https://fcm.googleapis.com/fcm/send

registration_id 将为:

APA91bHPffi8zclbIBDcToXN_LEpT6iA87pgR-J-MuuVVycM0SmptG-rXdCPKTM5pvKiHk2Ts-ukL1KV8exGOnurOAKdbvH9jcvg8h2gSi-zZJyToiiydjAJW6Fa9mE3_7vsNIgzF28KGspVmLUpMgYLBd1rxaVh-L4NDzD7HyTkhFOfwWiyVdKh__rEt15W9n2o6cZ8nxrP

这仅适用于使用 FCM 的浏览器。在普通浏览器中,您只需获取一个端点,然后以标准方式调用该端点,无论网址如何,它都会正常运行。

这意味着,您需要在服务器上检查端点是否适用于 FCM,如果适用,则提取 registration_id。如需在 Python 中执行此操作,您可以执行以下操作:

if endpoint.startswith('https://fcm.googleapis.com/fcm/send'):
    endpointParts = endpoint.split('/')
    registrationId = endpointParts[len(endpointParts) - 1]

    endpoint = 'https://fcm.googleapis.com/fcm/send'

获得注册 ID 后,您就可以调用 FCM API。您可以点击此处查看 FCM API 参考文档

调用 FCM 时需要注意以下关键方面:

  • 调用 API 时,必须设置值为 key=&lt;YOUR_API_KEY&gt;Authorization 标头,其中 &lt;YOUR_API_KEY&gt; 是 Firebase 项目中的 API 密钥。
    • FCM 使用 API 密钥来查找适当的发件人 ID、确保用户已为您的项目授予权限,并最终确保服务器的 IP 地址已列入该项目的许可名单。
  • 适当的 Content-Type 标头(application/jsonapplication/x-www-form-urlencoded;charset=UTF-8),具体取决于您是将数据作为 JSON 还是表单数据发送。
  • 一个 registration_ids 数组 - 这些是您从用户的端点中提取的注册 ID。

参阅文档,了解如何从服务器发送推送消息。不过,如需快速检查服务工作器,您可以使用 cURL 向浏览器发送推送消息。

将此 c网址 命令中的 &lt;YOUR_API_KEY&gt;&lt;YOUR_REGISTRATION_ID&gt; 替换为您自己的,然后从终端运行该命令。

您应该会看到以下通知:

    curl --header "Authorization: key=<YOUR_API_KEY>" --header
    "Content-Type: application/json" https://fcm.googleapis.com/fcm/send -d
    "{\"registration_ids\":[\"<YOUR_REGISTRATION_ID>\"]}"
来自 Chrome for Android 的推送消息示例。

在开发后端逻辑时,请注意,Authorization 标头和 POST 正文的格式因 FCM 端点而异,因此请检测端点何时为 FCM 端点,并有条件地添加标头并设置 POST 正文格式。对于其他浏览器(希望未来 Chrome 也能支持),您需要实现 Web Push 协议

Chrome 中当前实现的 Push API 的一个缺点是,您无法通过推送消息发送任何数据。没错,什么都没搜到。原因在于,在未来的实现中,载荷数据必须先在服务器上加密,然后才能发送到推送消息端点。这样一来,无论是哪种推送服务提供商,端点都无法轻松查看推送消息的内容。这还可防范其他漏洞,例如 HTTPS 证书验证不当以及服务器和推送服务提供商之间发生的中间人攻击。不过,此加密方式尚不受支持,因此在此期间,您需要执行提取操作,以获取填充通知所需的信息。

更完整的推送事件示例

我们目前看到的通知非常基本,就示例而言,它在涵盖实际应用场景方面做得非常不充分。

实际上,大多数用户都希望在显示通知之前从服务器获取一些信息。这些数据可以用来在通知标题和消息中填充特定内容,也可以进一步缓存一些网页或数据,以便在用户点击通知时,在打开浏览器时立即显示所有内容,即使网络在当时不可用也是如此。

在以下代码中,我们从 API 提取一些数据,将响应转换为对象,并使用该对象填充通知。

self.addEventListener('push', function(event) {
    // Since there is no payload data with the first version
    // of push messages, we'll grab some data from
    // an API and use it to populate a notification
    event.waitUntil(
    fetch(SOME_API_ENDPOINT).then(function(response) {
        if (response.status !== 200) {
        // Either show a message to the user explaining the error
        // or enter a generic message and handle the
        // onnotificationclick event to direct the user to a web page
        console.log('Looks like there was a problem. Status Code: ' + response.status);
        throw new Error();
        }

        // Examine the text in the response
        return response.json().then(function(data) {
        if (data.error || !data.notification) {
            console.error('The API returned an error.', data.error);
            throw new Error();
        }

        var title = data.notification.title;
        var message = data.notification.message;
        var icon = data.notification.icon;
        var notificationTag = data.notification.tag;

        return self.registration.showNotification(title, {
            body: message,
            icon: icon,
            tag: notificationTag
        });
        });
    }).catch(function(err) {
        console.error('Unable to retrieve data', err);

        var title = 'An error occurred';
        var message = 'We were unable to get the information for this push message';
        var icon = URL_TO_DEFAULT_ICON;
        var notificationTag = 'notification-error';
        return self.registration.showNotification(title, {
            body: message,
            icon: icon,
            tag: notificationTag
        });
    })
    );
});

值得再次强调的是,event.waitUntil() 会接受一个 promise,从而导致 showNotification() 返回 promise,这意味着在异步 fetch() 调用完成并显示通知之前,我们的事件监听器不会退出。

您会发现,即使出现错误,我们也会显示通知。这是因为,如果我们不这样做,Chrome 将显示自己的通用通知。

在用户点击通知时打开网址

当用户点击通知时,您的服务工作器中会分派 notificationclick 事件。在处理程序中,您可以执行适当的操作,例如将焦点移至标签页或打开包含特定网址的窗口:

self.addEventListener('notificationclick', function(event) {
    console.log('On notification click: ', event.notification.tag);
    // Android doesn't close the notification when you click on it
    // See: http://crbug.com/463146
    event.notification.close();

    // This looks to see if the current is already open and
    // focuses if it is
    event.waitUntil(
    clients.matchAll({
        type: "window"
    })
    .then(function(clientList) {
        for (var i = 0; i < clientList.length; i++) {
        var client = clientList[i];
        if (client.url == '/' && 'focus' in client)
            return client.focus();
        }
        if (clients.openWindow) {
        return clients.openWindow('/');
        }
    })
    );
});

此示例会将浏览器打开到网站源的根目录,具体方法是聚焦于现有的同源标签页(如果有),否则打开一个新标签页。

您可以点击此处,查看一篇介绍 Notification API 的一些用途的文章。

取消订阅用户的设备

您已为用户的设备订阅推送消息,并且用户正在接收推送消息,但如何取消订阅?

如需取消订阅用户设备,您需要执行的主要操作是:对 PushSubscription 对象调用 unsubscribe() 方法,并从服务器中移除端点(以免发送您知道不会收到的推送消息)。以下代码正是执行此操作:

function unsubscribe() {
    var pushButton = document.querySelector('.js-push-button');
    pushButton.disabled = true;

    navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
    // To unsubscribe from push messaging, you need get the
    // subscription object, which you can call unsubscribe() on.
    serviceWorkerRegistration.pushManager.getSubscription().then(
        function(pushSubscription) {
        // Check we have a subscription to unsubscribe
        if (!pushSubscription) {
            // No subscription object, so set the state
            // to allow the user to subscribe to push
            isPushEnabled = false;
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
            return;
        }

        var subscriptionId = pushSubscription.subscriptionId;
        // TODO: Make a request to your server to remove
        // the subscriptionId from your data store so you
        // don't attempt to send them push messages anymore

        // We have a subscription, so call unsubscribe on it
        pushSubscription.unsubscribe().then(function(successful) {
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
            isPushEnabled = false;
        }).catch(function(e) {
            // We failed to unsubscribe, this can lead to
            // an unusual state, so may be best to remove
            // the users data from your data store and
            // inform the user that you have done so

            console.log('Unsubscription error: ', e);
            pushButton.disabled = false;
            pushButton.textContent = 'Enable Push Messages';
        });
        }).catch(function(e) {
        console.error('Error thrown while unsubscribing from push messaging.', e);
        });
    });
}

及时更新订阅

FCM 和您的服务器之间的订阅可能会不同步。请确保您的服务器解析 FCM API 的 send POST 的响应正文,查找 error:NotRegisteredcanonical_id 结果,如 FCM 文档中所述。

Service Worker 和服务器之间的订阅也可能会不同步。例如,成功订阅/取消订阅后,不稳定的网络连接可能会阻止您更新服务器;或者用户可能会撤消通知权限,从而触发自动取消订阅。通过定期(例如在网页加载时)检查 serviceWorkerRegistration.pushManager.getSubscription() 的结果并将其与服务器同步,处理此类情况。如果您已不再订阅,并且 Notification.permission == 'granted',您可能还希望自动重新订阅。

sendSubscriptionToServer() 中,您需要考虑如何在更新 endpoint 时处理失败的网络请求。一种解决方案是在 Cookie 中跟踪 endpoint 的状态,以确定服务器是否需要最新详细信息。

上述所有步骤都会在 Chrome 46 中完全实现 Web 上的推送消息功能。我们仍在开发一些有助于简化操作的规范化功能(例如用于触发推送消息的标准 API),但借助此版本,您现在就可以开始在 Web 应用中构建推送消息功能了。

如何调试 Web 应用

在实现推送消息时,错误会出现在以下两个位置之一:您的网页或您的服务工作器。

您可以使用 DevTools 调试网页中的 bug。如需调试 Service Worker 问题,您有以下两种选择:

  1. 依次前往 chrome://inspect > Service workers。除了当前正在运行的服务工作器之外,此视图不会提供太多信息。
  2. 前往 chrome://serviceworker-internals,您可以在此处查看 Service Worker 的状态,以及查看是否存在错误。在 DevTools 提供类似的功能集之前,此页面是临时的。

对于刚开始接触服务工作线程的用户,我能提供的最佳提示之一就是使用名为“Open DevTools window and pause JavaScript execution on service worker startup for debugging”(在服务工作线程启动时打开 DevTools 窗口并暂停 JavaScript 执行以进行调试)的复选框。此复选框会在服务工作线程开头添加一个断点并暂停执行,以便您恢复或逐步执行服务工作线程脚本,并查看是否遇到任何问题。

显示 serviceworker-internals 中暂停执行复选框位置的屏幕截图。

如果 FCM 和您的服务工件的推送事件之间似乎存在问题,那么您无法排查问题,因为您无法查看 Chrome 是否收到了任何内容。关键要确保的是,当服务器进行 API 调用时,FCM 的响应是否成功。该代码将如下所示:

{"multicast_id":1234567890,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1234567890"}]}

请注意 "success": 1 响应。如果您看到失败,则表示 FCM 注册 ID 有问题,并且系统未将推送消息发送到 Chrome。

在 Android 版 Chrome 上调试 Service Worker

目前,在 Android 版 Chrome 上调试服务工件并不容易。您需要前往 chrome://inspect,找到您的设备,然后查找名称为“Worker pid:....”(包含服务工作器的网址)的列表项。

屏幕截图:显示 Chrome Inspect 中服务工件的所在位置

推送通知的用户体验

Chrome 团队一直在整理一份有关推送通知用户体验最佳实践的文档,以及一份介绍使用推送通知时的一些极端情况的文档。

Chrome 和开放网络上的推送消息的未来

本部分将详细介绍此实现的一些 Chrome 专用部分(您应予以留意),以及与其他浏览器实现的不同之处。

Web 推送协议和端点

Push API 标准的优点在于,您应该能够获取端点,将其传递给服务器,并通过实现 Web Push 协议来发送推送消息。

Web Push 协议是一种推送服务提供商可以实现的新标准,让开发者不必担心推送服务提供商是谁。其基本思路是,避免开发者需要注册 API 密钥并发送格式特殊的数据(如使用 FCM 时所需)。

Chrome 是第一个实现 Push API 的浏览器,而 FCM 不支持 Web Push 协议,因此 Chrome 需要 gcm_sender_id,并且您需要为 FCM 使用 RESTful API。

Chrome 的最终目标是逐步将 Web 推送协议与 Chrome 和 FCM 搭配使用。

在此之前,您需要检测端点“https://fcm.googleapis.com/fcm/send”,并将其与其他端点分开处理,即以特定方式设置载荷数据格式并添加授权密钥。

如何实现 Web Push 协议?

Firefox Nightly 目前正在开发推送功能,很可能是首个实现 Web Push 协议的浏览器。

常见问题解答

规格在哪里?

https://slightlyoff.github.io/ServiceWorker/spec/service_worker/ https://w3c.github.io/push-api/ https://notifications.spec.whatwg.org/

如果我的网站存在多个来源,或者我同时拥有网站版和原生版,我可以避免收到重复通知吗?

目前还没有解决此问题的方法,但您可以在 Chromium 上跟踪进度。

理想情况下,您应为用户设备提供某种 ID,然后在服务器端匹配原生应用和 Web 应用订阅 ID,并决定向哪个 ID 发送推送消息。您可以通过屏幕尺寸、设备型号,在 Web 应用和原生应用之间共享生成的密钥来实现此目的,但每种方法都有优缺点。

为什么需要 gcm_sender_id?

必须执行此操作,Chrome、Android 版 Opera 和三星浏览器才能使用 Firebase Cloud Messaging (FCM) API。目标是在标准最终确定且 FCM 可以支持 Web 推送协议时使用该协议。

为什么不使用 Web 套接字或服务器发送的事件 (EventSource)?

使用推送消息的好处在于,即使您的网页已关闭,您的 Service Worker 也会被唤醒,并能够显示通知。关闭网页或浏览器时,Web Sockets 和 EventSource 的连接会关闭。

如果我不需要后台事件传送,该怎么办?

如果您不需要后台传送,Web 套接字是一个不错的选择。

在什么情况下可以使用不显示通知的推送(即静默后台推送)?

我们尚未确定此功能的发布时间,但有意实现后台同步,虽然尚未确定或规范,但我们正在讨论如何通过后台同步启用静默推送。

为什么需要使用 HTTPS?如何在开发期间解决此问题?

Service Worker 需要安全的来源,以确保 Service Worker 脚本来自预期的来源,而不是来自中间人攻击。目前,这意味着在正式版网站上使用 HTTPS,但在开发期间可以使用 localhost。

浏览器支持情况如何?

Chrome 在其稳定版中支持此功能,Mozilla 正在 Firefox Nightly 中开发推送功能。 如需了解详情,请参阅实现 Push API 错误,您也可以点击此处跟踪其通知实现

我可以在一段时间后移除通知吗?

目前无法执行此操作,但我们计划添加支持,以便获取当前可见通知的列表。如果您有在通知显示创建后为其设置到期时间的用例,我们非常乐意了解,因此请添加评论,我们会将其转交给 Chrome 团队。

如果您只需要在特定时间段后停止向用户发送推送通知,并且不关心通知保持可见的时间长短,则可以使用 FCM 的存活时间 (ttl) 参数,点击此处了解详情

Chrome 中的推送消息有哪些限制?

本文中列出了一些限制:

  • Chrome 将 CCM 用作推送服务,这会产生一些专有要求。我们正在共同努力,希望日后能解除其中一些限制。
  • 您必须在收到推送消息时显示通知。
  • 桌面版 Chrome 有一个限制,即如果 Chrome 未运行,则不会收到推送消息。这与 ChromeOS 和 Android 不同,后者始终会收到推送消息。

我们不应该使用 Permissions API 吗?

Permission API 已在 Chrome 中实现,但不一定会在所有浏览器中提供。您可以点击此处了解详情

为什么当我点击通知时,Chrome 不会打开之前的标签页?

此问题仅会影响目前未由 Service Worker 控制的网页。您可以点击此处了解详情

如果通知在用户设备收到推送时已过期,该怎么办?

您必须在收到推送消息时始终显示通知。如果您想发送通知,但该通知只在特定时间段内有效,则可以在 CCM 上使用“time_to_live”参数,以便 FCM 在通知过期后不会发送推送消息。

如需了解详情,请点击此处

如果我发送 10 条推送消息,但只希望设备收到一条,会怎么样?

FCM 有一个“collapse_key”参数,您可以使用该参数告知 FCM 将具有相同“collapse_key”的所有待处理消息替换为新消息。

如需了解详情,请点击此处