Membuat Langganan Pengguna

Langkah pertama adalah mendapatkan izin dari pengguna untuk mengirim pesan {i>push<i} kepada mereka dan kemudian kita bisa mendapatkan PushSubscription.

JavaScript API untuk melakukan hal ini cukup mudah, jadi mari kita lanjutkan melalui aliran logika.

Deteksi fitur

Pertama, kita perlu memeriksa apakah browser saat ini benar-benar mendukung pengiriman pesan push. Kita dapat memeriksa apakah push didukung dengan dua pemeriksaan sederhana.

  1. Periksa serviceWorker pada navigator.
  2. Periksa PushManager di window.
if (!('serviceWorker' in navigator)) {
  // Service Worker isn't supported on this browser, disable or hide UI.
  return;
}

if (!('PushManager' in window)) {
  // Push isn't supported on this browser, disable or hide UI.
  return;
}

Sementara dukungan browser berkembang dengan cepat untuk pekerja layanan dan pengiriman pesan push, ada baiknya untuk melakukan deteksi fitur untuk secara progresif meningkat.

Mendaftarkan pekerja layanan

Dengan deteksi fitur, kita tahu bahwa pekerja layanan dan Push didukung. Langkah selanjutnya adalah dengan "mendaftar" pekerja layanan kita.

Ketika mendaftarkan pekerja layanan, kita memberi tahu browser letak file pekerja layanan kita. File masih berupa JavaScript, tetapi browser akan "memberikan akses" ke pekerja layanan API, termasuk push. Lebih tepatnya, browser menjalankan file di pekerja layanan lingkungan fleksibel App Engine.

Untuk mendaftarkan pekerja layanan, panggil navigator.serviceWorker.register(), dengan meneruskan jalur ke file kita. Seperti ini:

function registerServiceWorker() {
  return navigator.serviceWorker
    .register('/service-worker.js')
    .then(function (registration) {
      console.log('Service worker successfully registered.');
      return registration;
    })
    .catch(function (err) {
      console.error('Unable to register service worker.', err);
    });
}

Fungsi ini memberi tahu browser bahwa kita memiliki file pekerja layanan dan lokasinya. Di beberapa dalam hal ini, file pekerja layanan berada di /service-worker.js. Di balik layar browser akan melakukan langkah-langkah berikut setelah memanggil register():

  1. Download file pekerja layanan.

  2. Jalankan JavaScript.

  3. Jika semuanya berjalan dengan benar dan tidak ada error, promise yang ditampilkan oleh register() akan teratasi. Jika ada error apa pun, promise akan ditolak.

Jika register() menolak, periksa kembali JavaScript Anda untuk menemukan kesalahan ketik / error di Chrome DevTools.

Saat register() di-resolve, metode ini akan menampilkan ServiceWorkerRegistration. Kita akan menggunakan pendaftaran Anda untuk mengakses PushManager API.

Kompatibilitas browser PushManager API

Dukungan Browser

  • Chrome: 42.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 16.

Sumber

Meminta izin

Kita telah mendaftarkan pekerja layanan kita dan siap membuat pengguna berlangganan, langkah selanjutnya adalah mendapatkan izin akses dari pengguna untuk mengirim pesan {i>push<i} kepada mereka.

API untuk mendapatkan izin relatif sederhana, kekurangannya adalah API baru-baru ini berubah dari mengambil callback menjadi menampilkan Promise. Tujuan masalah ini, yaitu kita tidak bisa mengetahui versi API mana yang diimplementasikan oleh sehingga Anda harus menerapkan keduanya dan menangani keduanya.

function askPermission() {
  return new Promise(function (resolve, reject) {
    const permissionResult = Notification.requestPermission(function (result) {
      resolve(result);
    });

    if (permissionResult) {
      permissionResult.then(resolve, reject);
    }
  }).then(function (permissionResult) {
    if (permissionResult !== 'granted') {
      throw new Error("We weren't granted permission.");
    }
  });
}

Dalam kode di atas, cuplikan kode yang penting adalah panggilan ke Notification.requestPermission(). Metode ini akan menampilkan perintah kepada pengguna:

Permintaan izin di Chrome desktop dan seluler.

Setelah pengguna berinteraksi dengan dialog izin dengan menekan {i>Allow<i}, Blokir, atau hanya menutupnya, kita akan diberi hasilnya sebagai string: 'granted', 'default', atau 'denied'.

Pada kode contoh di atas, promise yang ditampilkan oleh askPermission() akan di-resolve jika izin diberikan, jika tidak, kita akan menampilkan error yang membuat promise ditolak.

Satu kasus ekstrem yang perlu Anda tangani adalah jika pengguna mengklik tombol 'Block' tombol. Jika ini aplikasi web Anda tidak akan bisa meminta izin dari pengguna lagi. Mereka harus "buka pemblokiran" secara manual aplikasi Anda dengan mengubah status izinnya, yang tersembunyi di panel setelan. Pikirkan baik-baik tentang bagaimana dan kapan Anda meminta izin dari pengguna, karena jika mereka mengeklik blokir, itu bukan cara mudah untuk membatalkan keputusan itu.

Kabar baiknya adalah sebagian besar pengguna dapat memberikan izin selama mereka tahu alasan permintaan izin tersebut.

Kita akan melihat bagaimana beberapa situs populer meminta izin nanti.

Menyertakan pengguna dengan PushManager

Setelah kita mendaftarkan pekerja layanan dan mendapatkan izin, kita dapat membuat pengguna berlangganan dengan memanggil registration.pushManager.subscribe().

function subscribeUserToPush() {
  return navigator.serviceWorker
    .register('/service-worker.js')
    .then(function (registration) {
      const subscribeOptions = {
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(
          'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
        ),
      };

      return registration.pushManager.subscribe(subscribeOptions);
    })
    .then(function (pushSubscription) {
      console.log(
        'Received PushSubscription: ',
        JSON.stringify(pushSubscription),
      );
      return pushSubscription;
    });
}

Saat memanggil metode subscribe(), kita meneruskan objek options, yang terdiri dari parameter wajib dan opsional.

Mari kita lihat semua opsi yang bisa kita berikan.

Opsi userVisibleOnly

Ketika push pertama kali ditambahkan ke browser, ada ketidakpastian tentang apakah developer harus dapat mengirim pesan push dan tidak menampilkan notifikasi. Ini biasanya disebut sebagai {i>push<i}, karena pengguna tidak mengetahui bahwa sesuatu telah terjadi di latar belakang.

Masalahnya, pengembang dapat melakukan hal-hal buruk seperti melacak lokasi pengguna di secara berkelanjutan tanpa disadari oleh pengguna.

Untuk menghindari skenario ini dan memberikan waktu kepada penulis spesifikasi untuk mempertimbangkan cara terbaik dalam mendukung permintaan ini fitur, opsi userVisibleOnly ditambahkan dan meneruskan nilai true adalah perjanjian dengan browser bahwa aplikasi web akan menampilkan notifikasi setiap kali push diterima (yaitu, tanpa push senyap).

Saat ini Anda harus meneruskan nilai true. Jika Anda tidak menyertakan Kunci userVisibleOnly atau meneruskan false Anda akan mendapatkan error berikut:

Chrome saat ini hanya mendukung Push API untuk langganan yang akan menghasilkan pesan yang terlihat oleh pengguna. Anda dapat menunjukkannya dengan memanggil Sebagai gantinya, pushManager.subscribe({userVisibleOnly: true}). Lihat https://goo.gl/yqv4Q4 untuk mengetahui detail selengkapnya.

Saat ini, fitur ini terlihat seperti push diam-diam yang tidak akan pernah diterapkan di Chrome. Sebagai gantinya, penulis spesifikasi sedang mempelajari gagasan API anggaran yang akan memungkinkan aplikasi web jumlah pesan push senyap berdasarkan penggunaan aplikasi web.

Opsi applicationServerKey

Kita sempat menyebutkan "kunci server aplikasi" di bagian sebelumnya. "Aplikasi kunci server" digunakan oleh layanan {i>push<i} untuk mengidentifikasi aplikasi yang membuat pengguna berlangganan dan memastikan bahwa aplikasi yang sama memberitahu pengguna tersebut.

Kunci server aplikasi adalah pasangan kunci publik dan pribadi yang unik untuk aplikasi Anda. Kunci pribadi harus dirahasiakan untuk aplikasi Anda dan kunci publik dapat dibagikan dengan bebas.

Opsi applicationServerKey yang diteruskan ke panggilan subscribe() merupakan akses publik tombol. Browser meneruskan ini ke layanan {i>push<i} ketika membuat pengguna berlangganan, yang berarti {i>push<i} layanan Anda dapat mengaitkan kunci publik aplikasi Anda ke PushSubscription pengguna.

Diagram di bawah menggambarkan langkah-langkah ini.

  1. Aplikasi web Anda dimuat di browser dan Anda memanggil subscribe(), yang meneruskan kunci server aplikasi.
  2. Browser kemudian membuat permintaan jaringan ke layanan {i>push<i} yang akan menghasilkan endpoint, mengaitkan endpoint ini dengan kunci publik aplikasi dan mengembalikan endpoint ke browser.
  3. Browser akan menambahkan endpoint ini ke PushSubscription, yang ditampilkan melalui Janji subscribe().

Ilustrasi kunci server aplikasi publik yang digunakan dalam subscribe
.

Jika nanti Anda ingin mengirim pesan push, Anda harus membuat header Authorization yang akan berisi informasi yang ditandatangani dengan kunci pribadi server aplikasi Anda. Jika layanan push menerima permintaan untuk mengirimkan pesan push, layanan ini dapat memvalidasi header Authorization yang ditandatangani ini dengan mencari kunci publik yang ditautkan ke titik akhir yang menerima permintaan. Jika tanda tangan tersebut valid, layanan push tahu bahwa layanan itu harus berasal dari server aplikasi dengan kunci pribadi yang cocok. Pada dasarnya, hal ini adalah langkah pengamanan yang mencegah orang lain mengirim email pesan ke pengguna aplikasi.

Cara kunci server aplikasi pribadi digunakan saat mengirim
pesan

Secara teknis, applicationServerKey bersifat opsional. Namun, cara termudah di Chrome memerlukannya, dan browser lain mungkin memerlukannya di masa depan. Setelan ini opsional di Firefox.

Spesifikasi yang menentukan apa kunci server aplikasi yang seharusnya spesifikasi VAPID. Setiap kali Anda membaca sesuatu yang mengacu pada "kunci server aplikasi" atau "Kunci VAPID", tetapi ingat bahwa keduanya sama.

Cara membuat kunci server aplikasi

Anda dapat membuat rangkaian kunci server aplikasi publik dan pribadi dengan mengunjungi web-push-codelab.glitch.me atau Anda dapat menggunakan class command line web-push cara membuat kunci dengan melakukan hal berikut:

    $ npm install -g web-push
    $ web-push generate-vapid-keys

Anda hanya perlu membuat kunci ini satu kali untuk aplikasi, tetapi pastikan Anda menyimpan kunci pribadi. (Ya, saya baru saja mengatakan itu.)

Izin dan subscribe()

Ada satu efek samping dari memanggil subscribe(). Jika aplikasi web Anda tidak memiliki izin untuk menampilkan notifikasi pada saat memanggil subscribe(), browser akan meminta izin untuk Anda. Hal ini berguna jika UI Anda berfungsi dengan alur ini, tetapi jika Anda ingin cara lainnya mengontrol (dan saya pikir sebagian besar developer akan melakukannya), tetap berpegang pada Notification.requestPermission() API yang kita gunakan sebelumnya.

Apa itu PushSubscription?

Kita memanggil subscribe(), meneruskan beberapa opsi, dan sebagai gantinya, kita mendapatkan promise yang di-resolve menjadi PushSubscription menghasilkan beberapa kode seperti berikut:

function subscribeUserToPush() {
  return navigator.serviceWorker
    .register('/service-worker.js')
    .then(function (registration) {
      const subscribeOptions = {
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array(
          'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
        ),
      };

      return registration.pushManager.subscribe(subscribeOptions);
    })
    .then(function (pushSubscription) {
      console.log(
        'Received PushSubscription: ',
        JSON.stringify(pushSubscription),
      );
      return pushSubscription;
    });
}

Objek PushSubscription berisi semua informasi yang diperlukan untuk mengirim push pesan ke pengguna tersebut. Jika Anda mencetak konten menggunakan JSON.stringify(), Anda akan melihat berikut ini:

    {
      "endpoint": "https://some.pushservice.com/something-unique",
      "keys": {
        "p256dh":
    "BIPUL12DLfytvTajnryr2PRdAgXS3HGKiLqndGcJGabyhHheJYlNGCeXl1dn18gSJ1WAkAPIxr4gK0_dQds4yiI=",
        "auth":"FPssNDTKnInHVndSTdbKFw=="
      }
    }

endpoint adalah URL layanan push. Untuk memicu pesan push, buat permintaan POST ke URL ini.

Objek keys berisi nilai yang digunakan untuk mengenkripsi data pesan yang dikirim dengan pesan push (yang akan kita bahas nanti di bagian ini).

Berlangganan lagi secara rutin untuk mencegah akhir masa berlaku

Saat berlangganan notifikasi push, Anda sering menerima PushSubscription.expirationTime dari null. Secara teori, hal ini berarti langganan tidak akan pernah berakhir (berbeda dengan saat Anda menerima DOMHighResTimeStamp, yang memberi tahu Anda waktu persisnya saat langganan berakhir). Namun dalam praktiknya, umumnya browser masih membiarkan masa berlaku langganan berakhir, misalnya, jika tidak ada notifikasi push yang diterima untuk waktu yang lebih lama, atau jika browser mendeteksi bahwa pengguna tidak menggunakan aplikasi yang memiliki izin notifikasi push. Salah satu pola untuk mencegah hal ini adalah dengan membuat pengguna berlangganan kembali pada setiap notifikasi yang diterima, seperti yang ditunjukkan dalam cuplikan berikut. Ini mengharuskan Anda untuk mengirim notifikasi cukup sering bagi browser agar tidak mengundurkan diri secara otomatis dari langganan, dan Anda harus mempertimbangkan dengan cermat kelebihan dan kekurangan notifikasi yang sah terhadap pengiriman spam kepada pengguna secara tidak sengaja hanya agar langganan tidak kedaluwarsa. Pada akhirnya, Anda tidak boleh mencoba melawan browser dalam upayanya untuk melindungi pengguna dari langganan notifikasi yang sudah lama terlupakan.

/* In the Service Worker. */

self.addEventListener('push', function(event) {
  console.log('Received a push message', event);

  // Display notification or handle data
  // Example: show a notification
  const title = 'New Notification';
  const body = 'You have new updates!';
  const icon = '/images/icon.png';
  const tag = 'simple-push-demo-notification-tag';

  event.waitUntil(
    self.registration.showNotification(title, {
      body: body,
      icon: icon,
      tag: tag
    })
  );

  // Attempt to resubscribe after receiving a notification
  event.waitUntil(resubscribeToPush());
});

function resubscribeToPush() {
  return self.registration.pushManager.getSubscription()
    .then(function(subscription) {
      if (subscription) {
        return subscription.unsubscribe();
      }
    })
    .then(function() {
      return self.registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY_HERE')
      });
    })
    .then(function(subscription) {
      console.log('Resubscribed to push notifications:', subscription);
      // Optionally, send new subscription details to your server
    })
    .catch(function(error) {
      console.error('Failed to resubscribe:', error);
    });
}

Mengirim langganan ke Server Anda

Setelah memiliki langganan push, Anda pasti ingin mengirimkannya ke server. Terserah Anda bagaimana lakukan itu, tetapi tip kecilnya adalah menggunakan JSON.stringify() untuk mengeluarkan semua data yang diperlukan langganan. Atau, Anda bisa membuat kumparan yang sama secara manual seperti berikut:

const subscriptionObject = {
  endpoint: pushSubscription.endpoint,
  keys: {
    p256dh: pushSubscription.getKeys('p256dh'),
    auth: pushSubscription.getKeys('auth'),
  },
};

// The above is the same output as:

const subscriptionObjectToo = JSON.stringify(pushSubscription);

Pengiriman langganan dilakukan di halaman web seperti berikut:

function sendSubscriptionToBackEnd(subscription) {
  return fetch('/api/save-subscription/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(subscription),
  })
    .then(function (response) {
      if (!response.ok) {
        throw new Error('Bad status code from server.');
      }

      return response.json();
    })
    .then(function (responseData) {
      if (!(responseData.data && responseData.data.success)) {
        throw new Error('Bad response from server.');
      }
    });
}

Server node menerima permintaan ini dan menyimpan datanya ke database untuk digunakan nanti.

app.post('/api/save-subscription/', function (req, res) {
  if (!isValidSaveRequest(req, res)) {
    return;
  }

  return saveSubscriptionToDatabase(req.body)
    .then(function (subscriptionId) {
      res.setHeader('Content-Type', 'application/json');
      res.send(JSON.stringify({data: {success: true}}));
    })
    .catch(function (err) {
      res.status(500);
      res.setHeader('Content-Type', 'application/json');
      res.send(
        JSON.stringify({
          error: {
            id: 'unable-to-save-subscription',
            message:
              'The subscription was received but we were unable to save it to our database.',
          },
        }),
      );
    });
});

Dengan detail PushSubscription di server, siap untuk mengirim pesan kapan pun kita mau.

Berlangganan lagi secara rutin untuk mencegah akhir masa berlaku

Saat berlangganan notifikasi push, Anda sering menerima PushSubscription.expirationTime dari null. Secara teori, hal ini berarti langganan tidak akan pernah berakhir (berbeda dengan saat Anda menerima DOMHighResTimeStamp, yang memberi tahu Anda waktu persisnya saat langganan berakhir). Namun dalam praktiknya, umumnya browser masih membiarkan masa berlaku langganan berakhir, misalnya, jika tidak ada notifikasi push yang diterima dalam waktu lama, atau jika browser mendeteksi bahwa pengguna tidak menggunakan aplikasi yang memiliki izin notifikasi push. Salah satu pola untuk mencegah hal ini adalah dengan membuat pengguna berlangganan kembali pada setiap notifikasi yang diterima, seperti yang ditunjukkan dalam cuplikan berikut. Ini mengharuskan Anda untuk mengirim notifikasi cukup sering bagi browser agar masa berlaku langganan tidak berakhir secara otomatis, dan Anda harus mempertimbangkan dengan cermat kelebihan dan kekurangan notifikasi yang sah terhadap permintaan spam kepada pengguna agar langganan tidak kedaluwarsa. Pada akhirnya, Anda tidak boleh mencoba melawan browser dalam upayanya untuk melindungi pengguna dari langganan notifikasi yang sudah lama terlupakan.

/* In the Service Worker. */

self.addEventListener('push', function(event) {
  console.log('Received a push message', event);

  // Display notification or handle data
  // Example: show a notification
  const title = 'New Notification';
  const body = 'You have new updates!';
  const icon = '/images/icon.png';
  const tag = 'simple-push-demo-notification-tag';

  event.waitUntil(
    self.registration.showNotification(title, {
      body: body,
      icon: icon,
      tag: tag
    })
  );

  // Attempt to resubscribe after receiving a notification
  event.waitUntil(resubscribeToPush());
});

function resubscribeToPush() {
  return self.registration.pushManager.getSubscription()
    .then(function(subscription) {
      if (subscription) {
        return subscription.unsubscribe();
      }
    })
    .then(function() {
      return self.registration.pushManager.subscribe({
        userVisibleOnly: true,
        applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY_HERE')
      });
    })
    .then(function(subscription) {
      console.log('Resubscribed to push notifications:', subscription);
      // Optionally, send new subscription details to your server
    })
    .catch(function(error) {
      console.error('Failed to resubscribe:', error);
    });
}

FAQ

Beberapa pertanyaan umum yang diajukan orang pada tahap ini:

Dapatkah saya mengubah layanan push yang digunakan browser?

Tidak. Layanan push dipilih oleh browser dan seperti yang kita lihat pada Panggilan subscribe(), browser akan membuat permintaan jaringan ke layanan push untuk mengambil detail yang membentuk PushSubscription.

Setiap browser menggunakan Layanan Push yang berbeda, bukankah API-nya berbeda?

Semua layanan push akan mengharapkan API yang sama.

API umum ini disebut Protokol Push Web dan menjelaskan permintaan jaringan yang harus dibuat aplikasi Anda untuk memicu pesan push.

Jika saya membuat pengguna berlangganan melalui desktop, apakah mereka juga berlangganan di ponsel?

Sayangnya tidak. Pengguna harus mendaftar untuk push di setiap browser yang ingin menerima pesan. Perlu juga dicatat bahwa ini akan membutuhkan pengguna yang memberikan izin akses di setiap perangkat.

Langkah berikutnya

Lab kode