開放網路上的推播通知

Matt Gaunt

如果您詢問一群開發人員,行動裝置缺少哪些行動裝置功能,推播通知都會列在清單中比較高。

推播通知可讓使用者選擇訂閱喜愛的網站最新消息,您也能透過自訂且吸引人的內容,有效地與使用者互動。

自 Chrome 42 版起,開發人員可以使用 Push APINotification API

Chrome 中的 Push API 需要使用幾項不同的技術,包括網頁應用程式資訊清單Service Worker。在本文中,我們將探討這些技術,但只會說明啟用推播訊息所需的最低要求。如要進一步瞭解資訊清單的其他部分功能和服務工作處理程序的離線功能,請點選上方連結。

我們也會看看日後的 Chrome 版本中,有哪些新的 API 會加入 API,最後我們來看看常見問題。

為 Chrome 實作推送訊息

本節將說明您必須完成的每個步驟,以便在網路應用程式中支援推播訊息。

註冊 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 檔案。這裡我們只是告訴瀏覽器,這個 JavaScript 檔案是網站的服務工作者。

設定初始狀態

範例:Chrome 中啟用和停用的推播訊息使用者體驗。

註冊服務工作者後,我們需要設定 UI 狀態。

使用者會期望簡單的使用者介面啟用或停用網站的推送訊息,一旦發生任何變更,也都能即時反映最新資訊。換句話說,如果使用者為您的網站啟用推播訊息,離開網站後一週後再回來,使用者介面應會醒目顯示已啟用推播訊息。

您可以參閱這份文件中的使用者體驗指南,本文則會著重於技術層面。

您可能會認為此時只需要處理兩種狀態:啟用或停用。不過,關於通知的其他狀態,您必須納入考量。

醒目顯示在 Chrome 中推送的各種注意事項和狀態的圖表

啟用按鈕前,我們需要檢查多個 API,如果所有 API 都支援,我們就可以啟用 UI,並設定初始狀態,指出是否已訂閱推播訊息。

由於大多數的檢查會導致 UI 停用,因此您應將初始狀態設為停用。這樣一來,如果網頁的 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);
        });
    });
}

以下簡要說明這些步驟:

  • 我們檢查 showNotification 是否在 ServiceWorkerRegistration 原型中推出。否則,我們就無法在收到推播訊息時顯示服務工作者的通知。
  • 我們會檢查目前的 Notification.permission,確認其並非 "denied"。如果權限遭到拒絕,您就無法顯示通知,除非使用者在瀏覽器中手動變更權限。
  • 如要確認是否支援推播訊息,我們會檢查 PushManager 是否可在視窗物件中使用。
  • 最後,我們使用 pushManager.getSubscription() 檢查是否已訂閱過。如果是,我們會將訂閱詳細資料傳送至伺服器,確保我們擁有正確的資訊,並設定 UI 指出是否已啟用推播訊息。本文稍後會介紹訂閱物件中有哪些詳細資料。

我們會等到 navigator.serviceWorker.ready 解析完成後,再檢查訂閱並啟用推送按鈕,因為這個按鈕只有在服務工作站已經啟用時,您才能實際訂閱推送訊息。

接下來要處理使用者想要啟用推播訊息的情況,但在進行這項操作之前,我們需要設定 Google 開發人員控制台專案,並在資訊清單中新增一些參數,以便使用 Firebase 雲端通訊 (FCM) (舊稱 Google 雲端通訊 (GCM))。

在 Firebase 開發人員控制台建立專案

Chrome 會使用 FCM 處理推送和推送訊息的傳送和傳送作業;但如要使用 FCM API,您必須在 Firebase Developer Console 設定專案。

下列步驟僅適用於使用 FCM 的 Chrome、Opera for Android 和 Samsung 瀏覽器。我們會在文章稍後的部分討論這項功能在其他瀏覽器中的運作方式。

建立新的 Firebase 開發人員專案

首先,請在 https://console.firebase.google.com/ 中按一下「Create New Project」(建立新專案),建立新專案。

新 Firebase 專案螢幕截圖

新增專案名稱並建立專案後,系統會將您帶往專案資訊主頁:

Firebase 專案首頁

在這個資訊主頁中,按一下左上角專案名稱旁的齒輪圖示,然後點選「專案設定」。

Firebase 專案設定選單

在設定頁面中,按一下「雲端通訊」分頁標籤。

Firebase 專案雲端通訊選單

這個頁面包含推播訊息的 API 金鑰 (稍後會用到),以及我們需要在下一節的網頁應用程式資訊清單中放入的寄件者 ID。

新增網頁應用程式資訊清單

針對推播,我們需要新增含有 gcm_sender_id 欄位的資訊清單檔案,才能成功訂閱推播。只有 Chrome、Android 版 Opera 和 Samsung Browser 需要這個參數,才能使用 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>"
}

您需要將 Firebase 專案中的 gcm_sender_id 值設為傳送者 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 物件,其中包含 端點

對於每位使用者,請在伺服器上儲存 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';
        }
        });
    });
}

此時,您的網路應用程式已可接收推播訊息,但在我們將推播事件事件監聽器新增至服務工作者檔案之前,不會有任何動作。

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() 方法。此方法會進入承諾,並延長事件處理常式的生命週期 (或是可視為保持服務工作站持續運作),直到承諾達成為止。在這種情況下,傳遞至 event.waitUntil 的承諾會由 showNotification() 傳回。

通知標記可做為不重複通知的 ID。如果我們傳送兩則推送訊息至同一個端點,且兩者之間有短暫延遲,並顯示同一個標記的通知,瀏覽器會顯示第一則通知,並在收到推送訊息時將其替換為第二則通知。

如果您想一次顯示多則通知,請使用其他代碼,或不使用任何代碼。我們將在本文稍後的部分,介紹更完整的顯示通知範例。我們先簡化一下,看看傳送推播訊息時是否會顯示這則通知。

傳送推送訊息

我們已訂閱推送訊息,我們的 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 位址已列入該專案的許可清單。
  • 視您傳送的資料是 JSON 還是表單資料而定,選擇適當的 Content-Type 標頭:application/jsonapplication/x-www-form-urlencoded;charset=UTF-8
  • registration_ids 陣列 - 這些是您從使用者端點擷取的註冊 ID。

請參閱相關說明文件,瞭解如何從伺服器傳送推送訊息,但若要快速確認服務工作站,可以使用 cURL 傳送推送訊息至瀏覽器。

將這個 cURL 指令中的 &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 未來也能支援),您需要導入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() 會做出承諾,也就是導致 showNotification() 傳回的承諾。也就是說,在非同步 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 傳送 POST 要求的回應主體,並尋找 error:NotRegisteredcanonical_id 結果,詳情請參閱 FCM 說明文件

Service Worker 和伺服器之間的訂閱項目也可能不一致。例如,在成功訂閱/取消訂閱後,不穩定的網路連線可能會導致您無法更新伺服器;或者使用者可能會撤銷通知權限,導致自動取消訂閱。如要處理這類情況,請定期 (例如在網頁載入時) 檢查 serviceWorkerRegistration.pushManager.getSubscription() 的結果,並與伺服器同步。如果您不再訂閱,且 Notification.permission == 'granted',您可能也想自動重新訂閱。

sendSubscriptionToServer() 中,您必須考量如何在更新 endpoint 時處理失敗的網路要求。其中一種解決方法是追蹤 Cookie 中的 endpoint 狀態,藉此判斷您的伺服器是否需要最新詳細資料。

上述所有步驟完成後,Chrome 46 就會在網頁上完整實作推播訊息功能。我們仍提供一些可讓您更輕鬆操作的功能 (例如用於觸發推播訊息的標準 API),但您現在就能開始在網路應用程式中建構推播訊息。

如何對網頁應用程式偵錯

在實作推播訊息時,錯誤會出現在以下任一位置:網頁或服務工作者。

您可以使用DevTools偵錯網頁中的錯誤。如要偵錯 Service Worker 問題,有兩種方法:

  1. 前往 chrome://inspect > Service workers。除了目前執行中的服務工作者,這個檢視畫面不會提供太多資訊。
  2. 前往 chrome://serviceworker-internals,您可以在這裡查看服務工作站的狀態,並看到錯誤 (如果有的話)。這個頁面是暫時性的,直到開發人員工具提供類似的功能集為止。

對於服務工作程式新手,我能提供的最佳提示之一,就是使用名為「Open DevTools window and pause JavaScript execution on service worker startup for debugging」的核取方塊。這個核取方塊會在服務工作站啟動時新增中斷點並「暫停執行」,方便您繼續執行或逐步執行服務工作站指令碼,看看是否遇到任何問題。

螢幕截圖:顯示 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:....」的清單項目,其中包含服務 worker 的網址。

顯示服務工作處理程序在 Chrome 中檢查位置的螢幕截圖

推播通知的使用者體驗

Chrome 團隊已彙整一份文件,其中包含推播通知使用者體驗的最佳做法,以及使用推播通知時的某些極端情況。

Chrome 和開放網路上的推播訊息未來發展

本節將詳細說明此實作項目的部分 Chrome 專屬部分,以及與其他瀏覽器實作項目的差異。

網路推送通訊協定和端點

Push API 標準的好處是,您應能擷取端點、將其傳送至伺服器,並透過實作 Web Push Protocol 傳送推送訊息。

Web Push 通訊協定是推播供應商可實作的全新標準,可讓開發人員不必擔心推播供應商為何人。這項通訊協定的概念是避免使用者需要註冊 API 金鑰,以及傳送格式特別的資料 (如使用 FCM 時)。

Chrome 是第一個實作 Push API 的瀏覽器,而 FCM 不支援 Web Push 通訊協定,因此 Chrome 要求使用 gcm_sender_id,因此您必須使用 FCM 適用的靜態 API。

Chrome 的最終目標是透過 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,然後在伺服器端比對原生應用程式和網頁應用程式訂閱 ID,並決定要將推播訊息傳送至哪一個。您可以透過螢幕大小、裝置型號,在網頁版應用程式和原生應用程式之間共用產生的金鑰,但每種方法都有優缺點。

為什麼需要 gcm_sender_id?

這項設定是必要的,才能讓 Chrome、Android 版 Opera 和 Samsung 瀏覽器使用 Firebase 雲端通訊 (FCM) API。目標是在標準定案且 FCM 支援時使用網路推送通訊協定。

為什麼不使用 Web Sockets 或伺服器傳送事件 (EventSource)?

使用推播訊息的好處是,即使網頁已關閉,服務工作者仍會喚醒並顯示通知。當網頁或瀏覽器關閉時,Web Sockets 和 EventSource 的連線就會關閉。

如果我不需要背景事件提交功能,該怎麼辦?

如果您不需要背景提交,Web Sockets 就是不錯的選擇。

何時可以使用推播功能,而不顯示通知 (即靜默背景推播)?

目前尚未決定何時推出這項功能,但我們有意圖實作背景同步功能,雖然尚未決定或規範,但我們已討論過如何透過背景同步功能啟用無聲推播。

為什麼要求採用 HTTPS?如何在開發期間解決這個問題?

Service Worker 需要安全來源來確保 Service Worker 指令碼來自預定來源,且不出自中間人攻擊。目前這表示在實際網站上使用 HTTPS,不過在開發期間會使用 localhost。

瀏覽器支援功能的運作方式為何?

Chrome 穩定版支援這項功能,Mozilla 也正在 Firefox Nightly 中推動這項功能。詳情請參閱「導入推送 API」錯誤,您也可以在這裡追蹤通知導入作業。

我可以在一段時間後移除通知嗎?

目前無法做到,但我們預計會新增支援功能,讓您取得目前顯示的通知清單。如果您有用途,需要在通知顯示建立後設定到期日,我們很樂意瞭解那是什麼,因此請新增留言,我們會將其傳回給 Chrome 團隊。

如果您只想在特定時間後停止向使用者傳送推播通知,且不關心通知會顯示多久,可以使用 FCM 的有效時間 (ttl) 參數,請參閱這篇文章瞭解詳情。

Chrome 中的推播訊息有哪些限制?

這篇文章會說明幾項限制:

  • Chrome 使用 CCM 做為推播服務,因此會產生一些專屬要求。我們正努力合作,確認日後是否有新功能可提升成效。
  • 您必須在收到推播訊息時顯示通知。
  • 電腦版 Chrome 有個警告,如果 Chrome 未執行,就不會收到推播訊息。這與 ChromeOS 和 Android 不同,後者會一律接收推播訊息。

我們不應該使用 Permissions API 嗎?

雖然 Chrome 中已實作 Permission API,但不一定適用於所有瀏覽器。詳情請參閱這篇文章

為什麼點選通知後,Chrome 不會開啟先前的分頁?

這個問題只會影響目前由服務工作站控管的網頁。您可以參閱這個網頁來瞭解詳情。

如果使用者裝置收到推送通知時,通知已過期,該怎麼辦?

收到推送訊息時,一律必須顯示通知。 如果您想傳送通知,但該通知只在特定時間內有效,您可以在 CCM 上使用「time_to_live」參數,這樣在到期時間過後,FCM 就不會傳送推播訊息。

詳情請參閱這篇文章

如果我傳送 10 則推播訊息,但只想讓裝置接收一則,會發生什麼情況?

FCM 有一個「collapse_key」參數,可用來告知 FCM 將任何具有相同「collapse_key」的待處理訊息,替換為新訊息。

詳情請參閱這篇文章