开放 Web 上的推送通知

Matt Gaunt

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

借助推送通知,您的用户可以选择及时接收他们喜爱的网站的最新动态,还可以利用具有吸引力的定制内容有效地重新吸引用户。

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

Chrome 中的 Push API 依赖于一些不同的技术,包括 Web 应用清单Service Worker。在这篇博文中,我们将介绍所有这些技术,但仅为启动并运行推送消息传递的最低要求。如需更好地了解清单的一些其他功能以及 Service Worker 的离线功能,请点击上面的链接。

此外,我们还将介绍未来 Chrome 版本中会添加到该 API 的内容,最后会提供一个常见问题解答。

为 Chrome 实现推送消息

本部分介绍了为了在 Web 应用中支持推送消息传递需要完成的各个步骤。

注册 Service Worker

通过 Service Worker 实现 Web 推送消息存在一个依赖关系。这是因为收到推送消息时,浏览器可以启动一个无需打开页面就在后台运行的 Service Worker,并分派事件,以便您决定如何处理该推送消息。

以下示例展示了如何在 Web 应用中注册 Service Worker。当注册成功完成后,我们会调用 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 文件(具有处理推送消息的逻辑)之前检查 Service Worker 是否受支持。在这里,我们直接告知浏览器此 JavaScript 文件是网站的 Service Worker。

设置初始状态

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

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

用户希望通过一个简单的界面为您的网站启用或停用推送消息,并且希望界面能及时反映任何变化。换句话说,如果它们为您的网站启用了推送消息,并在一周后退出,那么界面应突出显示推送消息已启用。

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

这时,您可能认为只有两种状态需要处理,即启用或停用。不过,您需要考虑通知周围的其他一些状态。

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

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

由于大多数此类检查都会导致界面被停用,因此您应将初始状态设置为“已停用”。如果网页的 JavaScript 存在问题(例如 JS 文件无法下载或用户已停用 JavaScript),这也可以避免混淆。

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

使用此初始状态,我们可以在 initialiseState() 方法中(即在注册 Service Worker 之后)执行上述检查。

// 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。如果没有它,我们将无法在收到推送消息时显示来自 Service Worker 的通知。
  • 我们会检查当前的 Notification.permission,以确保它不是 "denied"。权限遭拒意味着您无法显示通知,除非用户在浏览器中手动更改权限。
  • 如需检查是否支持推送消息传递,我们需要检查窗口对象中是否有 PushManager
  • 最后,我们使用 pushManager.getSubscription() 检查是否已有订阅。如果是,我们会将订阅详情发送到我们的服务器,以确保我们拥有正确的信息,并设置界面以指明推送消息功能已启用或未启用。我们将在本文的后面部分介绍订阅对象中存在哪些详细信息。

我们等到 navigator.serviceWorker.ready 解析完成后再检查订阅并启用推送按钮,因为只有在 Service Worker 处于活动状态后,您才可以实际订阅推送消息。

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

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

Chrome 使用 FCM 处理推送消息的发送和传递;不过,如需使用 FCM API,您需要在 Firebase Developer Console 上设置一个项目。

以下步骤仅适用于使用 FCM 的 Chrome、Android 版 Opera 和三星浏览器。我们将在本文的后面部分讨论该方法在其他浏览器中是如何工作的。

创建新的 Firebase 开发者项目

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

新建 Firebase 项目的屏幕截图

添加项目名称并创建项目,系统会将您转到项目信息中心:

Firebase 项目首页

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

Firebase 项目设置菜单

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

Firebase 项目 Cloud Messaging 菜单

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

添加 Web 应用清单

对于推送,我们需要添加一个带有 gcm_sender_id 字段的清单文件,以确保推送订阅成功。只有 Chrome、适用于 Android 的 Opera 和三星浏览器才需要此参数,以便它们可以使用 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">

如果您不添加包含这些参数的网页清单,则当您尝试订阅用户推送消息时,将会发生异常,并显示错误 "Registration failed - no sender id provided""Registration failed - permission denied"

订阅推送消息

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

若要进行订阅,您必须对 PushManager 对象调用 subscribe() 方法,该对象可通过 ServiceWorkerRegistration 访问。

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

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

您应该将每个用户的端点保存在服务器上,因为稍后您需要他们发送推送消息。

以下代码可为用户订阅推送消息功能:

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 文件之前,什么都不会发生。

Service Worker 推送事件监听器

收到推送消息时(我们将在下一部分中介绍如何实际发送推送消息),您的 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 并延长事件处理程序(也可以视为使 Service Worker 保持活动状态)的生命周期,直到 promise 得到解决;在这种情况下,传递给 event.waitUntil 的 promise 是从 showNotification() 返回的 Promise。

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

如果您想一次显示多个通知,请使用其他标记或根本不使用标记。 我们稍后会在本文中了解显示通知的更完整示例。现在,为简单起见,我们看看发送推送消息是否会显示此通知。

发送推送消息

我们已订阅推送消息,我们的 Service Worker 已准备好显示通知,是时候通过 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 地址已列入该项目的许可名单。
  • application/jsonapplication/x-www-form-urlencoded;charset=UTF-8 的相应 Content-Type 标头,具体取决于您是以 JSON 数据还是表单数据形式发送数据。
  • 一组 registration_ids - 这些是您从用户的端点中提取的注册 ID。

请务必查看相关文档,了解如何从服务器发送推送消息,但如需快速查看 Service Worker,您可以使用 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>\"]}"
来自 Android 版 Chrome 的推送消息示例。

在开发后端逻辑时,请记住 POST 正文的授权标头和格式特定于 FCM 端点,因此请检测端点何时用于 FCM,并有条件地添加标头并设置 POST 正文的格式。对于其他浏览器(以后可能是 Chrome),您需要实现网络推送协议

在 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 就会显示自己的一般通知。

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

当用户点击通知时,系统会在您的 Service Worker 中分派 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 发送 POST 的响应正文,以查找 error:NotRegisteredcanonical_id 结果,如 FCM 文档中所述。

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

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

完成上述所有步骤后,您便可以在 Chrome 46 中全面实现 Web 上的推送消息传递。但仍有一些规范化功能可以简化操作(例如用于触发推送消息的标准 API),但此版本可让您立即开始将推送消息构建到您的 Web 应用中。

如何调试您的 Web 应用

实现推送消息时,错误将出现在以下两个位置之一:页面或 Service Worker。

可使用 DevTools 调试网页中的错误。您可以通过以下两种方式调试 Service Worker 问题:

  1. 依次转到 chrome://inspect > Service Worker。除了当前正在运行的 Service Worker,此视图不提供太多信息。
  2. 转到 chrome://serviceworker-internals,您可以在其中查看 Service Worker 的状态,以及查看错误(如有)。在开发者工具提供类似的功能集之前,此页面只是临时页面。

对于刚接触 Service Worker 的人,我可以给出的一个最佳提示是,利用名为“Open DevTools window and pause JavaScript execution on Service Worker to debugging”(打开开发者工具窗口并暂停在 Service Worker 启动时暂停 JavaScript 执行)的复选框。此复选框将在 Service Worker 的开头添加一个断点并暂停执行,这使您能够恢复或单步调试 Service Worker 脚本,并查看是否遇到任何问题。

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

如果 FCM 和 Service Worker 的推送事件之间似乎有问题,那么您就没法进行太多调试来调试问题,因为您无法看到 Chrome 是否收到了任何内容。需要确保的关键在于,当您的服务器发出 API 调用时,FCM 的响应会成功。结果应如下所示:

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

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

调试 Chrome(Android 版)上的 Service Worker

目前,在 Chrome(Android 版)上调试 Service Worker 还不明显。您需要前往 chrome://inspect,找到您的设备并查找名为“Worker pid:....”的列表项,其中包含您的 Service Worker 的网址。

显示 Service Worker 在 Chrome 检查中的位置的屏幕截图

推送通知的用户体验

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

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

本部分详细介绍了您应了解的一些 Chrome 特定部分,以及该实现与其他浏览器实现的区别。

网络推送协议和端点

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

网络推送协议是一种推送提供程序可以实现的新标准,让开发者不必操心推送提供程序是谁。这样做的目的是,让开发者不必注册 API 密钥和发送特殊格式的数据,就像使用 FCM 时必须执行的操作。

Chrome 是第一个实现 Push API 的浏览器,但 FCM 不支持网络推送协议,这就是 Chrome 要求 gcm_sender_id 的原因,并且您需要为 FCM 使用 RESTful API。

Chrome 的最终目标是朝着将 Web 推送协议与 Chrome 和 FCM 结合使用。

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

如何实现网络推送协议?

Firefox Nightly 目前正在开发推送功能,很可能是第一个实现网络推送协议的浏览器。

常见问题解答

规格在哪里?

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、Opera for Android 和 Samsung 浏览器才能使用 Firebase Cloud Messaging (FCM) API。目标是在标准最终确定且 FCM 可支持后使用网络推送协议。

为什么不使用 Web Sockets 或服务器发送事件 (EventSource)?

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

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

如果您不需要后台传送,Web Sockets 是一个很好的选择。

何时可以在不显示通知的情况下使用推送(即无声背景推送)?

虽然目前还没有推出此功能的时间表,但有一个实现后台同步的意图,虽然未做出决定或规范,但关于如何启用后台同步静默推送,仍存在一些讨论。

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

Service Worker 需要安全源,以确保 Service Worker 脚本来自预期源,并且不会受到中间人攻击。目前,这意味着在实际网站上使用 HTTPS,尽管 localhost 在开发过程中可以正常使用。

浏览器支持情况如何?

Chrome 已支持稳定版,Mozilla 也已在 Firefox Nightly 中推送消息。 如需了解详情,请参阅实现 Push API bug;您可以在此处跟踪其通知实现情况

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

目前还不能,但我们计划添加相关支持,以获取当前显示的通知列表。如果您有一个用例,需要在通知显示后为其设置到期时间,我们很想知道这是什么,因此请添加评论,我们会将其转达给 Chrome 团队。

如果您只需要在一段时间后停止向用户发送推送通知,而不在意通知保持可见的时间,则可以使用 FCM 的存留时间 (ttl) 参数,点击此处了解详情

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

该博文会介绍一些限制:

  • Chrome 将 CCM 用作推送服务会产生许多专有要求。我们正在共同努力,看看未来是否可以提升其中的一些机制。
  • 您必须在收到推送消息时显示通知。
  • Chrome 桌面版会提醒用户,如果 Chrome 未运行,则无法收到推送消息。这与 ChromeOS 和 Android 不同,在 ChromeOS 和 Android 中,系统会始终接收推送消息。

我们不应该使用 Permissions API 吗?

Permission API 是在 Chrome 中实现的,但不一定在所有浏览器中都可用。您可以点击此处了解详情

当我点击通知时,为什么 Chrome 无法打开上一个标签页?

此问题只会影响当前不受 Service Worker 控制的页面。您可以点击此处了解详情

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

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

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

如果我发送了 10 条推送消息,但只希望设备接收一条,会发生什么情况?

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

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