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

Matt Gaunt

Nếu bạn hỏi một phòng các nhà phát triển về những tính năng thiết bị di động còn thiếu trên web, thì thông báo đẩy 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 thông tin 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 với họ một cách hiệu quả bằng nội dung hấp dẫn và 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ệ này, nhưng chỉ các 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à khả 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 tôi 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, và cuối cùng sẽ có mục 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 tất để hỗ trợ tính năng thông báo đẩy trong ứng dụng web của mình.

Đăng ký Service Worker

Bạn có thể phụ thuộc vào việc có một trình chạy dịch vụ để triển khai thông báo đẩy cho web. Lý do là khi nhận được thông báo đẩy, trình duyệt có thể khởi động một trình chạy dịch vụ để chạy trong nền mà không cần mở trang 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 các trình chạy 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 nút sẽ đăng ký hoặc huỷ đăng ký người dùng gửi 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 đăng ký hay chưa. Các đối số này sẽ được tham chiếu trong suốt các đoạn mã.

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

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 được bật và tắt trong Chrome.

Sau khi đăng ký service worker, chúng ta cần thiết lập trạng thái giao diện người dùng.

Người dùng sẽ mong đợi 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ọ cũng mong muốn giao diện này luôn cập nhật mọi thay đổi xảy ra. Nói cách khác, nếu người dùng bật thông báo đẩy cho trang web của bạn, hãy thoát rồi quay lại sau một tuần, thì giao diện người dùng sẽ làm nổi bật những thông báo đẩy đã được bật.

Bạn có thể xem 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 ta sẽ tập trung vào các khía cạnh kỹ thuật.

Tại thời điểm này, bạn có thể nghĩ rằng chỉ có hai trạng thái để xử lý, đó là bật hoặc vô hiệu hoá. Tuy nhiên, có một số trạng thái khác liên quan đến thông báo mà bạn cần tính đến.

Sơ đồ nêu bật các điểm 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 đăng ký hay chưa.

Vì phần lớn các bước kiểm tra này dẫn đến việc giao diện người dùng của chúng ta bị tắt, bạn nên đặt trạng thái ban đầu thành tắt. Điều này cũng giúp bạn tránh nhầm lẫn nếu có vấn đề với JavaScript của trang, 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 đăng ký trình chạy dịch vụ.

// 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 rằng showNotification có trong nguyên mẫu đăng ký ServiceWorker. Nếu không, 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ị thông báo cho đến khi người dùng thay đổi quyền trong trình duyệt theo cách thủ công.
  • Để kiểm tra xem thông báo đẩy có được hỗ trợ hay không, chúng tôi kiểm tra để đảm bảo rằng PushManager có sẵn trong đối tượng cửa sổ.
  • Cuối cùng, chúng ta sử dụng pushManager.getSubscription() để kiểm tra xem đã có gói thuê bao hay chưa. Nếu có, chúng ta sẽ gửi thông tin chi tiết về gói thuê bao đến máy chủ để đảm bảo có thông tin chính xác và 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 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 một gói thuê bao và bật nút đẩy, vì chỉ sau khi trình chạy dịch vụ hoạt động thì bạn mới có thể đăng ký nhận thông báo đẩy.

Bước tiếp theo là xử lý khi 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ố thông 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 có tên là Google Cloud Messaging (GCM).

Tạo một dự án trên Bảng điều khiển dành cho nhà phát triển Firebase

Chrome sử dụng FCM để xử lý việc gửi và gử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 Firebase.

Các bước sau đây 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 trình duyệt 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 "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 chuyển đến trang tổng quan của 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 "Project Settings" (Cài đặt dự án).

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

Trong trang cài đặt, nhấp vào tab 'Gửi thông báo qua đám mây'.

Trình đơn Giải pháp gửi thông báo 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 ở phần tiếp theo.

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

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

gcm_sender_id được các trình duyệt này sử dụng khi đăng ký thiết bị của người dùng bằng FCM. Điều này có nghĩa 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 của bạn 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 thông báo đẩy cho họ.

Dưới đây là 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 đặt giá trị gcm_sender_id thành ID 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 (manifest.json là một tên hay), hãy tham chiếu tệp đó 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 bằng các tham số này, bạn sẽ nhận được một ngoại lệ khi cố gắng đăng ký 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

Giờ đây, khi đã thiết lập tệp kê khai, bạn có thể quay lại JavaScript của trang web của mình.

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

Phương thức này sẽ yêu cầu người dùng cấp quyền gửi nguồ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 lời hứa 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.

Điểm cuối phải được lưu trên máy chủ của bạn cho mỗi người dùng, vì sau này bạn sẽ cần những điểm cuối này để gửi thông báo đẩy.

Mã sau đây giúp người dùng đăng ký 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ụ.

Trình xử lý sự kiện đẩy Service Worker

Khi nhận được thông báo đẩy (chúng ta sẽ nói về cách thực sự gửi một thông báo đẩy trong phần tiếp theo), sự kiện đẩy sẽ được gửi trong trình chạy dịch vụ, lúc đó 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ị một 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ế cần làm nổi bật trong ví dụ này là phương thức event.waitUntil(). Phương thức này thực hiện một lời hứa và kéo dài thời gian của một trình xử lý sự kiện (hoặc có thể được coi là giúp trình xử lý dịch vụ duy trì hoạt động), cho đến khi lời hứa được giải quyết; Trong trường hợp này, lời hứa được truyề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 duy nhấ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 có cùng một thẻ, thì trình duyệt sẽ hiển thị thông báo đầu tiên và thay thế thông báo đó 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 cần thẻ nào. Chúng ta sẽ xem một ví dụ hoàn chỉnh hơn về cách hiển thị thông báo ở phần sau của bài đăng này. Bây giờ, hãy đơn giản hoá mọi thứ và xem việc gửi thông báo đẩy có hiển thị thông báo này hay không.

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

Chúng ta đã đăng ký thông báo đẩy và trình chạy dịch vụ của chúng ta đã 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ủ, điểm cuối cho FCM là đặ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

Chỉ 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à gọi điểm cuối đó theo cách chuẩn và nó 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 sẽ cần kiểm tra xem điểm cuối có phải là dành cho FCM hay không và nếu có, hãy trích xuất register_id. Để thực hiện việc này trong 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 nhận được mã đăng ký, bạn có thể gọi đến API FCM. Bạn có thể tìm tài liệu tham khảo về API FCM tại đây.

Những khía cạnh quan trọng 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 của 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 đưa vào danh sách cho phép cho dự án đó.
  • Một tiêu đề Content-Type thích hợp của 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à 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. Tuy nhiên, để kiểm tra nhanh về 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 của riêng bạn và chạy lệnh đó từ một thiết bị đầu cuối.

Bạn sẽ thấy một thông báo nổi bật:

    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 cần triển khai Giao thức đẩy web.

Nhược điểm của việc triển khai API đẩy trong Chrome hiện tại 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 quá trình triển khai sau này, dữ liệu tải trọng sẽ phải được mã hoá trên máy chủ 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ể là nhà cung cấp dịch vụ đẩy nào, cũng sẽ không thể dễ dàng xem được nội dung của thông báo đẩy. Việc này cũng giúp bảo vệ 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ủ của bạn và nhà cung cấp dịch vụ đẩy. Tuy nhiên, phương thức mã hoá này chưa được hỗ trợ. Vì vậy, trong thời gian chờ đợi, bạn sẽ cần tìm nạp để nhận được thông tin cần thiết để đ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 và theo các mẫu, thông báo đó khá kém trong việc bao quát 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 đều muốn nhận 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 đề và tin nhắn của 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ứ đều có sẵn ngay khi trình duyệt mở – ngay cả khi chưa có mạng tại thời điểm đó.

Trong mã sau, chúng tôi 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, chúng ta cần nhấn mạnh rằng event.waitUntil() thực hiện lời hứa sẽ dẫn đến lời hứa được 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 sẽ hiển thị.

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

Mở URL khi người dùng nhấp vào 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, như tập trung vào một thẻ hoặc mở cửa sổ có 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ó 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 API thông báo tại đây.

Hủy đă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ọ đang nhận được thông báo đẩy, nhưng làm cách nào để bạn có thể huỷ đăng ký cho họ?

Những 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 (để tránh gửi thông báo đẩy mà bạn biết là sẽ không nhận được). Đoạn mã dưới đây chính xác như sau:

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

Luôn 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 cho yêu cầu POST gửi qua của API FCM, tìm kết quả error:NotRegisteredcanonical_id, như đã giải thích trong tài liệu 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, từ đó kích hoạt tính năng tự động huỷ đăng ký. 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 nào và thông báo 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. Có 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 đều dẫn đến việc triển khai đầy đủ thông báo đẩy trên web trong Chrome 46. Vẫn còn các tính năng thông số kỹ thuật sẽ giúp mọi việc 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 xây dựng thông báo đẩy vào các ứ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, các lỗi sẽ xuất hiện ở một trong hai nơi: trang hoặc trình chạy dịch vụ.

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

  1. Truy cập vào chrome://inspect > Trình chạy dịch vụ. Khung hiển thị này không cung cấp nhiều thông tin khác ngoài các trình chạy dịch vụ hiện đang chạy.
  2. Hãy truy cập vào chrome://serviceworker-internals để 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 chỉ là 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ể đưa ra cho bất cứ ai mới sử dụng trình chạy dịch vụ là 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 một đ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 của 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 nằm 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 vì không có cách nào để bạn biết liệu Chrome có nhận được bất cứ điều 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. Quy trình sẽ giống như sau:

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

Hãy chú ý phản hồi của "success": 1. Nếu bạn thấy lỗi thì tức là có vấn đề gì đó với mã đăng ký FCM và thông báo đẩy chưa đượ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 là rất 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:...." chứa URL của trình chạy dịch vụ.

Ảnh chụp màn hình cho thấy vị trí của trình chạy dịch vụ trong hoạt động kiểm tra của Chrome

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

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

Tương lai của tính năng Thông báo đẩy trên Chrome và Web mở

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

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

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

Web Push Protocol là một tiêu chuẩn mới mà các 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 nhà cung cấp dịch vụ đẩy là ai. Ý tưởng là điều này giúp bạn không cần phải đăng ký các khoá API và gửi dữ liệu có định dạng đặc biệt, giống như việc bạn phải làm với FCM.

Chrome là trình duyệt đầu tiên triển khai API Push và FCM không hỗ trợ Giao thức đẩy web. Đó là lý do 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à chuyển sang 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 này 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 trong quá trình phát triển 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ố ở đâ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 thông báo trùng lặp nếu sự hiện diện trên web của tôi xuất hiện trên nhiều nguồn gốc, hoặc nếu tôi có cả sự hiện diện trên web và nguồn gốc không?

Hiện không 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.

Tình huống lý tưởng nhất 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 với mã nhận dạng của ứng dụng gốc và mã gói thuê bao trong ứng dụng web, đồng thời quyết định xem mã nào sẽ gửi thông báo đẩy đến. Bạn có thể thực hiện việc này thông qua kích thước màn hình, kiểu thiết bị, chia sẻ khoá đã 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.

Tại sao tôi cần một 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 đã 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 bị đóng, trình chạy dịch vụ của bạn 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 đang đóng.

Nếu tôi không cần gửi sự kiện trong nền thì sao?

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

Khi nào tôi có thể dùng chế độ đẩy mà không hiện thông báo (ví dụ: đẩy ở chế độ nền im lặng)?

Chưa có thời gian về thời điểm ra mắt tính năng này, nhưng có ý định triển khai tính năng đồng bộ hoá trong nền. Mặc dù chưa được quyết định hay 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 im lặng bằng tính năng đồng bộ hoá trong 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 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ụ có nguồn gốc dự định và không phải do các 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 đang hoạt động, mặc dù localhost sẽ hoạt động trong quá trình phát triển.

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

Chrome hỗ trợ trong phiên bản ổn định và Mozilla đã tiếp tục triển khai trong Firefox Nightly. Hãy xem lỗi triển khai 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, bạn không thể làm việc này nhưng chúng tôi dự định bổ sung tính năng hỗ trợ để có được danh sách các thông báo hiện có thể 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 tạo, chúng tôi muốn biết thời hạn đó. Vì vậy, vui lòng thêm nhận xét và chúng tôi sẽ gửi lại thông tin đó cho nhóm Chrome.

Nếu chỉ cần ngừng gửi một thông báo đẩy tới 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ì?

Bài đăng này trình bày một số giới hạn:

  • Việc Chrome sử dụng CCM làm dịch vụ đẩy sẽ tạo ra một số yêu cầu về quyền sở hữu riêng. Chúng tôi sẽ phối hợp cùng nhau để xem liệu có thể loại bỏ một số biện pháp 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 sẽ đưa ra cảnh báo rằng nếu Chrome không chạy, thì bạn sẽ không nhận được thông báo đẩy. Phiên bản này khác với ChromeOS và Android, trong đó hệ thống sẽ luôn nhận được thông báo đẩy.

Chúng ta không nên sử dụng Permissions API?

API quyền được triển khai trong Chrome, nhưng không nhất thiết phải có sẵn trong mọi 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 trình chạy dịch vụ kiểm soát. Bạn có thể tìm hiểu thêm tại đây.

Nếu một thông báo chưa được cập nhật vào thời điểm thiết bị của người dùng nhận được thông báo đẩy thì sao?

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 đó chỉ hữu ích trong một khoảng thời gian nhất định, bạn có thể dùng thông số "time_to_live" trên CCM để FCM sẽ không gửi thông báo đẩy nếu quá thời gian hết hạn.

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

Điều gì 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ó thông số "Thu gọn" mà bạn có thể sử dụng để yêu cầu FCM thay thế bất kỳ tin nhắn đang chờ xử lý nào có cùng giá trị "thu gọn" bằng tin nhắn mới.

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