開放網路上的推播通知

Matt Gaunt

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

推播通知可讓使用者選擇即時接收所關注網站的最新消息,並讓您透過自訂、吸引人的內容有效再次吸引使用者。

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

Chrome 中的 Push API 需要使用多項技術,包括網頁應用程式資訊清單Service Worker。在本文中,我們會逐一介紹這些技術,但只有啟用推送訊息的最低版本限制。如要進一步瞭解資訊清單的其他部分功能和服務工作處理程序的離線功能,請點選上方連結。

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

實作 Chrome 的推送訊息功能

本節說明在網頁應用程式中支援推送訊息時,您必須完成的每個步驟。

註冊 Service Worker

您必須讓 Service Worker 實作網頁版推送訊息。原因在於,收到推送訊息時,瀏覽器可以啟動 Service Worker,該作業在沒有開啟頁面的情況下在背景執行,然後發送事件,讓您決定如何處理該推送訊息。

以下範例說明如何在網頁應用程式中註冊 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 檔案是網站的 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);
        });
    });
}

以下簡要說明這些步驟:

  • 我們檢查 showNotification 是否在 ServiceWorkerRegistration 原型中推出。否則,系統無法在收到推送訊息時顯示來自 Service Worker 的通知。
  • 我們會檢查目前的 Notification.permission,確認其並非 "denied"。權限遭拒表示使用者必須在瀏覽器中手動變更權限,才能顯示通知。
  • 如要確認是否支援推送訊息功能,我們會檢查視窗物件中是否提供 PushManager
  • 最後,我們使用 pushManager.getSubscription() 檢查是否已訂閱過。如果有,我們會傳送訂閱詳細資料至伺服器,確保我們擁有正確的資訊,並設定使用者介面,指出推送訊息功能是否已啟用。本文稍後會介紹訂閱物件中有哪些詳細資料。

我們會等到 navigator.serviceWorker.ready 解析完成後,再檢查訂閱並啟用按鈕,因為點選按鈕才會在服務工作站啟用後,實際訂閱推送訊息。

下一步是處理使用者想要啟用推送訊息的時機,但在此之前,我們必須設定 Google Developer Console 專案,並在資訊清單中加入一些參數,以使用 Firebase 雲端通訊 (FCM) (原稱 Google Cloud Messaging (GCM))。

在 Firebase Developer Console 中建立專案

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

下列步驟僅適用於使用 FCM 的 Chrome、Opera for Android 和 Samsung 瀏覽器。本文稍後會討論此問題在其他瀏覽器中的運作方式。

建立新的 Firebase 開發人員專案

如要開始操作,請在 https://console.firebase.google.com/ 上點選 [Create New Project] (建立新專案),

新增 Firebase 專案螢幕截圖

新增專案名稱、建立專案,系統會將您導向專案資訊主頁:

Firebase 專案首頁

在這個資訊主頁中,按一下左上角專案名稱旁的齒輪,然後按一下 [Project Settings]。

Firebase 專案設定選單

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

Firebase 專案雲端通訊選單

本頁麵包含推送訊息的 API 金鑰 (稍後會用到),以及傳送者 ID,需要放在下一節的網頁應用程式資訊清單中。

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

為進行推送,我們需要新增具有 gcm_sender_id 欄位的資訊清單檔案,以便推送訂閱成功。只有 Chrome、Opera for Android 和 Samsung 瀏覽器才需要這個參數,才能使用 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 推送事件監聽器

收到推送訊息 (下一節將會說明如何實際傳送推送訊息) 時,系統會在 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,如果是 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 或表單資料傳送資料,選擇適當的 application/jsonapplication/x-www-form-urlencoded;charset=UTF-8 Content-Type 標頭。
  • 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>\"]}"
Android 版 Google Chrome 的推送訊息範例。

開發後端邏輯時,請記得 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 就會顯示其專屬的一般通知。

使用者點擊通知時開啟網址

當使用者點選通知時,系統會在您的 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),但這個版本可讓您立即開始在網頁應用程式中建立推送訊息。

如何為網頁應用程式偵錯

實作推送訊息時,錯誤會出現在以下其中一個位置:網頁或您的服務工作處理程序。

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

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

對服務工作處理程序新手而言,其中一個最佳提示就是使用「開啟開發人員工具視窗,並在服務工作站啟動時暫停 JavaScript 執行進行偵錯」核取方塊。這個核取方塊會在服務工作站啟動時新增中斷點並「暫停執行」,方便您繼續執行或逐步執行服務工作站指令碼,看看是否遇到任何問題。

螢幕截圖顯示「暫停執行」核取方塊位於 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。

在 Android 版 Google Chrome 中對 Service Worker 進行偵錯

在 Chrome for Android 上對 Service Worker 進行偵錯的目前並不清楚。您需要前往 chrome://inspect,找出裝置,然後尋找名稱為「Worker pid:....」的清單項目,這份清單含有服務工作站的網址。

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

推播通知的使用者體驗

Chrome 團隊彙整了一份關於推播通知使用者體驗的最佳做法文件,以及介紹推播通知時的一些邊緣案例的文件。

Chrome 和開放網路推送訊息的未來

本節會進一步說明實作中 Chrome 的特定部分,說明您必須瞭解,以及它與其他瀏覽器實作項目的差異。

網路推送通訊協定和端點

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

網路推送通訊協定是一項新的標準,推送提供者可以實作,讓開發人員不必擔心推送提供者的身分。這樣一來,開發人員就不必註冊 API 金鑰並傳送特殊格式的資料,就像使用 FCM 時一樣。

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

Chrome 的最終目標是改用 Chrome 和 FCM 來使用網路推送通訊協定。

在此之前,您必須偵測端點「https://fcm.googleapis.com/fcm/send」,並與其他端點分開處理,例如以特定方式設定酬載資料格式,並新增授權金鑰。

如何實作網頁推送通訊協定?

Firefox 夜間版本目前正在推送版本,可能會是第一個實作 Web Push 通訊協定的瀏覽器。

常見問題

規格在哪裡?

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

如果我的網站有多個來源,或是同時擁有網站和原生網站,我可以避免收到重複通知嗎?

我們目前沒有解決方法,但你可以透過 Chromium 追蹤進度。

理想的情況為為使用者裝置提供某種 ID,然後在伺服器端比對原生應用程式和網頁應用程式的訂閱 ID,並決定要傳送推送訊息的目標 ID。您可以透過螢幕大小、裝置型號 (在網頁應用程式和原生應用程式之間共用產生的金鑰) 執行此操作,但每種做法都有專家和缺點。

為什麼我需要 gcm_sender_id?

您必須完成這個步驟,Chrome、Opera for Android 和 Samsung 瀏覽器才能使用 Firebase 雲端通訊 (FCM) API。目標是在標準定案且 FCM 支援時使用網路推送通訊協定。

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

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

如果不需要放送背景活動,該怎麼辦?

如果您不需要背景傳送,Web Sockets 是不錯的選擇。

何時可以使用推送功能而不顯示通知 (例如靜音背景推送)?

目前我們沒有推出這項功能的時程表,不過我們有意圖導入背景同步處理,但若尚未決定或確定,但現在有討論如何啟用背景同步處理功能推送的無訊息。

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

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

瀏覽器支援是什麼樣子?

Chrome 支援穩定版,而 Mozilla 已在 Firefox 夜間版本開發。 詳情請參閱「實作 Push API 錯誤」,並按這裡追蹤 Notification API 的實作方式。

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

目前不行,但我們預計新增支援功能,以便取得目前可見通知的清單。如果您有用途,可以在建立通知後設定通知的到期時間,請向我們新增註解,我們會將註解回傳給 Chrome 小組。

如果只需要在特定時間範圍過後停止向使用者傳送推播通知,而且您不在乎通知保持顯示的時間長度,可以使用 FCM 的存留時間 (ttl) 參數,請按這裡瞭解詳情。

Chrome 推送訊息功能有哪些限制?

本文說明以下幾項限制:

  • Chrome 使用 CCM 做為推送服務會產生多項專屬需求。我們正努力合作,確認日後是否有新功能可提升成效。
  • 您必須在收到推送訊息時顯示通知。
  • 電腦版 Chrome 的注意事項是指,如果 Chrome 沒有執行,就無法接收推送訊息。這與一律會接收推送訊息的 ChromeOS 和 Android 不同。

我們不該使用 Permissions API 嗎?

雖然 Chrome 中已實作 Permission API,但不一定適用於所有瀏覽器。點此瞭解詳情

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

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

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

收到推送訊息時,一律必須顯示通知。 如果您想傳送通知,但這項功能只能用於特定一段時間,您可以在 CCM 上使用「time_to_live」參數,這樣 FCM 就不會在超過到期時間時傳送推送訊息。

詳情請參閱這篇文章

如果我傳送 10 則推送訊息,但只希望裝置接收,該怎麼辦?

您可以使用 FCM 的「debug_key」參數,告知 FCM 將任何含有「收合_key」的待處理訊息替換成新訊息。

詳情請參閱這篇文章