การสมัครรับข้อมูลจากผู้ใช้

ขั้นตอนแรกคือขออนุญาตจากผู้ใช้ในการส่งข้อความ Push ถึงผู้ใช้ แล้วเราจะติดต่อ PushSubscription ให้

สำหรับ JavaScript API ในการดำเนินการนี้จะตรงไปตรงมาพอสมควร ดังนั้นเรามาดูขั้นตอนลอจิกกันบ้าง

การตรวจหาฟีเจอร์

ก่อนอื่น เราต้องตรวจสอบว่าเบราว์เซอร์ปัจจุบันสนับสนุนการรับส่งข้อความพุชหรือไม่ เราตรวจสอบได้ว่าระบบรองรับการพุชดังกล่าวหรือไม่ด้วยการตรวจสอบง่ายๆ 2 อย่าง

  1. ตรวจหา serviceWorker ในตัวนำทาง
  2. ตรวจหา PushManager ในหน้าต่าง
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;
}

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

ลงทะเบียน Service Worker

การตรวจพบฟีเจอร์นี้ทำให้เราทราบว่าระบบรองรับทั้ง Service Worker และพุช ขั้นตอนต่อไปคือการ "ลงทะเบียน" Service Worker

เมื่อเราลงทะเบียน Service Worker เราจะบอกเบราว์เซอร์ว่าไฟล์ Service Worker อยู่ที่ไหน ไฟล์ยังคงเป็นเพียง JavaScript แต่เบราว์เซอร์จะ "ให้สิทธิ์เข้าถึง" แก่โปรแกรมทำงานของบริการ API ซึ่งรวมถึงพุช พูดให้ชัดเจนยิ่งขึ้นคือ เบราว์เซอร์จะเรียกใช้ไฟล์ในสภาพแวดล้อมของโปรแกรมทำงานของบริการ

หากต้องการลงทะเบียน Service Worker ให้เรียก navigator.serviceWorker.register() ซึ่งส่งผ่านเส้นทางไปยังไฟล์ของเรา ดังนี้

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

ฟังก์ชันนี้จะบอกเบราว์เซอร์ว่าเรามีไฟล์ Service Worker และตำแหน่งของไฟล์ ในกรณีนี้ ไฟล์ Service Worker จะอยู่ที่ /service-worker.js ในเบื้องหลัง เบราว์เซอร์จะทำตามขั้นตอนต่อไปนี้หลังจากเรียกใช้ register()

  1. ดาวน์โหลดไฟล์ Service Worker

  2. เรียกใช้ JavaScript

  3. หากทุกอย่างทำงานได้อย่างถูกต้องและไม่มีข้อผิดพลาด คำสัญญาที่ register() ส่งกลับมาจะได้รับการแก้ไข หากมีข้อผิดพลาดประเภทใดก็ตาม คำสัญญาจะปฏิเสธ

หาก register() ปฏิเสธ ให้ตรวจสอบ JavaScript อีกครั้งว่าสะกดผิด / มีข้อผิดพลาดในเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome

เมื่อ register() แก้ไขแล้ว ระบบจะแสดงผล ServiceWorkerRegistration เราจะใช้การลงทะเบียนนี้เพื่อเข้าถึง PushManager API

ความเข้ากันได้ของเบราว์เซอร์ PushManager API

การสนับสนุนเบราว์เซอร์

  • 42
  • 17
  • 44
  • 16

แหล่งที่มา

กำลังขอสิทธิ์

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

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

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

ในโค้ดข้างต้น ข้อมูลโค้ดที่สำคัญคือการเรียกไปยัง Notification.requestPermission() วิธีนี้จะแสดงข้อความแจ้งแก่ผู้ใช้

ข้อความแจ้งเกี่ยวกับสิทธิ์ใน Chrome บนเดสก์ท็อปและอุปกรณ์เคลื่อนที่

เมื่อผู้ใช้โต้ตอบกับข้อความแจ้งสิทธิ์โดยกด "อนุญาต" "บล็อก" หรือปิดข้อความแจ้ง เราจะได้รับผลลัพธ์เป็นสตริง: 'granted', 'default' หรือ 'denied'

ในโค้ดตัวอย่างด้านบน คำสัญญาที่ askPermission() แสดงผลไว้จะได้รับการแก้ไขหากมีการให้สิทธิ์ มิเช่นนั้นเราจะแสดงข้อผิดพลาดที่ทำให้คำสัญญาปฏิเสธ

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

ข่าวดีคือผู้ใช้ส่วนใหญ่จะยินดีให้สิทธิ์ตราบใดที่ผู้ใช้ทราบถึงเหตุผลที่ต้องขอสิทธิ์

เราจะมาดูกันว่าเว็บไซต์ยอดนิยมบางแห่งขอการอนุญาตอย่างไรในภายหลัง

สมัครรับข้อมูลผู้ใช้ด้วย PushManager

เมื่อเราลงทะเบียน Service Worker และได้รับอนุญาตแล้ว เราสามารถสมัครสมาชิกผู้ใช้ได้โดยโทรไปที่ 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;
    });
}

เมื่อเรียกเมธอด subscribe() เราจะส่งในออบเจ็กต์ options ซึ่งประกอบด้วยพารามิเตอร์ที่จำเป็นและไม่บังคับ

มาดูตัวเลือกต่างๆ ที่เราส่งเข้ามาได้

ตัวเลือก userVisibleOnly

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

ข้อกังวลก็คือนักพัฒนาซอฟต์แวร์อาจทำเรื่องน่ารังเกียจ เช่น ติดตามตำแหน่งของผู้ใช้อย่างต่อเนื่องโดยที่ผู้ใช้ไม่รู้ตัว

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

ตอนนี้คุณต้องส่งที่มีมูลค่า true หากไม่ใส่คีย์ userVisibleOnly หรือบัตรผ่านใน false คุณจะได้รับข้อผิดพลาดต่อไปนี้

ปัจจุบัน Chrome รองรับเฉพาะ Push API สำหรับการสมัครใช้บริการที่จะส่งผลให้มีข้อความที่ผู้ใช้เห็น คุณระบุข้อมูลนี้ได้โดยโทรไปที่ pushManager.subscribe({userVisibleOnly: true}) แทน ดูรายละเอียดเพิ่มเติมได้ที่ https://goo.gl/yqv4Q4

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

ตัวเลือก applicationServerKey

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

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

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

แผนภาพด้านล่างจะแสดงขั้นตอนเหล่านี้

  1. ระบบจะโหลดเว็บแอปในเบราว์เซอร์และคุณเรียกใช้ subscribe() โดยส่งผ่านคีย์เซิร์ฟเวอร์แอปพลิเคชันสาธารณะ
  2. จากนั้นเบราว์เซอร์จะส่งคำขอเครือข่ายไปยังบริการพุชซึ่งจะสร้างปลายทาง เชื่อมโยงปลายทางนี้กับคีย์สาธารณะของแอปพลิเคชัน แล้วส่งปลายทางกลับไปยังเบราว์เซอร์
  3. เบราว์เซอร์จะเพิ่มปลายทางนี้ไปยัง PushSubscription ซึ่งส่งคืนผ่าน subscribe() ของสัญญา

ภาพคีย์เซิร์ฟเวอร์ของแอปพลิเคชันสาธารณะใช้ในวิธีการสมัคร

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

วิธีใช้คีย์เซิร์ฟเวอร์แอปพลิเคชันส่วนตัว
เมื่อส่งข้อความ

โดยทางเทคนิคแล้ว applicationServerKey เป็นตัวเลือกที่ไม่บังคับ อย่างไรก็ตาม วิธีการติดตั้งใช้งานที่ง่ายที่สุดใน Chrome จำเป็นต้องใช้ และเบราว์เซอร์อื่นๆ อาจจำเป็นต้องใช้ในอนาคต แต่ไม่บังคับใน Firefox

ข้อกำหนดที่กำหนดสิ่งที่คีย์เซิร์ฟเวอร์แอปพลิเคชันควรเป็น ข้อกำหนด VAPID เมื่อใดก็ตามที่คุณอ่านข้อมูลที่เกี่ยวข้องกับ "คีย์เซิร์ฟเวอร์แอปพลิเคชัน" หรือ "คีย์ VAPID" โปรดอย่าลืมว่าคีย์เหล่านั้นคือสิ่งเดียวกัน

วิธีสร้างคีย์เซิร์ฟเวอร์แอปพลิเคชัน

คุณสร้างชุดคีย์เซิร์ฟเวอร์ของแอปพลิเคชันแบบสาธารณะและส่วนตัวได้โดยไปที่ web-push-codelab.glitch.me หรือใช้บรรทัดคำสั่ง Web-push เพื่อสร้างคีย์โดยทำตามขั้นตอนต่อไปนี้

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

คุณเพียงต้องสร้างคีย์เหล่านี้เพียงครั้งเดียวสำหรับแอปพลิเคชัน ขอแค่คุณเก็บคีย์ส่วนตัวของคุณให้เป็นส่วนตัว (ใช่แล้ว ฉันเพิ่งพูดไป)

สิทธิ์และ subscription()

การโทรหา subscribe() จะมีผลข้างเคียง 1 อย่าง หากเว็บแอปไม่มีสิทธิ์แสดงการแจ้งเตือนเมื่อเรียกใช้ subscribe() เบราว์เซอร์จะขอสิทธิ์ให้คุณ วิธีนี้มีประโยชน์หาก UI ของคุณใช้กับขั้นตอนนี้ได้ แต่หากคุณต้องการการควบคุมมากขึ้น (และเราคิดว่านักพัฒนาซอฟต์แวร์ส่วนใหญ่จะต้องการ) ให้ยึดตาม Notification.requestPermission() API ที่เราใช้ก่อนหน้านี้

การสมัครรับข้อมูลแบบ Push คืออะไร

เราโทรติดต่อ subscribe() อนุญาตบางตัวเลือก และในทางกลับกัน เราได้รับคำสัญญาที่จะเปลี่ยนเป็น PushSubscription ซึ่งส่งผลให้โค้ดมีลักษณะดังนี้

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

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

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

endpoint คือ URL ของบริการพุช หากต้องการทริกเกอร์ข้อความพุช ให้ส่งคำขอ POST ไปยัง URL นี้

ออบเจ็กต์ keys มีค่าที่ใช้ในการเข้ารหัสข้อมูลข้อความที่ส่งด้วยข้อความพุช (ซึ่งเราจะพูดถึงในภายหลังในส่วนนี้)

ส่งการสมัครใช้บริการไปยังเซิร์ฟเวอร์ของคุณ

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

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

ซึ่งการสมัครใช้บริการจะเสร็จสิ้นในหน้าเว็บ ดังนี้

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

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

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

เมื่อระบุรายละเอียดของ PushSubscription บนเซิร์ฟเวอร์ เราพร้อมที่จะส่งข้อความให้ผู้ใช้ได้ทุกเมื่อที่ต้องการ

คำถามที่พบบ่อย

คำถาม 2-3 ข้อที่ผู้คนมักถามกันมีดังนี้

ฉันจะเปลี่ยนบริการพุชที่เบราว์เซอร์ใช้ได้ไหม

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

แต่ละเบราว์เซอร์ใช้บริการพุชที่แตกต่างกันและไม่มี API ที่แตกต่างกันใช่ไหม

บริการพุชทั้งหมดจะใช้ API เดียวกัน

API ทั่วไปนี้เรียกว่าโปรโตคอลพุชจากเว็บและอธิบายคำขอเครือข่ายที่แอปพลิเคชันของคุณต้องทำเพื่อทริกเกอร์ข้อความพุช

หากฉันสมัครใช้บริการบนเดสก์ท็อป ผู้ใช้นั้นจะสมัครใช้บริการในโทรศัพท์ด้วยไหม

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

ขั้นตอนถัดไป

ห้องทดลองการเขียนโค้ด