การส่งข้อความด้วยไลบรารีพุชจากเว็บ

ปัญหาอย่างหนึ่งเมื่อใช้ Web Push คือการเปิดใช้งานข้อความ Push นั้น "ยุ่งยาก" มาก หากต้องการทริกเกอร์ข้อความพุช แอปพลิเคชันต้องส่งคำขอ POST ไปยังบริการพุชตามโปรโตคอลพุชจากเว็บ หากต้องการใช้พุชในเบราว์เซอร์ทั้งหมด คุณต้องใช้ VAPID (หรือที่เรียกว่าคีย์แอปพลิเคชันเซิร์ฟเวอร์) ซึ่งโดยพื้นฐานแล้วจะต้องตั้งค่าส่วนหัวที่มีค่าที่พิสูจน์ว่าแอปพลิเคชันของคุณส่งข้อความถึงผู้ใช้ได้ หากต้องการส่งข้อมูลด้วยข้อความพุช ข้อมูลจะต้องเข้ารหัสและต้องเพิ่มส่วนหัวที่เฉพาะเจาะจงเพื่อให้เบราว์เซอร์ถอดรหัสข้อความได้อย่างถูกต้อง

ปัญหาหลักของการทริกเกอร์ Push คือหากพบปัญหา คุณจะวินิจฉัยปัญหาได้ยาก การปรับปรุงนี้ได้รับการปรับปรุงให้ดีขึ้นโดยใช้เวลาและการรองรับเบราว์เซอร์ที่กว้างขึ้น แต่ก็ไม่ใช่เรื่องง่าย เราจึงขอแนะนำอย่างยิ่งให้ใช้ไลบรารีเพื่อจัดการการเข้ารหัส การจัดรูปแบบ และการเรียกใช้ข้อความ Push

หากต้องการทราบสิ่งที่ไลบรารีทํา เราจะอธิบายในส่วนถัดไป ในตอนนี้จะมาดูการจัดการการติดตามและใช้คลังเว็บ Push ที่มีอยู่เพื่อส่งคําขอ Push

ในส่วนนี้ เราจะใช้ web-push Node library ภาษาอื่นๆ จะมีความแตกต่างกันบ้าง แต่ก็จะไม่แตกต่างกันมากนัก เราพิจารณาใช้โหนดเพราะเป็น JavaScript และควรให้ผู้อ่านเข้าถึงโหนดได้มากที่สุด

เราจะทำตามขั้นตอนต่อไปนี้

  1. ส่งการสมัครใช้บริการไปยังแบ็กเอนด์ของเราและบันทึก
  2. เรียกข้อมูลการติดตามที่บันทึกไว้และทริกเกอร์ข้อความ Push

บันทึกการติดตาม

การบันทึกและค้นหาฐานข้อมูล PushSubscription จากฐานข้อมูลจะแตกต่างกันไปตามภาษาและฐานข้อมูลฝั่งเซิร์ฟเวอร์ของคุณ แต่การดูตัวอย่างว่าควรทำอย่างไรก็อาจมีประโยชน์

ในหน้าเว็บเดโม ระบบจะส่ง PushSubscription ไปยังแบ็กเอนด์ของเราด้วยการทำคำขอ POST ง่ายๆ ดังนี้

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

เซิร์ฟเวอร์ Express ในการแสดงตัวอย่างของเรามีโปรแกรมรับฟังคําขอที่ตรงกันสําหรับปลายทาง /api/save-subscription/ ดังนี้

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

ในเส้นทางนี้ เราจะตรวจสอบการสมัครรับข้อมูลเพื่อให้แน่ใจว่าคำขอนั้นปลอดภัยและเต็มไปด้วยขยะ

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

หากการสมัครใช้บริการถูกต้อง เราจะต้องบันทึกการสมัครใช้บริการและแสดงผลการตอบกลับ 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.',
        },
      }),
    );
  });

การสาธิตนี้ใช้ nedb ในการจัดเก็บการติดตาม และเป็นฐานข้อมูลที่อิงตามไฟล์เรียบง่าย แต่คุณสามารถใช้ฐานข้อมูลใดก็ได้ตามต้องการ เราใช้วิธีนี้เนื่องจากไม่ต้องตั้งค่าใดๆ สำหรับเวอร์ชันที่ใช้งานจริง ควรใช้ชื่อที่ไว้วางใจได้มากขึ้น (ผมก็มักจะใช้ MySQL แบบเดิมๆ)

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

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

การส่งข้อความ Push

เมื่อพูดถึงการส่งข้อความพุช ท้ายที่สุดแล้ว เราจำเป็นต้องมีเหตุการณ์บางอย่างเพื่อทริกเกอร์กระบวนการส่งข้อความไปยังผู้ใช้ แนวทางที่ใช้โดยทั่วไปคือการสร้างหน้าผู้ดูแลระบบที่ให้คุณกำหนดค่าและทริกเกอร์ข้อความพุช แต่คุณสามารถสร้างโปรแกรมเพื่อเรียกใช้ในเครื่องหรือวิธีอื่นที่อนุญาตให้เข้าถึงรายการของ PushSubscription และเรียกใช้โค้ดเพื่อทริกเกอร์ข้อความพุช

เดโมของเรามีหน้า "ผู้ดูแลระบบ" ที่ช่วยให้คุณทริกเกอร์ Push ได้ เนื่องจากเป็นเพียงการสาธิต จึงถือเป็นหน้าสาธารณะ

เราจะอธิบายแต่ละขั้นตอนที่เกี่ยวข้องกับการทำให้เดโมใช้งานได้ ขั้นตอนเหล่านี้จะเป็นขั้นตอนเริ่มต้นเพื่อให้ทุกคนทำตามได้ รวมถึงผู้ที่เพิ่งเริ่มใช้ Node

เมื่อเราพูดถึงการติดตามผู้ใช้ เราได้พูดถึงการเพิ่ม applicationServerKey ลงในตัวเลือก subscribe() เราต้องใช้คีย์ส่วนตัวนี้ในแบ็กเอนด์

ในการสาธิต จะมีการเพิ่มค่าเหล่านี้ลงในแอปโหนด (ฉันรู้จักโค้ดที่น่าเบื่อ แต่แค่อยากให้คุณรู้ว่าไม่มีเวทมนตร์อะไรเลย)

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

ต่อไปเราต้องติดตั้งโมดูล web-push สําหรับเซิร์ฟเวอร์ Node

npm install web-push --save

จากนั้นในสคริปต์ Node เราต้องใช้โมดูล web-push ดังนี้

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

ตอนนี้เราเริ่มใช้โมดูล web-push ได้แล้ว ก่อนอื่นเราต้องบอกโมดูล web-push เกี่ยวกับกุญแจเซิร์ฟเวอร์แอปพลิเคชันของเรา (โปรดทราบว่าคีย์เหล่านี้เรียกอีกอย่างว่าคีย์ VAPID เนื่องจากเป็นชื่อของข้อกำหนด)

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

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

โปรดทราบว่าเราได้ใส่สตริง "mailto:" ด้วย สตริงนี้ต้องเป็น URL หรืออีเมลรูปแบบ mailto ระบบจะส่งข้อมูลนี้ไปยังบริการพุชบนเว็บจริงๆ โดยเป็นส่วนหนึ่งของคำขอทริกเกอร์การพุช เหตุผลที่ดำเนินการเช่นนี้ก็เพื่อให้บริการ Push บนเว็บมีข้อมูลบางอย่างที่จะช่วยให้ติดต่อผู้ส่งได้หากจำเป็น

เท่านี้โมดูล web-push ก็พร้อมใช้งานแล้ว ขั้นตอนถัดไปคือการทริกเกอร์ข้อความ Push

การสาธิตนี้ใช้แผงการดูแลระบบจำลองเพื่อทริกเกอร์ข้อความ Push

ภาพหน้าจอของหน้าผู้ดูแลระบบ

การคลิกปุ่ม "ทริกเกอร์ข้อความ Push" จะส่งคําขอ POST ไปยัง /api/trigger-push-msg/ ซึ่งเป็นสัญญาณให้แบ็กเอนด์ส่งข้อความ Push เราจึงสร้างเส้นทางใน Express สําหรับปลายทางนี้

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

เมื่อได้รับคําขอนี้ เราจะดึงข้อมูลการติดตามจากฐานข้อมูลและเรียกใช้ข้อความ 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;
});

จากนั้นฟังก์ชัน triggerPushMsg() จะใช้ไลบรารี Web Push เพื่อส่งข้อความไปยังการสมัครใช้บริการที่ระบุ

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

การเรียกใช้ webpush.sendNotification() จะแสดงผลลัพธ์เป็นสัญญา หากส่งข้อความสำเร็จ สัญญาจะได้รับการแก้ไขและเราไม่จำเป็นต้องดำเนินการใดๆ หากสัญญาถูกปฏิเสธ คุณจะต้องตรวจสอบข้อผิดพลาด เนื่องจากข้อผิดพลาดจะแจ้งให้ทราบว่า PushSubscription ยังคงใช้งานได้หรือไม่

หากต้องการทราบประเภทของข้อผิดพลาดจากบริการพุช ทางที่ดีคุณควรดูที่รหัสสถานะ ข้อความแสดงข้อผิดพลาดจะแตกต่างกันไปตามบริการ Push และบางข้อความจะมีประโยชน์มากกว่าข้อความอื่นๆ

ในตัวอย่างนี้ ระบบจะตรวจสอบรหัสสถานะ 404 และ 410 ซึ่งเป็นรหัสสถานะ HTTP สําหรับ "ไม่พบ" และ "ไม่มีแล้ว" หากเราได้รับข้อเสนออย่างใดอย่างหนึ่ง หมายความว่าการสมัครใช้บริการนั้นหมดอายุหรือใช้งานไม่ได้อีกต่อไป ในกรณีเหล่านี้ เราจำเป็นต้องนำการติดตามออกจากฐานข้อมูล

ในกรณีที่เกิดข้อผิดพลาดอื่นๆ เราจะแค่ throw err ซึ่งจะทำให้ Promise ที่ triggerPushMsg() แสดงผลปฏิเสธ

เราจะกล่าวถึงรหัสสถานะอื่นๆ บางส่วนในส่วนถัดไปเมื่อพูดถึงโปรโตคอล Push บนเว็บอย่างละเอียด

หลังจากวนดูการติดตามแล้ว เราจะต้องแสดงผลการตอบกลับ 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}'`
    }
}));
});

เราได้อธิบายขั้นตอนการติดตั้งใช้งานหลักๆ ดังนี้

  1. สร้าง API เพื่อส่งการสมัครใช้บริการจากหน้าเว็บไปยังแบ็กเอนด์ของเราเพื่อให้บันทึกการสมัครใช้บริการเหล่านั้นลงในฐานข้อมูลได้
  2. สร้าง API เพื่อทริกเกอร์การส่งข้อความ Push (ในกรณีนี้คือ API ที่เรียกใช้จากแผงการดูแลระบบจำลอง)
  3. ดึงข้อมูลการสมัครใช้บริการทั้งหมดจากแบ็กเอนด์ของเราและส่งข้อความถึงการสมัครใช้บริการแต่ละรายการด้วยไลบรารี Web-push แบบใดแบบหนึ่ง

ขั้นตอนการติดตั้งใช้งาน Push จะเหมือนกันไม่ว่าแบ็กเอนด์ของคุณจะเป็น Node, PHP, Python หรืออื่นๆ

ต่อไป ไลบรารีพุชจากเว็บเหล่านี้ทำอะไรให้เราได้บ้าง

สถานที่ที่จะไปต่อ

Codelabs