Thông báo đẩy trên web mở

Nếu bạn hỏi các nhà phát triển về những tính năng trên thiết bị di động bị thiếu trên web, thì thông báo đẩy sẽ luôn ở vị trí cao trong danh sách.

Thông báo đẩy cho phép người dùng chọn nhận nội dung cập nhật kịp thời từ các trang web họ yêu thích và cho phép bạn tương tác lại một cách hiệu quả với họ bằng nội dung hấp dẫn và được tuỳ chỉnh.

Kể từ Chrome phiên bản 42, nhà phát triển có thể sử dụng API đẩyAPI thông báo.

API Đẩy trong Chrome dựa trên một số công nghệ, bao gồm Tệp kê khai ứng dụng webTrình chạy dịch vụ. Trong bài đăng này, chúng ta sẽ xem xét từng công nghệ trong số này, nhưng chỉ những công nghệ tối thiểu để thiết lập và chạy thông báo đẩy. Để hiểu rõ hơn về một số tính năng khác của tệp kê khai và các chức năng ngoại tuyến của trình chạy dịch vụ, vui lòng xem các đường liên kết ở trên.

Chúng ta cũng sẽ xem xét nội dung sẽ được thêm vào API trong các phiên bản Chrome sau này. Cuối cùng, chúng tôi sẽ cung cấp Câu hỏi thường gặp.

Triển khai Thông báo đẩy cho Chrome

Phần này mô tả từng bước bạn cần hoàn thành để hỗ trợ tính năng thông báo đẩy trong ứng dụng web.

Đăng ký một Trình chạy dịch vụ

Có một phần phụ thuộc là việc có một trình chạy dịch vụ để triển khai thông báo đẩy cho web. Nguyên nhân là do khi nhận được một thông báo đẩy, trình duyệt có thể khởi động một trình chạy dịch vụ. Trình chạy này chạy trong nền mà không có trang đang mở và gửi một sự kiện để bạn có thể quyết định cách xử lý thông báo đẩy đó.

Dưới đây là ví dụ về cách bạn đăng ký một trình chạy dịch vụ trong ứng dụng web của mình. Khi quá trình đăng ký hoàn tất thành công, chúng ta sẽ gọi initialiseState(). Chúng ta sẽ sớm đề cập đến vấn đề này.

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.');
    }
});

Trình xử lý lượt nhấp vào nút đăng ký hoặc huỷ đăng ký cho người dùng đẩy thông báo. isPushEnabled là một biến toàn cục chỉ theo dõi xem thông báo đẩy hiện có được đăng ký hay không. Các mã này sẽ được tham chiếu trong suốt các đoạn mã.

Sau đó, chúng tôi sẽ kiểm tra xem trình chạy dịch vụ có được hỗ trợ hay không trước khi đăng ký tệp service-worker.js có logic để xử lý thông báo đẩy. Ở đây, chúng tôi chỉ thông báo cho trình duyệt rằng tệp JavaScript này là trình chạy dịch vụ cho trang web của chúng tôi.

Thiết lập trạng thái ban đầu

Ví dụ về trải nghiệm người dùng thông báo đẩy đã bật và đã tắt trong Chrome.

Sau khi đăng ký trình chạy dịch vụ, chúng ta cần thiết lập trạng thái cho giao diện người dùng.

Người dùng mong muốn một giao diện người dùng đơn giản để bật hoặc tắt thông báo đẩy cho trang web của bạn, đồng thời họ sẽ kỳ vọng giao diện người dùng này sẽ luôn cập nhật mọi thay đổi xảy ra. Nói cách khác, nếu kích hoạt thông báo đẩy cho trang web của bạn, hãy rời khỏi và quay lại sau một tuần, giao diện người dùng sẽ nêu bật rằng thông báo đẩy đã được bật.

Bạn có thể tìm thấy một số nguyên tắc về trải nghiệm người dùng trong tài liệu này. Trong bài viết này, chúng tôi sẽ tập trung vào các khía cạnh kỹ thuật.

Tại thời điểm này, có thể bạn đang cho rằng chỉ có hai trạng thái để xử lý là bật hoặc tắt. Tuy nhiên, có một số trạng thái khác xung quanh thông báo mà bạn cần tính đến.

Sơ đồ nêu bật các điểm khác nhau cần cân nhắc và trạng thái đẩy trong Chrome

Chúng ta cần kiểm tra một số API trước khi bật nút. Nếu mọi thứ được hỗ trợ, chúng ta có thể bật giao diện người dùng và đặt trạng thái ban đầu để cho biết thông báo đẩy có được đăng ký hay không.

Vì phần lớn các bước kiểm tra này đều dẫn đến việc giao diện người dùng của chúng tôi bị vô hiệu hoá, bạn nên đặt trạng thái ban đầu thành tắt. Điều này cũng giúp tránh mọi nhầm lẫn nếu có vấn đề với JavaScript trên trang của bạn, ví dụ: không thể tải tệp JS xuống hoặc người dùng đã tắt JavaScript.

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

Với trạng thái ban đầu này, chúng ta có thể thực hiện các bước kiểm tra nêu trên trong phương thức initialiseState(), tức là sau khi trình chạy dịch vụ được đăng ký.

// 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);
        });
    });
}

Tổng quan ngắn gọn về các bước này:

  • Chúng tôi kiểm tra để đảm bảo showNotification có trong nguyên mẫu ServiceWorkerRegistration. Nếu không có thông báo này, chúng tôi sẽ không thể hiển thị thông báo từ trình chạy dịch vụ khi nhận được thông báo đẩy.
  • Chúng tôi kiểm tra xem Notification.permission hiện tại là gì để đảm bảo đó không phải là "denied". Quyền bị từ chối có nghĩa là bạn không thể hiện thông báo cho đến khi người dùng thay đổi quyền này theo cách thủ công trong trình duyệt.
  • Để kiểm tra xem thông báo đẩy có được hỗ trợ hay không, chúng tôi sẽ kiểm tra xem PushManager có trong đối tượng cửa sổ hay không.
  • Cuối cùng, chúng tôi sử dụng pushManager.getSubscription() để kiểm tra xem mình đã có gói thuê bao hay chưa. Nếu có, chúng tôi sẽ gửi thông tin chi tiết về gói thuê bao đến máy chủ để đảm bảo chúng tôi có thông tin chính xác, đồng thời thiết lập giao diện người dùng để cho biết thông báo đẩy đã được bật hay chưa. Chúng ta sẽ xem xét những thông tin chi tiết nào có trong đối tượng gói thuê bao ở phần sau của bài viết này.

Chúng tôi sẽ đợi cho đến khi navigator.serviceWorker.ready được giải quyết để kiểm tra gói thuê bao và bật nút đẩy vì chỉ sau khi trình thực thi dịch vụ hoạt động thì bạn mới thực sự đăng ký được thông báo đẩy.

Bước tiếp theo là xử lý thời điểm người dùng muốn bật thông báo đẩy, nhưng trước khi có thể thực hiện việc này, chúng ta cần thiết lập dự án Google Developer Console và thêm một số tham số vào tệp kê khai để sử dụng Giải pháp gửi thông báo qua đám mây của Firebase (FCM), trước đây là Google Cloud Messaging (GCM).

Tạo dự án trên Firebase Developer Console

Chrome sử dụng FCM để xử lý việc gửi và phân phối thông báo đẩy. Tuy nhiên, để sử dụng API FCM, bạn cần thiết lập một dự án trên Bảng điều khiển dành cho nhà phát triển của Firebase.

Các bước sau dành riêng cho Chrome, Opera dành cho Android và Trình duyệt Samsung sử dụng FCM. Chúng ta sẽ thảo luận về cách hoạt động của tính năng này trong các trình duyệt khác ở phần sau của bài viết.

Tạo Dự án mới dành cho nhà phát triển Firebase

Để bắt đầu, bạn cần tạo một dự án mới trên https://console.firebase.google.com/ bằng cách nhấp vào "Create New Project" (Tạo dự án mới).

Ảnh chụp màn hình dự án Firebase mới

Thêm tên dự án, tạo dự án và bạn sẽ được đưa đến trang tổng quan dự án:

Trang chủ dự án Firebase

Trên trang tổng quan này, hãy nhấp vào biểu tượng bánh răng bên cạnh tên dự án ở góc trên cùng bên trái và nhấp vào "Cài đặt dự án".

Trình đơn cài đặt dự án Firebase

Trong trang cài đặt, nhấp vào tab 'Nhắn tin qua đám mây'.

Trình đơn Nhắn tin qua đám mây của dự án Firebase

Trang này chứa khoá API cho thông báo đẩy mà chúng ta sẽ sử dụng sau này và mã nhận dạng người gửi mà chúng ta cần đưa vào tệp kê khai ứng dụng web trong phần tiếp theo.

Thêm tệp kê khai ứng dụng web

Đối với thông báo đẩy, chúng ta cần thêm tệp kê khai có trường gcm_sender_id để giúp gói thuê bao đẩy thành công. Chỉ Trình duyệt Chrome, Opera dành cho Android và Samsung yêu cầu thông số này để có thể sử dụng FCM / GCM.

Các trình duyệt này sử dụng gcm_sender_id khi đăng ký một thiết bị của người dùng bằng FCM. Tức là FCM có thể xác định thiết bị của người dùng và đảm bảo mã nhận dạng người gửi khớp với khoá API tương ứng và người dùng đã cho phép máy chủ của bạn gửi cho họ thông báo đẩy.

Dưới đây là một tệp kê khai cực kỳ đơn giản:

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

Bạn cần phải đặt giá trị gcm_sender_id thành mã người gửi từ Dự án Firebase của mình.

Sau khi bạn đã lưu tệp kê khai trong dự án của mình (manifest.json là một tên hay), hãy tham chiếu tệp kê khai đó từ HTML của bạn bằng thẻ sau trong phần đầu trang.

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

Nếu không thêm tệp kê khai web có các tham số này, bạn sẽ nhận được một trường hợp ngoại lệ khi cố gắng đăng ký cho người dùng để đẩy thông báo (với lỗi "Registration failed - no sender id provided" hoặc "Registration failed - permission denied").

Đăng ký nhận thông báo đẩy

Bây giờ, bạn đã thiết lập xong tệp kê khai, bạn có thể quay lại JavaScript của trang web.

Để đăng ký, bạn phải gọi phương thức subscribe() trên đối tượng PushManager mà bạn truy cập thông qua ServiceWorkerRegistration.

Thao tác này sẽ yêu cầu người dùng cấp cho bạn quyền gốc để gửi thông báo đẩy. Nếu không có quyền này, bạn sẽ không thể đăng ký thành công.

Nếu promise mà phương thức subscribe() trả về được giải quyết, bạn sẽ được cấp một đối tượng PushSubscription chứa một điểm cuối.

Bạn phải lưu điểm cuối trên máy chủ cho từng người dùng, vì sau này bạn sẽ cần họ gửi thông báo đẩy.

Đoạn mã sau đây đăng ký cho người dùng nhận thông báo đẩy:

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';
        }
        });
    });
}

Tại thời điểm này, ứng dụng web của bạn đã sẵn sàng nhận thông báo đẩy, mặc dù sẽ không có gì xảy ra cho đến khi chúng ta thêm trình nghe sự kiện đẩy vào tệp trình chạy dịch vụ của mình.

Trình nghe sự kiện đẩy trình chạy dịch vụ

Khi nhận được thông báo đẩy (chúng tôi sẽ nói về cách thực sự gửi một thông báo đẩy trong phần tiếp theo), một sự kiện đẩy sẽ được gửi trong trình chạy dịch vụ của bạn, tại thời điểm đó bạn cần hiển thị thông báo.

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
    })
    );
});

Mã này đăng ký trình nghe sự kiện đẩy và hiển thị thông báo có tiêu đề, văn bản nội dung, biểu tượng và thẻ thông báo được xác định trước. Một điểm tinh tế đáng chú ý trong ví dụ này là phương thức event.waitUntil(). Phương thức này đảm bảo lời hứa và kéo dài thời gian hoạt động của trình xử lý sự kiện (hoặc có thể được coi là duy trì hoạt động của trình thực thi dịch vụ) cho đến khi lời hứa được giải quyết; Trong trường hợp này, lời hứa được chuyển đến event.waitUntil là Lời hứa được trả về từ showNotification().

Thẻ thông báo đóng vai trò là giá trị nhận dạng cho các thông báo riêng biệt. Nếu chúng ta đã gửi hai thông báo đẩy đến cùng một điểm cuối, với độ trễ ngắn giữa các thông báo đó và hiển thị thông báo bằng cùng một thẻ, thì trình duyệt sẽ cho thấy thông báo đầu tiên và thay thế bằng thông báo thứ hai khi nhận được thông báo đẩy.

Nếu bạn muốn hiển thị nhiều thông báo cùng một lúc, hãy sử dụng một thẻ khác hoặc không dùng thẻ nào. Chúng ta sẽ xem xét ví dụ hoàn chỉnh hơn về việc hiển thị thông báo ở phần sau trong bài đăng này. Bây giờ, hãy đơn giản hoá mọi thứ và xem liệu việc gửi thông báo đẩy có hiển thị thông báo này không.

Đang gửi thông báo đẩy

Chúng tôi đã đăng ký nhận thông báo đẩy và trình chạy dịch vụ của chúng tôi đã sẵn sàng hiển thị thông báo, vì vậy, đã đến lúc gửi thông báo đẩy qua FCM.

Cách này chỉ áp dụng cho các trình duyệt sử dụng FCM.

Khi bạn gửi biến PushSubscription.endpoint đến máy chủ của mình, điểm cuối của FCM sẽ đặc biệt. Có một tham số ở cuối URL là registration_id.

Ví dụ về điểm cuối:

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

URL FCM là:

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

registration_id sẽ là:

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

Thao tác này dành riêng cho các trình duyệt sử dụng FCM. Trong trình duyệt thông thường, bạn chỉ cần nhận một điểm cuối và bạn sẽ gọi điểm cuối đó theo cách tiêu chuẩn và điểm cuối sẽ hoạt động bất kể URL là gì.

Điều này có nghĩa là trên máy chủ của bạn, bạn cần kiểm tra xem điểm cuối có phải là dành cho FCM hay không và nếu điểm cuối đó là dành cho FCM, hãy trích xuất registration_id. Để thực hiện việc này bằng Python, bạn có thể làm những việc như:

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

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

Sau khi có ID đăng ký, bạn có thể gọi đến API FCM. Bạn có thể tìm thấy tài liệu tham khảo về API FCM tại đây.

Các khía cạnh chính cần nhớ khi gọi FCM là:

  • Bạn phải đặt tiêu đề Uỷ quyền có giá trị key=&lt;YOUR_API_KEY&gt; khi gọi API, trong đó &lt;YOUR_API_KEY&gt; là khoá API từ dự án Firebase.
    • Khoá API được FCM sử dụng để tìm mã người gửi phù hợp, đảm bảo người dùng đã cấp quyền cho dự án của bạn và cuối cùng đảm bảo rằng địa chỉ IP của máy chủ có trong danh sách cho phép đối với dự án đó.
  • Tiêu đề Content-Type thích hợp là application/json hoặc application/x-www-form-urlencoded;charset=UTF-8 tuỳ thuộc vào việc bạn gửi dữ liệu dưới dạng JSON hay dữ liệu biểu mẫu.
  • Một mảng registration_ids – đây là những mã nhận dạng đăng ký mà bạn sẽ trích xuất từ các điểm cuối của người dùng.

Vui lòng xem tài liệu về cách gửi thông báo đẩy từ máy chủ của bạn, nhưng để kiểm tra nhanh trình chạy dịch vụ, bạn có thể sử dụng cURL để gửi thông báo đẩy đến trình duyệt của mình.

Hoán đổi &lt;YOUR_API_KEY&gt;&lt;YOUR_REGISTRATION_ID&gt; trong lệnh cURL này bằng lệnh cURL của bạn và chạy trên một thiết bị đầu cuối.

Bạn sẽ thấy một thông báo thú vị:

    curl --header "Authorization: key=<YOUR_API_KEY>" --header
    "Content-Type: application/json" https://fcm.googleapis.com/fcm/send -d
    "{\"registration_ids\":[\"<YOUR_REGISTRATION_ID>\"]}"
Ví dụ về thông báo đẩy từ Chrome dành cho Android.

Khi phát triển logic phụ trợ, hãy nhớ rằng tiêu đề Uỷ quyền và định dạng của nội dung POST dành riêng cho điểm cuối FCM, vì vậy, hãy phát hiện thời điểm điểm cuối dành cho FCM và thêm tiêu đề cũng như định dạng nội dung POST theo điều kiện. Đối với các trình duyệt khác (và hy vọng là Chrome trong tương lai), bạn sẽ cần triển khai Giao thức đẩy web.

Nhược điểm của cách triển khai API đẩy hiện tại trong Chrome là bạn không thể gửi bất kỳ dữ liệu nào bằng thông báo đẩy. Không, không có gì. Lý do là vì trong tương lai, dữ liệu tải trọng sẽ phải được mã hoá trên máy chủ của bạn trước khi được gửi đến điểm cuối của thông báo đẩy. Theo đó, điểm cuối, bất kể có trình cung cấp dịch vụ đẩy nào, sẽ không thể dễ dàng xem nội dung của thông báo đẩy. Điều này cũng giúp chống lại các lỗ hổng bảo mật khác như chứng chỉ HTTPS xác thực kém và các cuộc tấn công xen giữa giữa máy chủ và trình cung cấp dịch vụ đẩy. Tuy nhiên, lớp mã hoá này chưa được hỗ trợ. Vì vậy, trong thời gian chờ đợi, bạn cần thực hiện tìm nạp để lấy thông tin cần thiết cho việc điền thông báo.

Ví dụ về sự kiện đẩy hoàn chỉnh hơn

Thông báo mà chúng ta đã thấy cho đến thời điểm này khá cơ bản. Đối với các mẫu, thông báo này khá kém trong việc bao gồm một trường hợp sử dụng thực tế.

Trên thực tế, hầu hết mọi người sẽ muốn lấy một số thông tin từ máy chủ của họ trước khi hiển thị thông báo. Đây có thể là dữ liệu để điền nội dung cụ thể vào tiêu đề thông báo và thông báo, hoặc tiến thêm một bước và lưu một số trang hoặc dữ liệu vào bộ nhớ đệm để khi người dùng nhấp vào thông báo, mọi thứ sẽ có ngay khi trình duyệt mở, ngay cả khi khi đó chưa kết nối mạng.

Trong mã sau, chúng ta tìm nạp một số dữ liệu từ API, chuyển đổi phản hồi thành một đối tượng và sử dụng đối tượng đó để điền thông báo.

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
        });
    })
    );
});

Một lần nữa, hãy nhấn mạnh rằng event.waitUntil() cam kết dẫn đến lời hứa mà showNotification() trả về, nghĩa là trình nghe sự kiện của chúng ta sẽ không thoát cho đến khi lệnh gọi fetch() không đồng bộ hoàn tất và thông báo được hiển thị.

Bạn sẽ nhận thấy rằng chúng tôi vẫn hiện thông báo ngay cả khi có lỗi. Lý do là nếu chúng ta không làm như vậy, Chrome sẽ hiển thị thông báo chung của riêng Chrome.

Mở URL khi người dùng nhấp vào một thông báo

Khi người dùng nhấp vào một thông báo, sự kiện notificationclick sẽ được gửi trong trình chạy dịch vụ của bạn. Trong trình xử lý, bạn có thể thực hiện hành động thích hợp, chẳng hạn như đặt tiêu điểm vào một thẻ hoặc mở một cửa sổ có một URL cụ thể:

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('/');
        }
    })
    );
});

Ví dụ này mở trình duyệt đến thư mục gốc của trang web, bằng cách tập trung vào một thẻ cùng nguồn gốc hiện có (nếu có), và mở một thẻ mới.

Có một bài đăng dành riêng cho một số việc bạn có thể làm với Notification API tại đây.

Huỷ đăng ký thiết bị của người dùng

Bạn đã đăng ký thiết bị của người dùng và họ sẽ nhận được thông báo đẩy, nhưng làm cách nào để bạn huỷ đăng ký họ?

Việc chính cần làm để huỷ đăng ký thiết bị của người dùng là gọi phương thức unsubscribe() trên đối tượng PushSubscription và xoá điểm cuối khỏi máy chủ của bạn (để bạn không gửi thông báo đẩy mà bạn biết rằng mình sẽ không nhận được thông báo này). Mã dưới đây thực hiện chính xác điều này:

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);
        });
    });
}

Duy trì cập nhật gói thuê bao

Gói thuê bao có thể không đồng bộ hoá giữa FCM và máy chủ của bạn. Hãy đảm bảo máy chủ của bạn phân tích cú pháp nội dung phản hồi của yêu cầu POST qua của API FCM để tìm kết quả error:NotRegisteredcanonical_id, như giải thích trong tài liệu về FCM.

Các gói thuê bao cũng có thể không đồng bộ hoá giữa trình chạy dịch vụ và máy chủ của bạn. Ví dụ: sau khi đăng ký/huỷ đăng ký thành công, kết nối mạng không ổn định có thể khiến bạn không cập nhật được máy chủ; hoặc người dùng có thể thu hồi quyền gửi thông báo, kích hoạt thao tác huỷ đăng ký tự động. Hãy xử lý các trường hợp như vậy bằng cách kiểm tra định kỳ kết quả của serviceWorkerRegistration.pushManager.getSubscription() (ví dụ: khi tải trang) và đồng bộ hoá kết quả đó với máy chủ. Bạn cũng nên tự động đăng ký lại nếu không còn gói thuê bao và Notification.permission == 'granted'.

Trong sendSubscriptionToServer(), bạn sẽ cần xem xét cách xử lý các yêu cầu mạng không thành công khi cập nhật endpoint. Một giải pháp là theo dõi trạng thái của endpoint trong cookie để xác định xem máy chủ của bạn có cần thông tin chi tiết mới nhất hay không.

Tất cả các bước trên giúp triển khai đầy đủ thông báo đẩy trên web trong Chrome 46. Vẫn còn một số tính năng theo quy cách giúp mọi thứ trở nên dễ dàng hơn (như API tiêu chuẩn để kích hoạt thông báo đẩy). Tuy nhiên, bản phát hành này cho phép bạn bắt đầu tạo thông báo đẩy vào ứng dụng web của mình ngay hôm nay.

Cách gỡ lỗi ứng dụng web

Trong khi triển khai thông báo đẩy, lỗi sẽ xuất hiện ở một trong hai vị trí: trang hoặc trình chạy dịch vụ.

Bạn có thể gỡ lỗi trên trang bằng DevTools. Để gỡ lỗi các vấn đề về trình chạy dịch vụ, bạn có 2 lựa chọn:

  1. Chuyển đến chrome://Inspect > Service worker (Trình chạy dịch vụ). Khung hiển thị này không cung cấp nhiều thông tin ngoài các trình chạy dịch vụ hiện đang chạy.
  2. Truy cập vào chrome://serviceworker-internals. Từ đây, bạn có thể xem trạng thái của trình chạy dịch vụ cũng như xem các lỗi (nếu có). Trang này là trang tạm thời cho đến khi Công cụ cho nhà phát triển có một bộ tính năng tương tự.

Một trong những mẹo hay nhất mà tôi có thể hướng dẫn cho bất cứ ai mới sử dụng trình chạy dịch vụ là hãy sử dụng hộp đánh dấu có tên "Mở cửa sổ Công cụ cho nhà phát triển và tạm dừng thực thi JavaScript khi khởi động trình chạy dịch vụ để gỡ lỗi". Hộp đánh dấu này sẽ thêm điểm ngắt khi bắt đầu trình chạy dịch vụ và tạm dừng thực thi. Thao tác này cho phép bạn tiếp tục hoặc duyệt qua tập lệnh trình chạy dịch vụ và xem liệu bạn có gặp vấn đề nào không.

Ảnh chụp màn hình cho thấy vị trí của hộp đánh dấu tạm dừng thực thi trên serviceworker-internals.

Nếu có vẻ như có sự cố giữa FCM và sự kiện đẩy của trình chạy dịch vụ, thì bạn không thể làm gì để gỡ lỗi sự cố vì không có cách nào để bạn biết liệu Chrome có nhận được gì hay không. Điều quan trọng cần đảm bảo là phản hồi từ FCM sẽ thành công khi máy chủ của bạn thực hiện lệnh gọi API. Giao diện sẽ có dạng như sau:

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

Hãy lưu ý phản hồi "success": 1. Thay vào đó, nếu bạn thấy lỗi, thì điều đó cho thấy rằng có gì đó không đúng với ID đăng ký FCM và thông báo đẩy không được gửi đến Chrome.

Gỡ lỗi Trình chạy dịch vụ trên Chrome dành cho Android

Hiện tại, việc gỡ lỗi trình chạy dịch vụ trên Chrome dành cho Android chưa rõ ràng. Bạn cần chuyển đến chrome://inspect, tìm thiết bị của bạn rồi tìm một mục trong danh sách có tên "Worker pid:...." có URL của worker của bạn.

Ảnh chụp màn hình cho thấy vị trí của trình thực thi dịch vụ trong quá trình kiểm tra của Chrome

Trải nghiệm người dùng cho thông báo đẩy

Nhóm Chrome đã tổng hợp một tài liệu gồm các phương pháp hay nhất về trải nghiệm người dùng của thông báo đẩy, cũng như một tài liệu trình bày một số trường hợp đặc biệt khi làm việc với thông báo đẩy.

Tương lai của thông báo đẩy trên Chrome và web mở

Phần này trình bày chi tiết về một số phần cụ thể của Chrome trong quá trình triển khai này mà bạn nên lưu ý cũng như sự khác biệt giữa các phần này với các cách triển khai trình duyệt khác.

Giao thức đẩy của web và điểm cuối

Điểm hay của tiêu chuẩn API Đẩy là bạn có thể lấy điểm cuối, truyền điểm cuối đó đến máy chủ và gửi thông báo đẩy bằng cách triển khai Giao thức đẩy web.

Giao thức đẩy web là một tiêu chuẩn mới mà các trình cung cấp dịch vụ đẩy có thể triển khai, cho phép các nhà phát triển không phải lo lắng về việc ai là trình cung cấp dịch vụ đẩy. Ý tưởng ở đây là giúp tránh phải đăng ký các khoá API và gửi dữ liệu có định dạng đặc biệt, như bạn phải làm với FCM.

Chrome là trình duyệt đầu tiên triển khai API Đẩy và FCM không hỗ trợ Giao thức đẩy web. Đó là lý do tại sao Chrome yêu cầu gcm_sender_id và bạn cần sử dụng API yên tĩnh cho FCM.

Mục tiêu cuối cùng của Chrome là hướng tới sử dụng Giao thức đẩy web với Chrome và FCM.

Cho đến lúc đó, bạn cần phát hiện điểm cuối "https://fcm.googleapis.com/fcm/send" và xử lý điểm cuối đó riêng biệt với các điểm cuối khác, tức là định dạng dữ liệu tải trọng theo cách cụ thể và thêm khoá uỷ quyền.

Cách triển khai Giao thức đẩy web?

Firefox Nightly hiện đang nghiên cứu công nghệ đẩy và có thể sẽ là trình duyệt đầu tiên triển khai Giao thức đẩy web.

Câu hỏi thường gặp

Thông số kỹ thuật nằm ở đâu?

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

Tôi có thể ngăn chặn các thông báo trùng lặp nếu sự hiện diện của tôi trên web có nhiều nguồn gốc hoặc nếu tôi có cả phiên bản gốc và phiên bản web?

Hiện chưa có giải pháp cho vấn đề này, nhưng bạn có thể theo dõi tiến trình trên Chromium.

Trường hợp lý tưởng là có một số loại mã nhận dạng cho thiết bị của người dùng, sau đó ở phía máy chủ so khớp mã nhận dạng gói thuê bao của ứng dụng web và ứng dụng gốc, rồi quyết định xem sẽ gửi thông báo đẩy đến mã nào. Bạn có thể thực hiện việc này thông qua kích thước màn hình, mẫu thiết bị, chia sẻ khoá được tạo giữa ứng dụng web và ứng dụng gốc, nhưng mỗi phương pháp đều có ưu và nhược điểm riêng.

Tại sao tôi cần có gcm_sender_id?

Đây là yêu cầu bắt buộc để Chrome, Opera dành cho Android và Trình duyệt Samsung có thể sử dụng API Giải pháp gửi thông báo qua đám mây của Firebase (FCM). Mục tiêu là sử dụng Giao thức đẩy web khi tiêu chuẩn được hoàn thiện và FCM có thể hỗ trợ giao thức này.

Tại sao không sử dụng Web Sockets hoặc Sự kiện do máy chủ gửi (EventSource)?

Lợi thế của việc sử dụng thông báo đẩy là ngay cả khi trang của bạn bị đóng, trình chạy dịch vụ sẽ được đánh thức và có thể hiển thị thông báo. Web Sockets và EventSource sẽ đóng kết nối khi trang hoặc trình duyệt đóng.

Nếu tôi không cần phân phối sự kiện nền thì sao?

Nếu bạn không cần phân phối dưới nền thì Web Sockets là một lựa chọn tuyệt vời.

Khi nào tôi có thể sử dụng tính năng đẩy mà không hiển thị thông báo (tức là đẩy trong nền ở chế độ im lặng)?

Chưa có tiến trình cho thời điểm tính năng này hoạt động, nhưng có ý định triển khai tính năng đồng bộ hoá ở chế độ nền và mặc dù chưa được quyết định hoặc đưa ra thông số kỹ thuật, nhưng vẫn có một số nội dung thảo luận về việc bật tính năng đẩy thầm lặng cùng với tính năng đồng bộ hoá ở chế độ nền.

Tại sao phương thức này yêu cầu giao thức HTTPS? Làm cách nào để giải quyết vấn đề này trong quá trình phát triển?

Trình chạy dịch vụ yêu cầu các nguồn gốc bảo mật để đảm bảo rằng tập lệnh của trình chạy dịch vụ đến từ nguồn gốc dự kiến và không bắt nguồn từ cuộc tấn công xen giữa. Hiện tại, điều đó có nghĩa là bạn cần sử dụng HTTPS trên các trang web trực tiếp, mặc dù máy chủ cục bộ sẽ hoạt động trong quá trình phát triển.

Việc hỗ trợ trình duyệt trông như thế nào?

Chrome hỗ trợ phiên bản ổn định và Mozilla đã triển khai tính năng đẩy trong Firefox Nightly. Hãy xem lỗi triển khai lỗi API đẩy để biết thêm thông tin và bạn có thể theo dõi cách triển khai thông báo của họ tại đây.

Tôi có thể xoá thông báo sau một khoảng thời gian nhất định không?

Hiện tại, việc này chưa thể thực hiện được, nhưng chúng tôi đang dự định bổ sung tính năng hỗ trợ để nhận danh sách các thông báo hiện đang hiển thị. Nếu bạn có trường hợp sử dụng để đặt thời hạn cho thông báo sau khi thông báo được tạo, chúng tôi rất muốn biết đó là gì, vì vậy hãy thêm nhận xét và chúng tôi sẽ chuyển lại cho nhóm Chrome.

Nếu bạn chỉ cần dừng gửi thông báo đẩy đến người dùng sau một khoảng thời gian nhất định và không quan tâm đến khoảng thời gian thông báo hiển thị, thì bạn có thể sử dụng tham số thời gian tồn tại (ttl) của FCM, tìm hiểu thêm tại đây.

Những hạn chế của thông báo đẩy trong Chrome là gì?

Có một vài giới hạn được nêu trong bài đăng này:

  • Việc Chrome sử dụng CCM làm dịch vụ đẩy sẽ tạo ra một số yêu cầu độc quyền. Chúng tôi đang hợp tác cùng nhau để xem liệu có thể gỡ bỏ một số chỉ số này trong tương lai hay không.
  • Bạn phải hiển thị thông báo khi nhận được thông báo đẩy.
  • Chrome trên máy tính lưu ý rằng nếu Chrome không chạy thì thiết bị sẽ không nhận được thông báo đẩy. Điều này khác với ChromeOS và Android, vốn luôn nhận được thông báo đẩy.

Không phải chúng ta nên sử dụng API Quyền sao?

API Quyền được triển khai trong Chrome, nhưng không nhất thiết phải hoạt động trên một số trình duyệt. Tìm hiểu thêm tại đây.

Tại sao Chrome không mở thẻ trước đó khi tôi nhấp vào một thông báo?

Vấn đề này chỉ ảnh hưởng đến các trang hiện không do một nhân viên dịch vụ kiểm soát. Bạn có thể tìm hiểu thêm tại đây.

Điều gì sẽ xảy ra nếu thông báo đã lỗi thời vào thời điểm thiết bị của người dùng nhận được thông báo đẩy?

Bạn luôn phải hiển thị thông báo khi nhận được thông báo đẩy. Trong trường hợp bạn muốn gửi một thông báo nhưng thông báo này chỉ hữu ích trong một khoảng thời gian nhất định, bạn có thể sử dụng tham số "time_to_live" trên CCM để FCM không gửi thông báo đẩy nếu đã hết thời gian hết hạn.

Bạn có thể xem thêm thông tin tại đây.

Điều gì sẽ xảy ra nếu tôi gửi 10 thông báo đẩy nhưng chỉ muốn thiết bị nhận 1 thông báo?

FCM có tham số "thu gọn_key" mà bạn có thể sử dụng để yêu cầu FCM thay thế mọi tin nhắn đang chờ xử lý có cùng "thu gọn_key" bằng tin nhắn mới.

Bạn có thể xem thêm thông tin tại đây.