Web push notification

Notification là những thông báo từ những application được hiển thị trên các thiết bị của người dùng(Phone, Browser) ngay cả khi application đó không được chạy. Bài viết này chúng ta sẽ tìm hiểu cơ chế push notification trên các trình duyệt (Push notification on the web) 🌎 .

Push Notifications on the web

Push notification giúp các application trên browsers tăng tương tác với user hơn, từ những việc như thông báo tới user những thông báo quan trọng, chuyển hướng user tới site nếu notification được click, hoặc những notification popup được thêm những action button giúp user có thể thao tác ngay trên đó mà không cần chuyến hướng tới website.

Concepts

  • Notifications API: API được sử dụng để hiển thị các notification tới phía người dùng
  • Push API: hỗ trợ các phương thức đăng ký app với browser push service và nhận các push messages trong service worker
  • Push service: Một hệ thống có nhiệm vụ chuyển hướng các push messages từ server tới các clients(browsers) thông qua Web Push Protocol. Mỗi browser đều có hỗ trợ push service của riêng nó.
  • Service worker: Ở bài post trước mình đã nói về service worker, nhiệm vụ của service worker ở đây là được chạy ở background, xử lý các push messages nhận được từ server.

Flow

Để thực hiện push notification trên browser, ta cần phải yêu cầu quyền từ phía user(cung cấp bởi Notification API):

Notification.requestPermission(function(status) {
    console.log('Notification permission status:', status);
});

Sau khi trang web được grant permission, ảnh trên minh họa luồng thực hiện cho push notification:

  • Trước tiên, trang web cần register Service worker
  • Nếu register thành công, browser sẽ trả về một ServiceWorkerRegistration object.
  • Tiếp đó, webpage cần phải subscribe tới browser push service:
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('sw.js').then(function(reg) {
    console.log('Service Worker Registered!', reg);

    reg.pushManager.getSubscription().then(function(sub) {
        console.log('Subscription object: ', sub.endpoint);
      }
    });
  })
   .catch(function(err) {
    console.log('Service Worker registration failed: ', err);
  });
}

1 subcription object được trả về có dạng:

{
"endpoint":"https://fcm.googleapis.com/fcm/send/dpH5lCsTSSM:APA91bHqjZxM0VImWWqDRN7U0a3AycjUf4O-byuxb_wJsKRaKvV_iKw56s16ekq6FUqoCF7k2nICUpd8fHPxVTgqLunFeVeB9lLCQZyohyAztTH8ZQL9WCxKpA6dvTG_TUIhQUFq_n",
"keys": {
    "p256dh":"BLQELIDm-6b9Bl07YrEuXJ4BL_YBVQ0dvt9NQGGJxIQidJWHPNa9YrouvcQ9d7_MqzvGS9Alz60SZNCG3qfpk=",
    "auth":"4vQK-SvRAN5eo-8ASlrwA=="
    }
}

endpoint URL trả về sẽ khác nhau với mỗi browser, là định danh của browser đó, được sử dụng cho việc route tới đúng browser khi gửi push message.

Khi push message được gửi đi từ server, nó sẽ được gửi tới endpoint URL này, được encrypted với keys (trong subcription object) và được push service gửi đúng tới browser thông qua định danh ở endpoint URL:

service worker xử lý các push event mỗi khi có push message tới, hiển thị tới user.

FCM push notification

Register service worker

Đầu tiên cần phải register service worker cho trang web của chúng ta với browser:

if ('serviceWorker' in navigator) {
   navigator.serviceWorker.register('./firebase-messaging-sw.js');
}

File firebase-messagin-sw.js phải đặt setting ở ngoài thư mục root trên server. Trong Rails có thể đặt trong thư mục public.

Set up firebase

Cần set up firebase ở code javascript của webpage. Ở đây mình xử lý trong Rails.

  • Cài đặt firebase library qua yarn:
$ yarn add firebase
  • Init firebase:
// app/javascripts/packs/application.js
import * as firebase from 'firebase/app';
import 'firebase/messaging';

// init firebase
var firebaseConfig = {
  apiKey: 'xxxxxxxxxxxxxxxxxxxxxxx,
  authDomain: 'app_name.firebaseapp.com',
  databaseURL: 'https://app_name.firebaseio.com',
  projectId: 'app_name',
  storageBucket: 'app_name.appspot.com',
  messagingSenderId: "111111111111",
  appId: "1:11111111111:web:d34d1206a3553c2b92f648",
  measurementId: "XXXXXXXXXXXXX"
};
firebase.initializeApp(firebaseConfig);

const messaging = firebase.messaging();

Phần firebaseConfig bạn có thể xem tại đây.

  • Get FCM token:
// Yêu cầu permission từ user, sau đó lấy log ra token
messaging.requestPermission().then(function() {
  return messaging.getToken();
}).then(function(token) {
  console.log(token);
})

token ở đây là chuỗi token trong endpoint URL format ở trên, có nhiệm vụ định danh browser hiện tại.

"endpoint":"https://fcm.googleapis.com/fcm/send/dpH5lCsTSSM:APA91bHqjZxM0VImWWqDRN7U0a3AycjUf4O-byuxb_wJsKRaKvV_iKw56s16ekq6FUqoCF7k2nICUpd8fHPxVTgqLunFeVeB9lLCQZyohyAztTH8ZQL9WCxKpA6dvTG_TUIhQUFq_n"

Sau khi log ra token, ta sẽ gửi token này về server, sau đó server sẽ khởi tạo message muốn gửi và gửi push message này tới firebase.

Ở đây mình lấy token được log ra và gửi luôn tới firebase.

  • Server setup Phía server cần tương tác để gửi push message tới firebase. Trong Rails mình sẽ dùng gem fcm (hoặc bạn có thể test trực tiếp qua REST API)
gem 'fcm'

firebase cần FCM_SERVER_KEY để xác định application của bạn, có thể lấy tại đây (Project credentials => Server key) tạo FCM_COMPOSER tiến hành gửi message:

FCM_COMPOSER = FCM.new(ENV['FCM_SERVER_KEY'])

tokens = [
"eXOGKT9WlA7u-OlZyRb_9U:APA91bFYctmLOiwTYfFR-UGMmx7CeOChxjzEBA4klZcgrAtJDGDSHPU8_6LAhgqq4LPA5zkH_7clNQ7K3Oti87G0izuln7S-RImOrgnK16DrIW7aJteBP3X5uEz9FeNk8a_2jRVKXu5-"
]

message = {
    "notification": {
        "title": "Hello world"
    },
    "data": {
        "url": "https://viblo.asia/p/service-worker-Az45b4bOZxY"
    }
}

FCM_COMPOSER.send(tokens, message)
  • Receive message: Ta cần xử lý hai trường hợp khi nhận message: Ở foregroundbackground.

foreground(khi user đang truy cập webpage), ta có thể nhận được message data và sử dụng Notification API để hiển thị popup notification tới user, thông qua hàm callback:

messaging.onMessage((payload) => {
      console.log('Message received. ', payload);

      // Notification API hiển thị noti
});

Khi user trong trạng thái background(bật tab khác hoặc app khác), message tới được xử lý ở service worker qua callback:

// ./firebase-messageing-sw.js
// version firebase ở đây phải đúng với firebase ở yarn
importScripts('https://www.gstatic.com/firebasejs/7.16.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/7.16.0/firebase-messaging.js');

// init firebase
var firebaseConfig = {
  apiKey: 'xxxxxxxxxxxxxxxxxxxxxxx,
  authDomain: 'app_name.firebaseapp.com',
  databaseURL: 'https://app_name.firebaseio.com',
  projectId: 'app_name',
  storageBucket: 'app_name.appspot.com',
  messagingSenderId: "111111111111",
  appId: "1:11111111111:web:d34d1206a3553c2b92f648",
  measurementId: "XXXXXXXXXXXXX"
};
firebase.initializeApp(firebaseConfig);

const messaging = firebase.messaging();

messaging.setBackgroundMessageHandler(function(payload) {
    console.log("Message: " + payload.data.url);
    console.log("Message: " + payload.notification.title);

  return self.registration.showNotification("Hello", { body: "world" });
})

Lưu ý, khi handle message ở background(service worker), nếu message gửi tới có chứa trường notification thì browser sẽ hiện thì popup thông báo mặc định của nó mà không qua hàm setBackgroundMessageHandler, còn lại nếu message chỉ chứa data thì sẽ xử lý bình thường.


Trên đây là tìm hiểu của mình về web push notification. Cảm ơn mọi người đã theo dõi! 💬


All Rights Reserved