Mengirim pesan dengan library web push

Salah satu poin masalah ketika bekerja dengan {i>web push<i} adalah bahwa memicu pesan {i>push<i} sangat "fiddly". Untuk memicu pesan push, aplikasi perlu membuat permintaan POST ke push layanan setelah push web . Untuk menggunakan push di semua browser, Anda harus menggunakan VAPID (alias kunci server aplikasi) yang pada dasarnya memerlukan penyetelan header dengan nilai yang membuktikan aplikasi Anda bisa mengirimkan pesan kepada pengguna. Untuk mengirim data dengan pesan {i>push<i}, data harus terenkripsi dan header tertentu perlu ditambahkan agar {i>browser<i} dapat membongkar enkripsi pesan dengan benar.

Masalah utama yang memicu {i>push<i} adalah jika Anda menghadapi masalah, sulit untuk didiagnosis menyelesaikan masalah. Hal ini meningkat seiring waktu dan dukungan browser yang lebih luas, tetapi hal ini tidak mudah. Sebagai alasan ini, saya sangat menyarankan untuk menggunakan perpustakaan untuk menangani enkripsi, pemformatan, memicu pesan push Anda.

Jika Anda benar-benar ingin belajar tentang apa yang dilakukan {i>library<i}, kami akan membahasnya di bagian berikutnya. Untuk saat ini, kita akan melihat cara mengelola langganan dan menggunakan library web push yang ada untuk membuat permintaan push.

Di bagian ini, kita akan menggunakan node web-push library. Bahasa lain akan memiliki perbedaan, tetapi mereka tidak akan terlalu berbeda. Kita sedang melihat Node karena itu adalah JavaScript dan seharusnya paling mudah diakses oleh pembaca.

Kita akan melakukan langkah-langkah berikut:

  1. Kirim langganan ke backend dan simpan langganan tersebut.
  2. Mengambil langganan yang tersimpan dan memicu pesan push.

Menyimpan langganan

Menyimpan dan membuat kueri PushSubscription dari database akan bervariasi bergantung pada pilihan bahasa sisi server dan {i>database<i}, tetapi mungkin berguna untuk melihat contoh bagaimana hal itu bisa dilakukan.

Di halaman web demo, PushSubscription dikirim ke backend kami dengan membuat permintaan POST sederhana:

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 Express dalam demo kami memiliki pemroses permintaan yang cocok untuk Endpoint /api/save-subscription/:

app.post('/api/save-subscription/', function (req, res) {

Dalam rute ini, kami memvalidasi langganan hanya untuk memastikan permintaan tidak dipenuhi dan tidak sampah:

const isValidSaveRequest = (req, res) => {
  // Check the request body has at least an endpoint.
  if (!req.body || !req.body.endpoint) {
    // Not a valid subscription.
    res.status(400);
    res.setHeader('Content-Type', 'application/json');
    res.send(
      JSON.stringify({
        error: {
          id: 'no-endpoint',
          message: 'Subscription must have an endpoint.',
        },
      }),
    );
    return false;
  }
  return true;
};

Jika langganan valid, kita harus menyimpannya dan mengembalikan langganan yang sesuai Respons JSON:

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

Demo ini menggunakan nedb untuk menyimpan langganan, yaitu {i>database<i} berbasis file sederhana, tetapi Anda dapat menggunakan {i>database<i} pilihan Anda. Kita hanya menggunakan ini sebagai tidak perlu pengaturan apa pun. Untuk produksi, Anda ingin menggunakan sesuatu yang lebih dapat diandalkan. (Saya cenderung tetap menggunakan MySQL lama yang bagus.)

function saveSubscriptionToDatabase(subscription) {
  return new Promise(function (resolve, reject) {
    db.insert(subscription, function (err, newDoc) {
      if (err) {
        reject(err);
        return;
      }

      resolve(newDoc._id);
    });
  });
}

Mengirim pesan push

Dalam hal mengirim pesan push, pada akhirnya kita memerlukan beberapa peristiwa untuk memicu proses mengirim pesan ke pengguna. Pendekatan umumnya adalah membuat halaman admin yang memungkinkan Anda mengonfigurasi dan memicu pesan push. Tetapi Anda dapat membuat program untuk dijalankan secara lokal atau pendekatan lain yang memungkinkan pengaksesan daftar PushSubscription dan menjalankan kode untuk memicu pesan push.

Demo kami memiliki "seperti admin" yang memungkinkan Anda memicu push. Karena ini hanya demo, ini adalah publik.

Saya akan menjelaskan setiap langkah agar demo berfungsi. Ini akan bayi langkah-langkah sehingga semua orang dapat mengikuti, termasuk siapa saja yang baru menggunakan Node.

Saat membahas cara membuat pengguna berlangganan, kita telah membahas cara menambahkan applicationServerKey ke subscribe() opsi. Ada di backend di mana kita akan memerlukan kunci pribadi ini.

Dalam demo, nilai-nilai ini ditambahkan ke aplikasi Node seperti itu (kode yang membosankan, tapi hanya ingin Anda tahu bahwa tidak ada sihir):

const vapidKeys = {
  publicKey:
    'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
  privateKey: 'UUxI4O8-FbRouAevSmBQ6o18hgE4nSG3qwvJTfKc-ls',
};

Selanjutnya, kita perlu menginstal modul web-push untuk server Node:

npm install web-push --save

Kemudian, dalam skrip Node kita memerlukan modul web-push seperti ini:

const webpush = require('web-push');

Sekarang kita dapat mulai menggunakan modul web-push. Pertama-tama, kita perlu memberi tahu modul web-push tentang kunci server aplikasi kita. (Ingat, kunci ini juga dikenal sebagai kunci VAPID karena itu adalah namanya spesifikasi.)

const vapidKeys = {
  publicKey:
    'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
  privateKey: 'UUxI4O8-FbRouAevSmBQ6o18hgE4nSG3qwvJTfKc-ls',
};

webpush.setVapidDetails(
  'mailto:web-push-book@gauntface.com',
  vapidKeys.publicKey,
  vapidKeys.privateKey,
);

Perhatikan bahwa kami juga menyertakan "mailto:" {i>string<i}. String ini harus berupa URL atau mailto alamat email Anda. Potongan informasi ini sebenarnya akan dikirim ke layanan {i>web push<i} sebagai bagian dari permintaan untuk memicu push. Alasan ini dilakukan adalah agar jika layanan {i>web push <i}perlu untuk menghubungi pengirim, mereka memiliki beberapa informasi yang akan memungkinkan mereka.

Dengan demikian, modul web-push siap digunakan, langkah berikutnya adalah memicu pesan push.

Demo ini menggunakan panel admin fiktif untuk memicu pesan push.

Screenshot halaman admin.

Mengklik "Picu Pesan Push" akan membuat permintaan POST ke /api/trigger-push-msg/, yang merupakan sinyal bagi backend untuk mengirim pesan {i>push<i}, jadi kita membuat rute di ekspres untuk endpoint ini:

app.post('/api/trigger-push-msg/', function (req, res) {

Ketika permintaan ini diterima, kita mengambil langganan dari {i>database<i} dan untuk setiap instance, kita memicu pesan push.

return getSubscriptionsFromDatabase().then(function (subscriptions) {
  let promiseChain = Promise.resolve();

  for (let i = 0; i < subscriptions.length; i++) {
    const subscription = subscriptions[i];
    promiseChain = promiseChain.then(() => {
      return triggerPushMsg(subscription, dataToSend);
    });
  }

  return promiseChain;
});

Fungsi triggerPushMsg() kemudian dapat menggunakan library web-push untuk mengirim pesan ke langganan yang disediakan.

const triggerPushMsg = function (subscription, dataToSend) {
  return webpush.sendNotification(subscription, dataToSend).catch((err) => {
    if (err.statusCode === 404 || err.statusCode === 410) {
      console.log('Subscription has expired or is no longer valid: ', err);
      return deleteSubscriptionFromDatabase(subscription._id);
    } else {
      throw err;
    }
  });
};

Panggilan ke webpush.sendNotification() akan menampilkan promise. Jika pesan berhasil dikirim, promise akan diselesaikan dan ada tidak ada yang perlu kita lakukan. Jika promise ditolak, Anda perlu memeriksa error karena akan memberi tahu Anda apakah PushSubscription masih valid atau tidak.

Untuk menentukan jenis error dari layanan push, sebaiknya lihat kode status. {i>Error<i} pesan bervariasi antara layanan push dan beberapa layanan lebih membantu dibandingkan yang lain.

Dalam contoh ini, pemeriksaan memeriksa kode status 404 dan 410, yang merupakan kode status HTTP untuk 'Tidak Ditemukan' dan 'Hilang'. Jika kami menerima salah satu dari langganan ini, berarti langganan telah berakhir atau tidak lagi valid. Dalam skenario ini, kita perlu menghapus langganan dari database.

Dalam kasus error lain, kita hanya perlu throw err, yang akan membuat promise ditampilkan oleh triggerPushMsg() menolak.

Kita akan membahas beberapa kode status lainnya di bagian berikutnya saat melihat push web protokol secara lebih rinci.

Setelah melakukan loop langganan, kita perlu menampilkan respons JSON.

.then(() => {
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-send-messages',
    message: `We were unable to send messages to all subscriptions : ` +
        `'${err.message}'`
    }
}));
});

Kita telah membahas langkah-langkah penerapan utama:

  1. Buat API untuk mengirim langganan dari halaman web ke backend kami sehingga bisa menyimpannya ke dalam {i>database<i}.
  2. Buat API untuk memicu pengiriman pesan push (dalam hal ini, API yang dipanggil dari panel admin yang meniru).
  3. Mengambil semua langganan dari backend dan mengirim pesan ke setiap langganan dengan salah satu web-push library.

Terlepas dari backend Anda (Node, PHP, Python, ...), langkah-langkah untuk mengimplementasikan push akan berjalan dengan baik sama.

Selanjutnya, apa sebenarnya yang dilakukan library web-push ini untuk kita?

Langkah berikutnya

Lab kode