开放 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 后,我们需要设置 UI 的状态。

用户希望有一个简单的界面来为您的网站启用或停用推送消息,并且希望网站能及时掌握发生的任何变化。换句话说,如果他们为网站启用了推送消息,一周后又离开,界面应该会突出显示推送消息已启用。

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

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

一张示意图,突出显示了 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"。权限遭拒表示您无法显示通知,除非用户在浏览器中手动更改该权限。
  • 如需检查是否支持推送消息传递,请检查 window 对象中是否提供 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、Opera for Android 和三星浏览器。我们稍后会在本文中讨论该方法在其他浏览器中的运作方式。

创建新的 Firebase 开发者项目

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

新建 Firebase 项目屏幕截图

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

Firebase 项目主页

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

Firebase 项目设置菜单

在设置页面中,点击“云消息传递”标签页。

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">

如果您不添加包含这些参数的 Web 清单,那么当您尝试为用户订阅推送消息时,会发生异常,并显示错误 "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,以及是否提取注册 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>\"]}"
来自 Chrome(Android 版)的推送消息示例。

在开发后端逻辑时,请注意 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 中完整实现网页版推送消息。有些功能(例如,用于触发推送消息的标准 API)可让您更轻松地完成相关规范化功能,但此版本可让您立即开始将推送消息构建到您的 Web 应用中。

如何调试您的 Web 应用

在实现推送消息时,bug 会存在于以下两个位置之一:您的网页或 Service Worker。

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

  1. 转到 chrome://inspect > Service Worker。除了当前正在运行的 Service Worker,此视图不会提供很多信息。
  2. 转到 chrome://serviceworker-internals,然后您就可以在此处查看 Service Worker 的状态和查看错误(如果有)。在开发者工具具备类似的功能集之前,此页面只是暂时的。

对于刚接触 Service Worker 的人,我给予的最佳提示之一就是利用名为“Open DevTools window and paused JavaScript execution on Service Worker boot for debug”(打开开发者工具窗口并在 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 的部分,以及此实现与其他浏览器实现有何不同。

Web 推送协议和端点

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

网络推送协议是推送提供方可以实现的一种新标准,让开发者无需担心推送提供方是谁。思路在于,这样就无需注册 API 密钥和发送特殊格式的数据,就像使用 FCM 时那样。

Chrome 是首款实现 Push API 的浏览器,而 FCM 不支持 Web 推送协议,因此 Chrome 要求使用 gcm_sender_id,而您需要为 FCM 使用 RESTful API。

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

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

使用推送消息的好处是,即使页面关闭,您的 Service Worker 也会被唤醒并显示通知。当网页或浏览器关闭时,Web Socket 和 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 不同,在这两个版本中,Chrome 一律会收到推送消息。

我们应该使用 Permissions API 吗?

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

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

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

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

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

如需了解详情,请访问此处

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

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

如需了解详情,请访问此处