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

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

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

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

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

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

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

กำลังบันทึกการสมัครใช้บริการ

การบันทึกและค้นหาของ 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 ในการสาธิตมี Listener คำขอที่ตรงกันสำหรับ /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);
    });
  });
}

การส่งข้อความพุช

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

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

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

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

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

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

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

npm install web-push --save

จากนั้นในสคริปต์โหนด เราต้องการโมดูล 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 อีเมล ข้อมูลนี้จะถูกส่งไปยังบริการพุชบนเว็บโดยเป็นส่วนหนึ่งของ คำขอเพื่อเริ่มการพุช สาเหตุที่เป็นเช่นนั้นก็เพราะถ้าบริการพุชจากเว็บต้องการ เพื่อติดต่อกับผู้ส่งได้ พวกเขามีข้อมูลที่จะช่วยได้

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

เดโมใช้แผงจำลองของผู้ดูแลระบบเพื่อทริกเกอร์ข้อความพุช

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

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

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

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

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() จะสามารถใช้ไลบรารีการพุชจากเว็บเพื่อส่งข้อความไปยัง ที่มีให้

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 ยังคง ถูกต้องหรือไม่

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

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

ในกรณีที่มีข้อผิดพลาดอื่นๆ เราจะเหลือเพียง throw err ซึ่งจะทำให้คำมั่นสัญญาที่ส่งคืนโดย ปฏิเสธ 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 เพื่อทริกเกอร์การส่งข้อความพุช (ในกรณีนี้คือ มีการเรียก API จากแผงการดูแลระบบสมมติ)
  3. ดึงข้อมูลการสมัครใช้บริการทั้งหมดจากแบ็กเอนด์ของเรา และส่งข้อความถึงการสมัครรับข้อมูลแต่ละรายการด้วย ห้องสมุด

ไม่ว่าแบ็กเอนด์จะเป็นแบบใดก็ตาม (โหนด, PHP, Python, ...) ขั้นตอนในการใช้งานการพุชมีดังนี้ เหมือนกัน

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

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

Code Lab