หากถามนักพัฒนาแอปว่าฟีเจอร์ใดของอุปกรณ์เคลื่อนที่ที่ขาดหายไปจากเว็บ ฟีเจอร์ Push Notification มักจะติดอันดับต้นๆ เสมอ
ข้อความ Push ช่วยให้ผู้ใช้เลือกรับข้อมูลอัปเดตจากเว็บไซต์ที่ชื่นชอบได้ทันท่วงที และช่วยให้คุณสามารถดึงดูดผู้ใช้ให้กลับมามีส่วนร่วมอีกครั้งด้วยเนื้อหาที่น่าสนใจและเหมาะกับแต่ละบุคคล
ใน Chrome เวอร์ชัน 42 นักพัฒนาซอฟต์แวร์ใช้งาน Push API และ Notification API ได้
Push API ใน Chrome อาศัยเทคโนโลยีหลายอย่างด้วยกัน ซึ่งรวมถึงไฟล์ Manifest ของเว็บแอปและ Service Worker ในโพสต์นี้ เราจะพูดถึงเทคโนโลยีแต่ละอย่างเหล่านี้ แต่ขอพูดถึงเฉพาะข้อมูลขั้นต่ำเท่านั้นเพื่อเริ่มต้นใช้งานการรับส่งข้อความ Push หากต้องการทําความเข้าใจฟีเจอร์อื่นๆ ของไฟล์ Manifest และความสามารถแบบออฟไลน์ของ Service Worker ให้ดียิ่งขึ้น โปรดดูลิงก์ด้านบน
นอกจากนี้ เราจะดูสิ่งที่จะเพิ่มลงใน API ใน Chrome เวอร์ชันต่อๆ ไป และสุดท้ายจะมีคําถามที่พบบ่อย
การใช้การรับส่งข้อความพุชสำหรับ Chrome
ส่วนนี้จะอธิบายขั้นตอนแต่ละขั้นตอนที่คุณต้องดำเนินการเพื่อรองรับการรับส่งข้อความ Push ในเว็บแอป
ลงทะเบียน Service Worker
อาจต้องให้ Service Worker นำข้อความพุชสำหรับเว็บไปใช้ เหตุผลคือเมื่อได้รับข้อความ Push เบราว์เซอร์สามารถเริ่ม Service Worker ซึ่งทำงานในเบื้องหลังโดยที่ไม่ต้องเปิดหน้าเว็บ และส่งเหตุการณ์เพื่อให้คุณตัดสินใจว่าจะจัดการข้อความ Push นั้นอย่างไร
ด้านล่างนี้คือตัวอย่างวิธีลงทะเบียน Service Worker ในเว็บแอป เมื่อลงทะเบียนสำเร็จแล้ว เราจะเรียกใช้ initialiseState() ซึ่งเราจะกล่าวถึงในอีกไม่ช้า
var isPushEnabled = false;
…
window.addEventListener('load', function() {
var pushButton = document.querySelector('.js-push-button');
pushButton.addEventListener('click', function() {
if (isPushEnabled) {
unsubscribe();
} else {
subscribe();
}
});
// Check that service workers are supported, if so, progressively
// enhance and add push messaging support, otherwise continue without it.
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js')
.then(initialiseState);
} else {
console.warn('Service workers aren\'t supported in this browser.');
}
});
แฮนเดิลการคลิกปุ่มจะสมัครหรือยกเลิกการสมัครรับข้อความ Push ของผู้ใช้ isPushEnabled เป็นตัวแปรส่วนกลางที่ติดตามว่าขณะนี้มีการสมัครรับข้อความ Push หรือไม่ ระบบจะใช้ตัวแปรเหล่านี้ตลอดทั้งข้อมูลโค้ด
จากนั้นเราจะตรวจสอบว่าระบบรองรับ Service Worker ก่อนที่จะลงทะเบียนservice-worker.js
ไฟล์ซึ่งมีตรรกะสำหรับจัดการข้อความ Push ในส่วนนี้ เราเพียงแค่บอกเบราว์เซอร์ว่าไฟล์ JavaScript นี้เป็น Service Worker สำหรับเว็บไซต์ของเรา
ตั้งค่าสถานะเริ่มต้น
เมื่อลงทะเบียน Service Worker แล้ว เราจำเป็นต้องตั้งค่าสถานะของ UI
ผู้ใช้จะคาดหวัง UI ที่ใช้งานง่ายเพื่อเปิดหรือปิดใช้ข้อความ Push สําหรับเว็บไซต์ของคุณ และคาดหวังว่า UI จะอัปเดตอยู่เสมอเมื่อมีการเปลี่ยนแปลง กล่าวคือ หากผู้ใช้เปิดใช้ข้อความ Push สําหรับเว็บไซต์ของคุณ แล้วออกจากเว็บไซต์ไป 1 สัปดาห์ต่อมา UI ควรไฮไลต์ว่าเปิดใช้ข้อความ Push แล้ว
คุณดูหลักเกณฑ์ด้าน UX ได้ในเอกสารนี้ โดยในบทความนี้เราจะเน้นด้านเทคนิค
ณ จุดนี้ คุณอาจคิดว่ามีสถานะเพียง 2 สถานะเท่านั้น ได้แก่ เปิดใช้หรือปิดใช้ อย่างไรก็ตาม ยังมีสถานะอื่นๆ เกี่ยวกับการแจ้งเตือนที่คุณต้องพิจารณาด้วย
มี API จำนวนหนึ่งที่เราต้องตรวจสอบก่อนที่จะเปิดใช้งานปุ่มของเรา และ หากทุกอย่างมีการสนับสนุน เราสามารถเปิดใช้งาน UI ของเราและตั้งค่าสถานะเริ่มต้นเพื่อระบุว่าเป็นการติดตามการรับส่งข้อความพุชหรือไม่
เนื่องจากการตรวจสอบส่วนใหญ่เหล่านี้ส่งผลให้ UI ถูกปิดใช้ คุณจึงควรตั้งค่าสถานะเริ่มต้นเป็น "ปิดใช้" นอกจากนี้ ยังหลีกเลี่ยงการสับสนในกรณีที่เกิดปัญหากับ JavaScript ของหน้าเว็บ เช่น ดาวน์โหลดไฟล์ JS ไม่ได้ หรือผู้ใช้ปิดใช้ JavaScript ไว้
<button class="js-push-button" disabled>
Enable Push Messages
</button>
สถานะเริ่มต้นนี้ช่วยให้เราทําการตรวจสอบที่ระบุไว้ข้างต้นในวิธี initialiseState()
ได้ เช่น หลังจากลงทะเบียน Service Worker แล้ว
// Once the service worker is registered set the initial state
function initialiseState() {
// Are Notifications supported in the service worker?
if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
console.warn('Notifications aren\'t supported.');
return;
}
// Check the current Notification permission.
// If its denied, it's a permanent block until the
// user changes the permission
if (Notification.permission === 'denied') {
console.warn('The user has blocked notifications.');
return;
}
// Check if push messaging is supported
if (!('PushManager' in window)) {
console.warn('Push messaging isn\'t supported.');
return;
}
// We need the service worker registration to check for a subscription
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
// Do we already have a push message subscription?
serviceWorkerRegistration.pushManager.getSubscription()
.then(function(subscription) {
// Enable any UI which subscribes / unsubscribes from
// push messages.
var pushButton = document.querySelector('.js-push-button');
pushButton.disabled = false;
if (!subscription) {
// We aren't subscribed to push, so set UI
// to allow the user to enable push
return;
}
// Keep your server in sync with the latest subscriptionId
sendSubscriptionToServer(subscription);
// Set your UI to show they have subscribed for
// push messages
pushButton.textContent = 'Disable Push Messages';
isPushEnabled = true;
})
.catch(function(err) {
console.warn('Error during getSubscription()', err);
});
});
}
ภาพรวมคร่าวๆ ของขั้นตอนเหล่านี้มีดังนี้
- เราตรวจสอบว่า
showNotification
พร้อมใช้งานใน ServiceWorkerRegistration prototype หากไม่มี แสดงว่าเราไม่สามารถแสดงการแจ้งเตือนจาก Service Worker ได้เมื่อได้รับข้อความ Push - เราจะตรวจสอบว่า
Notification.permission
ปัจจุบันคืออะไรเพื่อให้แน่ใจว่าไม่ใช่"denied"
สิทธิ์ที่ถูกปฏิเสธหมายความว่าคุณจะแสดงการแจ้งเตือนไม่ได้จนกว่าผู้ใช้จะเปลี่ยนสิทธิ์ในเบราว์เซอร์ด้วยตนเอง - หากต้องการตรวจสอบว่าระบบรองรับการรับส่งข้อความ Push หรือไม่ เราจะตรวจสอบว่า
PushManager
พร้อมใช้งานในออบเจ็กต์หน้าต่างหรือไม่ - สุดท้าย เราใช้
pushManager.getSubscription()
เพื่อตรวจสอบว่าเรามีการสมัครใช้บริการอยู่แล้วหรือไม่ หากมี เราจะส่งรายละเอียดการสมัครใช้บริการไปยังเซิร์ฟเวอร์เพื่อให้มั่นใจว่าเรามีข้อมูลที่ถูกต้องและตั้งค่า UI เพื่อระบุว่าเปิดใช้การรับส่งข้อความ Push ไว้แล้วหรือยัง เราจะดูรายละเอียดที่มีอยู่ในออบเจ็กต์การสมัครใช้บริการในบทความนี้ในภายหลัง
เราจะรอจนกว่า navigator.serviceWorker.ready
จะได้รับการแก้ไขเพื่อตรวจสอบการสมัครใช้บริการและเปิดใช้ปุ่ม Push เนื่องจากคุณจะสมัครรับข้อความ Push ได้ก็ต่อเมื่อผู้ให้บริการทำงานอยู่
ขั้นตอนถัดไปคือจัดการเมื่อผู้ใช้ต้องการเปิดใช้ข้อความ Push แต่ก่อนอื่นเราต้องตั้งค่าโปรเจ็กต์ Google Developer Console และเพิ่มพารามิเตอร์บางอย่างลงในไฟล์ Manifest เพื่อใช้ Firebase Cloud Messaging (FCM) หรือเดิมเรียกว่า Google Cloud Messaging (GCM)
สร้างโปรเจ็กต์ในคอนโซลนักพัฒนาซอฟต์แวร์ Firebase
Chrome ใช้ FCM เพื่อจัดการการส่งและการนำส่งข้อความ Push แต่หากต้องการใช้ FCM API คุณต้องตั้งค่าโปรเจ็กต์ในคอนโซลนักพัฒนาแอป Firebase
ขั้นตอนต่อไปนี้มีไว้สำหรับ Chrome, Opera สําหรับ Android และเบราว์เซอร์ Samsung ที่ใช้ FCM เราจะพูดถึงวิธีการทำงานของฟีเจอร์นี้ในเบราว์เซอร์อื่นๆ ในบทความนี้
สร้างโปรเจ็กต์นักพัฒนาแอป Firebase ใหม่
หากต้องการเริ่มต้น คุณต้องสร้างโปรเจ็กต์ใหม่ใน https://console.firebase.google.com/ โดยคลิก "สร้างโปรเจ็กต์ใหม่"
เพิ่มชื่อโปรเจ็กต์ สร้างโปรเจ็กต์ แล้วระบบจะนําคุณไปยังแดชบอร์ดของโปรเจ็กต์
จากหน้าแดชบอร์ดนี้ ให้คลิกฟันเฟืองข้างชื่อโปรเจ็กต์ที่มุมซ้ายบน แล้วคลิก "การตั้งค่าโปรเจ็กต์"
ในหน้าการตั้งค่า ให้คลิกแท็บ "การรับส่งข้อความในระบบคลาวด์"
หน้านี้มีคีย์ API สําหรับการรับส่งข้อความ Push ซึ่งเราจะใช้ในภายหลัง และรหัสผู้ส่งซึ่งเราต้องใส่ไว้ในไฟล์ Manifest ของเว็บแอปในส่วนถัดไป
เพิ่มไฟล์ Manifest ของเว็บแอป
สําหรับ Push เราจําเป็นต้องเพิ่มไฟล์ Manifest ที่มีช่อง gcm_sender_id เพื่อให้การสมัครรับ Push สําเร็จ พารามิเตอร์นี้จําเป็นสําหรับ Chrome, Opera สําหรับ Android และเบราว์เซอร์ Samsung เท่านั้นเพื่อให้ใช้ FCM / GCM ได้
เบราว์เซอร์เหล่านี้ใช้ gcm_sender_id เมื่อสมัครใช้บริการอุปกรณ์ของผู้ใช้กับ FCM ซึ่งหมายความว่า FCM สามารถระบุอุปกรณ์ของผู้ใช้และตรวจสอบว่ารหัสผู้ส่งตรงกับคีย์ API ที่เกี่ยวข้อง และผู้ใช้ได้อนุญาตให้เซิร์ฟเวอร์ของคุณส่งข้อความ Push ไปให้
ด้านล่างนี้คือไฟล์ Manifest ที่ง่ายสุดๆ
{
"name": "Push Demo",
"short_name": "Push Demo",
"icons": [{
"src": "images/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
}],
"start_url": "/index.html?homescreen=1",
"display": "standalone",
"gcm_sender_id": "<Your Sender ID Here>"
}
คุณจะต้องตั้งค่า gcm_sender_id เป็นรหัสผู้ส่งจากโปรเจ็กต์ Firebase
เมื่อบันทึกไฟล์ Manifest ในโปรเจ็กต์แล้ว (ใช้ชื่อ manifest.json ก็ได้) ให้อ้างอิงไฟล์จาก HTML ด้วยแท็กต่อไปนี้ในส่วนหัวของหน้า
<link rel="manifest" href="/manifest.json">
หากไม่เพิ่มไฟล์ Manifest ของเว็บที่มีพารามิเตอร์เหล่านี้ คุณจะได้รับข้อยกเว้นเมื่อพยายามสมัครใช้บริการ Push ของผู้ใช้พร้อมข้อผิดพลาด "Registration failed - no sender id provided"
หรือ "Registration failed -
permission denied"
สมัครรับการรับส่งข้อความ Push
เมื่อตั้งค่าไฟล์ Manifest แล้ว คุณสามารถกลับไปที่ JavaScript ของเว็บไซต์ได้
หากต้องการสมัครใช้บริการ คุณต้องเรียกใช้เมธอด subscribe() ในออบเจ็กต์ PushManager ซึ่งเข้าถึงได้ผ่าน ServiceWorkerRegistration
การดำเนินการนี้จะขอให้ผู้ใช้ให้สิทธิ์แก่ต้นทางในการส่งข้อความ Push หากไม่มีสิทธิ์นี้ คุณจะสมัครใช้บริการไม่สำเร็จ
หากสัญญาที่แสดงผลโดยเมธอด subscribe() สำเร็จ คุณจะได้รับออบเจ็กต์ PushSubscription ซึ่งมีปลายทาง
คุณควรบันทึกอุปกรณ์ปลายทางไว้ในเซิร์ฟเวอร์สำหรับผู้ใช้แต่ละราย เนื่องจากคุณจะต้องส่งข้อความ Push ในภายหลัง
โค้ดต่อไปนี้จะสมัครใช้บริการการรับส่งข้อความ Push ให้กับผู้ใช้
function subscribe() {
// Disable the button so it can't be changed while
// we process the permission request
var pushButton = document.querySelector('.js-push-button');
pushButton.disabled = true;
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
serviceWorkerRegistration.pushManager.subscribe()
.then(function(subscription) {
// The subscription was successful
isPushEnabled = true;
pushButton.textContent = 'Disable Push Messages';
pushButton.disabled = false;
// TODO: Send the subscription.endpoint to your server
// and save it to send a push message at a later date
return sendSubscriptionToServer(subscription);
})
.catch(function(e) {
if (Notification.permission === 'denied') {
// The user denied the notification permission which
// means we failed to subscribe and the user will need
// to manually change the notification permission to
// subscribe to push messages
console.warn('Permission for Notifications was denied');
pushButton.disabled = true;
} else {
// A problem occurred with the subscription; common reasons
// include network errors, and lacking gcm_sender_id and/or
// gcm_user_visible_only in the manifest.
console.error('Unable to subscribe to push.', e);
pushButton.disabled = false;
pushButton.textContent = 'Enable Push Messages';
}
});
});
}
ณ จุดนี้เว็บแอปของคุณพร้อมรับข้อความพุชแล้ว แต่จะไม่มีอะไรเกิดขึ้นจนกว่าเราจะเพิ่ม Listener เหตุการณ์พุชลงในไฟล์โปรแกรมทำงานของบริการ
Listener เหตุการณ์พุชของ Service Worker
เมื่อได้รับข้อความ Push (เราจะพูดถึงวิธีส่งข้อความ Push จริงในส่วนถัดไป) ระบบจะส่งเหตุการณ์ Push ใน Service Worker ซึ่งคุณจะต้องแสดงการแจ้งเตือน
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
var title = 'Yay a message.';
var body = 'We have received a push message.';
var icon = '/images/icon-192x192.png';
var tag = 'simple-push-demo-notification-tag';
event.waitUntil(
self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag
})
);
});
โค้ดนี้จะลงทะเบียน Listener เหตุการณ์ Push และแสดงการแจ้งเตือนที่มีชื่อ ข้อความเนื้อหา ไอคอน และแท็กการแจ้งเตือนที่กําหนดไว้ล่วงหน้า
สิ่งที่ควรสังเกตอย่างหนึ่งในตัวอย่างนี้คือevent.waitUntil()
method เมธอดนี้จะรับพรอมต์และขยายอายุของตัวแฮนเดิลเหตุการณ์ (หรืออาจกล่าวได้ว่าเป็นการทำให้ Service Worker ทำงานต่อไป) จนกว่าพรอมต์จะยุติ
ในกรณีนี้ พรอมต์ที่ส่งไปยัง event.waitUntil
คือพรอมต์ที่ได้จาก showNotification()
แท็กการแจ้งเตือนจะทำหน้าที่เป็นตัวระบุสำหรับการแจ้งเตือนที่ไม่ซ้ำกัน หากเราส่งข้อความพุช 2 รายการไปยังปลายทางเดียวกันโดยมีเวลาหน่วงสั้นๆ และแสดงการแจ้งเตือนด้วยแท็กเดียวกัน เบราว์เซอร์จะแสดงการแจ้งเตือนแรกและแทนที่ด้วยการแจ้งเตือนครั้งที่ 2 เมื่อได้รับข้อความพุช
หากต้องการแสดงการแจ้งเตือนหลายรายการพร้อมกัน ให้ใช้แท็กอื่นหรือไม่มีแท็กเลย เราจะมาดูตัวอย่างที่สมบูรณ์ขึ้นของการแสดงการแจ้งเตือนในช่วงท้ายของโพสต์นี้ ในระหว่างนี้ เรามาลองทำแบบง่ายๆ และดูว่าการส่งข้อความ Push จะแสดงการแจ้งเตือนนี้ไหม
การส่งข้อความพุช
เราได้สมัครรับข้อความ Push และ Service Worker พร้อมแสดงการแจ้งเตือนแล้ว ตอนนี้ถึงเวลาส่งข้อความ Push ผ่าน FCM
วิธีนี้ใช้ได้กับเบราว์เซอร์ที่ใช้ FCM เท่านั้น
เมื่อคุณส่งตัวแปร PushSubscription.endpoint
ไปยังเซิร์ฟเวอร์ จุดสิ้นสุดสําหรับ FCM จะมีลักษณะพิเศษ มีพารามิเตอร์ที่ส่วนท้ายของ URL ซึ่งเป็น registration_id
ตัวอย่างปลายทาง ได้แก่
https://fcm.googleapis.com/fcm/send/APA91bHPffi8zclbIBDcToXN_LEpT6iA87pgR-J-MuuVVycM0SmptG-rXdCPKTM5pvKiHk2Ts-ukL1KV8exGOnurOAKdbvH9jcvg8h2gSi-zZJyToiiydjAJW6Fa9mE3_7vsNIgzF28KGspVmLUpMgYLBd1rxaVh-L4NDzD7HyTkhFOfwWiyVdKh__rEt15W9n2o6cZ8nxrP
URL ของ FCM คือ
https://fcm.googleapis.com/fcm/send
registration_id
จะเป็นดังนี้
APA91bHPffi8zclbIBDcToXN_LEpT6iA87pgR-J-MuuVVycM0SmptG-rXdCPKTM5pvKiHk2Ts-ukL1KV8exGOnurOAKdbvH9jcvg8h2gSi-zZJyToiiydjAJW6Fa9mE3_7vsNIgzF28KGspVmLUpMgYLBd1rxaVh-L4NDzD7HyTkhFOfwWiyVdKh__rEt15W9n2o6cZ8nxrP
กรณีนี้ใช้กับเบราว์เซอร์ที่ใช้ FCM เท่านั้น ในเบราว์เซอร์ปกติ คุณเพียงแค่ได้รับปลายทางและเรียกใช้ปลายทางนั้นด้วยวิธีมาตรฐาน ซึ่งจะใช้งานได้ไม่ว่า URL ใด
ซึ่งหมายความว่าคุณจะต้องตรวจสอบในเซิร์ฟเวอร์ว่าปลายทางเป็น FCM หรือไม่ หากใช่ ให้ดึงข้อมูล registration_id หากต้องการดำเนินการนี้ใน Python คุณอาจทำดังนี้
if endpoint.startswith('https://fcm.googleapis.com/fcm/send'):
endpointParts = endpoint.split('/')
registrationId = endpointParts[len(endpointParts) - 1]
endpoint = 'https://fcm.googleapis.com/fcm/send'
เมื่อได้รหัสการลงทะเบียนแล้ว คุณจะเรียกใช้ FCM API ได้ คุณดูเอกสารอ้างอิงเกี่ยวกับ FCM API ได้ที่นี่
สิ่งสำคัญที่ควรทราบเมื่อเรียกใช้ FCM มีดังนี้
- คุณต้องตั้งค่าส่วนหัวการให้สิทธิ์ซึ่งมีค่าเป็น
key=<YOUR_API_KEY>
เมื่อเรียกใช้ API โดยที่<YOUR_API_KEY>
คือคีย์ API จากโปรเจ็กต์ Firebase- FCM จะใช้คีย์ API เพื่อค้นหารหัสผู้ส่งที่เหมาะสม ตรวจสอบว่าผู้ใช้ให้สิทธิ์สำหรับโปรเจ็กต์ของคุณ และตรวจสอบว่าที่อยู่ IP ของเซิร์ฟเวอร์อยู่ในรายการที่อนุญาตสำหรับโปรเจ็กต์นั้น
- ส่วนหัว
Content-Type
ที่เหมาะสมของapplication/json
หรือapplication/x-www-form-urlencoded;charset=UTF-8
โดยขึ้นอยู่กับว่าคุณส่งข้อมูลเป็น JSON หรือข้อมูลแบบฟอร์ม - อาร์เรย์
registration_ids
- รหัสการลงทะเบียนที่ดึงมาจากปลายทางของผู้ใช้
โปรดอ่านเอกสารประกอบเกี่ยวกับวิธีส่งข้อความ Push จากเซิร์ฟเวอร์ แต่หากต้องการตรวจสอบ Service Worker อย่างรวดเร็ว คุณสามารถใช้ cURL เพื่อส่งข้อความ Push ไปยังเบราว์เซอร์ได้
สลับ <YOUR_API_KEY>
และ <YOUR_REGISTRATION_ID>
ในคำสั่ง cURL นี้เป็นของคุณเอง แล้วเรียกใช้จากเทอร์มินัล
คุณควรเห็นการแจ้งเตือนที่ยอดเยี่ยมนี้
curl --header "Authorization: key=<YOUR_API_KEY>" --header
"Content-Type: application/json" https://fcm.googleapis.com/fcm/send -d
"{\"registration_ids\":[\"<YOUR_REGISTRATION_ID>\"]}"
เมื่อพัฒนาตรรกะแบ็กเอนด์ โปรดทราบว่าส่วนหัวการให้สิทธิ์และรูปแบบของเนื้อหา POST นั้นเจาะจงสำหรับปลายทาง FCM ดังนั้นให้ตรวจหาเมื่อปลายทางสำหรับ FCM แล้วเพิ่มส่วนหัวและจัดรูปแบบเนื้อหา POST ตามเงื่อนไข สำหรับเบราว์เซอร์อื่นๆ (และหวังว่าจะเป็น Chrome ในอนาคต) คุณจะต้องใช้โปรโตคอลการพุชในเว็บ
ข้อเสียของการใช้งาน Push API ใน Chrome ในปัจจุบันคือคุณจะส่งข้อมูลใดๆ กับข้อความ Push ไม่ได้ ไม่เป็นไร เหตุผลคือในการใช้งานในอนาคต ข้อมูลเพย์โหลดจะต้องได้รับการเข้ารหัสบนเซิร์ฟเวอร์ก่อนที่จะส่งไปยังปลายทางการรับส่งข้อความ Push วิธีนี้จะช่วยให้ปลายทางไม่ว่าจะเป็นผู้ให้บริการพุชใดจะไม่สามารถดูเนื้อหาของข้อความ Push ได้โดยง่าย และยังป้องกันช่องโหว่อื่นๆ ด้วย เช่น การตรวจสอบใบรับรอง HTTPS ที่ไม่มีประสิทธิภาพและการโจมตีแบบแทรกกลางการสื่อสารระหว่างเซิร์ฟเวอร์ของคุณและผู้ให้บริการพุช อย่างไรก็ตาม เรายังไม่รองรับการเข้ารหัสนี้ ดังนั้นในระหว่างนี้ คุณจะต้องดึงข้อมูลเพื่อรับข้อมูลที่จําเป็นในการสร้างการแจ้งเตือน
ตัวอย่างเหตุการณ์ Push ที่สมบูรณ์ยิ่งขึ้น
การแจ้งเตือนที่เราเห็นจนถึงตอนนี้ค่อนข้างพื้นฐานและตัวอย่างก็ไม่ค่อยครอบคลุม Use Case ในชีวิตจริง
ในทางปฏิบัติแล้ว ผู้ใช้ส่วนใหญ่ต้องการรับข้อมูลบางอย่างจากเซิร์ฟเวอร์ก่อนแสดงการแจ้งเตือน ข้อมูลนี้อาจเป็นข้อมูลที่จะเติมชื่อและข้อความแจ้งเตือนที่เจาะจง หรืออาจดำเนินการไปอีกขั้นโดยแคชหน้าเว็บหรือข้อมูลบางอย่างไว้ เพื่อที่ว่าเมื่อผู้ใช้คลิกการแจ้งเตือน ทุกอย่างจะพร้อมใช้งานทันทีเมื่อเปิดเบราว์เซอร์ แม้ว่าเครือข่ายจะไม่พร้อมใช้งานในขณะนั้นก็ตาม
ในโค้ดต่อไปนี้ เราจะดึงข้อมูลบางอย่างจาก API, แปลงการตอบกลับเป็นออบเจ็กต์ และใช้เพื่อแสดงการแจ้งเตือน
self.addEventListener('push', function(event) {
// Since there is no payload data with the first version
// of push messages, we'll grab some data from
// an API and use it to populate a notification
event.waitUntil(
fetch(SOME_API_ENDPOINT).then(function(response) {
if (response.status !== 200) {
// Either show a message to the user explaining the error
// or enter a generic message and handle the
// onnotificationclick event to direct the user to a web page
console.log('Looks like there was a problem. Status Code: ' + response.status);
throw new Error();
}
// Examine the text in the response
return response.json().then(function(data) {
if (data.error || !data.notification) {
console.error('The API returned an error.', data.error);
throw new Error();
}
var title = data.notification.title;
var message = data.notification.message;
var icon = data.notification.icon;
var notificationTag = data.notification.tag;
return self.registration.showNotification(title, {
body: message,
icon: icon,
tag: notificationTag
});
});
}).catch(function(err) {
console.error('Unable to retrieve data', err);
var title = 'An error occurred';
var message = 'We were unable to get the information for this push message';
var icon = URL_TO_DEFAULT_ICON;
var notificationTag = 'notification-error';
return self.registration.showNotification(title, {
body: message,
icon: icon,
tag: notificationTag
});
})
);
});
ขอย้ำอีกครั้งว่า event.waitUntil()
จะต้องทำตามสัญญา ซึ่งส่งผลให้ showNotification()
ส่งคำสัญญากลับมา ซึ่งหมายความว่า Listener เหตุการณ์จะไม่ออกจนกว่าการเรียกใช้ fetch()
แบบไม่พร้อมกันจะเสร็จสมบูรณ์ และการแจ้งเตือนจะแสดงขึ้น
คุณจะเห็นว่าเราแสดงการแจ้งเตือนแม้ว่าจะมีข้อผิดพลาดก็ตาม เนื่องจากหากไม่ทำเช่นนั้น Chrome จะแสดงการแจ้งเตือนทั่วไปของตนเอง
การเปิด URL เมื่อผู้ใช้คลิกการแจ้งเตือน
เมื่อผู้ใช้คลิกการแจ้งเตือน ระบบจะส่งเหตุการณ์ notificationclick
ใน Service Worker ภายในเครื่องจัดการของคุณ คุณสามารถดำเนินการที่เหมาะสม
เช่น โฟกัสแท็บหรือเปิดหน้าต่างด้วย URL ที่ต้องการ
self.addEventListener('notificationclick', function(event) {
console.log('On notification click: ', event.notification.tag);
// Android doesn't close the notification when you click on it
// See: http://crbug.com/463146
event.notification.close();
// This looks to see if the current is already open and
// focuses if it is
event.waitUntil(
clients.matchAll({
type: "window"
})
.then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == '/' && 'focus' in client)
return client.focus();
}
if (clients.openWindow) {
return clients.openWindow('/');
}
})
);
});
ตัวอย่างนี้จะเปิดเบราว์เซอร์ไปยังรูทของต้นทางของเว็บไซต์ โดยโฟกัสที่แท็บต้นทางเดียวกันที่มีอยู่ หากมี หรือเปิดแท็บใหม่
โปรดดูโพสต์ที่อธิบายสิ่งที่คุณทำได้ด้วย Notification API ที่นี่
ยกเลิกการสมัครใช้งานอุปกรณ์ของผู้ใช้
คุณได้สมัครใช้บริการอุปกรณ์ของผู้ใช้และผู้ใช้ก็ได้รับข้อความ Push แล้ว แต่คุณจะยกเลิกการสมัครใช้บริการได้อย่างไร
สิ่งที่ต้องทำหลักๆ เพื่อยกเลิกการสมัครใช้บริการในอุปกรณ์ของผู้ใช้คือการเรียกใช้เมธอด unsubscribe()
ในออบเจ็กต์ PushSubscription และนำปลายทางออกจากเซิร์ฟเวอร์ (เพื่อไม่ให้ส่งข้อความ Push ที่รู้ว่าผู้ใช้จะไม่รับ) โค้ดด้านล่าง
จะมีคุณสมบัติดังนี้
function unsubscribe() {
var pushButton = document.querySelector('.js-push-button');
pushButton.disabled = true;
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
// To unsubscribe from push messaging, you need get the
// subscription object, which you can call unsubscribe() on.
serviceWorkerRegistration.pushManager.getSubscription().then(
function(pushSubscription) {
// Check we have a subscription to unsubscribe
if (!pushSubscription) {
// No subscription object, so set the state
// to allow the user to subscribe to push
isPushEnabled = false;
pushButton.disabled = false;
pushButton.textContent = 'Enable Push Messages';
return;
}
var subscriptionId = pushSubscription.subscriptionId;
// TODO: Make a request to your server to remove
// the subscriptionId from your data store so you
// don't attempt to send them push messages anymore
// We have a subscription, so call unsubscribe on it
pushSubscription.unsubscribe().then(function(successful) {
pushButton.disabled = false;
pushButton.textContent = 'Enable Push Messages';
isPushEnabled = false;
}).catch(function(e) {
// We failed to unsubscribe, this can lead to
// an unusual state, so may be best to remove
// the users data from your data store and
// inform the user that you have done so
console.log('Unsubscription error: ', e);
pushButton.disabled = false;
pushButton.textContent = 'Enable Push Messages';
});
}).catch(function(e) {
console.error('Error thrown while unsubscribing from push messaging.', e);
});
});
}
การอัปเดตการสมัครใช้บริการให้เป็นปัจจุบัน
การสมัครใช้บริการอาจไม่ซิงค์กันระหว่าง FCM กับเซิร์ฟเวอร์ ตรวจสอบว่าเซิร์ฟเวอร์ของคุณแยกวิเคราะห์เนื้อหาการตอบกลับของการส่ง POST ของ FCM API โดยมองหาผลลัพธ์ error:NotRegistered
และ canonical_id
ตามที่อธิบายไว้ในเอกสารประกอบของ FCM
นอกจากนี้ การสมัครใช้บริการอาจทำให้ Service Worker กับเซิร์ฟเวอร์ไม่ตรงกันด้วย ตัวอย่างเช่น หลังจากสมัคร/ยกเลิกการสมัครใช้บริการเรียบร้อยแล้ว การเชื่อมต่อเครือข่ายที่ไม่เสถียรอาจทำให้คุณอัปเดตเซิร์ฟเวอร์ไม่ได้ หรือผู้ใช้อาจเพิกถอนสิทธิ์การแจ้งเตือน ซึ่งจะทริกเกอร์การยกเลิกการสมัครใช้บริการโดยอัตโนมัติ จัดการกรณีดังกล่าวโดยตรวจสอบผลลัพธ์ของ serviceWorkerRegistration.pushManager.getSubscription()
เป็นระยะๆ (เช่น เมื่อโหลดหน้าเว็บ) และซิงค์กับเซิร์ฟเวอร์ นอกจากนี้ คุณอาจต้องขอให้ระบบสมัครใช้บริการอีกครั้งโดยอัตโนมัติหากไม่มีการสมัครใช้บริการแล้ว และ Notification.permission == "granted"
ใน sendSubscriptionToServer()
คุณจะต้องพิจารณาวิธีจัดการคำขอเครือข่ายที่ล้มเหลวเมื่ออัปเดต endpoint
วิธีแก้ไขวิธีหนึ่งคือการติดตามสถานะของ endpoint
ในคุกกี้เพื่อดูว่าเซิร์ฟเวอร์ของคุณต้องการรายละเอียดล่าสุดหรือไม่
ขั้นตอนทั้งหมดข้างต้นจะส่งผลให้มีการใช้การรับส่งข้อความ Push บนเว็บใน Chrome 46 อย่างเต็มรูปแบบ แต่ยังคงมีฟีเจอร์ข้อมูลจำเพาะที่ทำให้สิ่งต่างๆ ง่ายขึ้น (เช่น API มาตรฐานสำหรับเรียกข้อความพุช) แต่เวอร์ชันนี้ช่วยให้คุณเริ่มสร้างข้อความ Push ไปยังเว็บแอปได้แล้ว
วิธีแก้ไขข้อบกพร่องของเว็บแอป
ในระหว่างการใช้ข้อความ Push ข้อบกพร่องจะอยู่ในที่ใดที่หนึ่งใน 2 ที่ ได้แก่ หน้าเว็บหรือ Service Worker
คุณแก้ไขข้อบกพร่องในหน้าเว็บได้โดยใช้ DevTools คุณมี 2 ทางเลือกต่อไปนี้เพื่อแก้ไขข้อบกพร่องของ Service Worker
- ไปที่ chrome://inspect > Service Workers ซึ่งมุมมองนี้จะไม่ให้ข้อมูลอื่นนอกเหนือจาก Service Worker ที่ทำงานอยู่ในปัจจุบัน
- ไปที่ chrome://serviceworker-internals จากที่นี่ คุณสามารถดูสถานะของ Service Worker และดูข้อผิดพลาด (หากมี) หน้านี้มีไว้ชั่วคราวจนกว่าเครื่องมือสำหรับนักพัฒนาเว็บจะมีชุดฟีเจอร์ที่คล้ายกัน
เคล็ดลับที่ดีที่สุดอย่างหนึ่งที่ผมสามารถมอบให้กับใครก็ตามที่ยังไม่คุ้นเคยกับโปรแกรมทำงานของบริการคือให้ใช้ช่องทำเครื่องหมาย "เปิดหน้าต่างเครื่องมือสำหรับนักพัฒนาเว็บและหยุดการดำเนินการของ JavaScript ชั่วคราวเมื่อเริ่มโปรแกรมทำงานของบริการเพื่อแก้ไขข้อบกพร่อง" ช่องทําเครื่องหมายนี้จะเพิ่มจุดหยุดพักที่จุดเริ่มต้นของ Service Worker และหยุดการดําเนินการชั่วคราว ซึ่งจะช่วยให้คุณกลับมาดําเนินการต่อหรือดูสคริปต์ Service Worker ทีละขั้นตอนเพื่อดูว่าพบปัญหาหรือไม่
หากดูเหมือนว่ามีปัญหาระหว่าง FCM กับเหตุการณ์ Push ของ Service Worker คุณจะทำสิ่งต่างๆ ได้ไม่มากนักเพื่อแก้ไขข้อบกพร่อง เนื่องจากไม่มีวิธีให้คุณดูว่า Chrome ได้รับข้อมูลใดๆ หรือไม่ สิ่งที่ต้องตรวจสอบคือคําตอบจาก FCM สำเร็จเมื่อเซิร์ฟเวอร์เรียก API จะมีหน้าตาดังนี้
{"multicast_id":1234567890,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1234567890"}]}
สังเกตคําตอบ "success": 1
หากคุณเห็นข้อผิดพลาดแทน นั่นแสดงว่ามีบางอย่างผิดปกติกับรหัสการลงทะเบียน FCM และข้อความ Push ไม่ถูกส่งไปยัง Chrome
การแก้ไขข้อบกพร่องของ Service Worker ใน Chrome สำหรับ Android
ขณะนี้การแก้ไขข้อบกพร่อง Service Worker ใน Chrome สำหรับ Android ยังไม่ชัดเจน คุณต้องไปที่ chrome://inspect ค้นหาอุปกรณ์ของคุณ แล้วมองหารายการที่มีชื่อว่า "Worker pid:...." ซึ่งมี URL ของผู้ปฏิบัติงานบริการ
UX สําหรับข้อความ Push
ทีม Chrome กำลังรวบรวมเอกสารแนวทางปฏิบัติแนะนำสำหรับ UX ของข้อความ Push รวมถึงเอกสารที่ครอบคลุมกรณีการใช้งานที่พบไม่บ่อยเมื่อทำงานกับข้อความ Push
อนาคตของการรับส่งข้อความ Push ใน Chrome และเว็บแบบเปิด
ส่วนนี้จะอธิบายรายละเอียดเล็กน้อยเกี่ยวกับส่วนต่างๆ ของ Chrome ที่เกี่ยวข้องกับการใช้งานนี้ที่คุณควรทราบ และความแตกต่างจากการใช้งานเบราว์เซอร์อื่นๆ
โปรโตคอลพุชและปลายทางสำหรับเว็บ
ข้อดีของมาตรฐาน Push API คือคุณควรใช้ Endpoint ส่งไปยังเซิร์ฟเวอร์ และส่งข้อความ Push โดยใช้ Web Push Protocol ได้
Web Push Protocol คือมาตรฐานใหม่ที่ผู้ให้บริการพุชสามารถนำไปใช้งาน ทำให้นักพัฒนาซอฟต์แวร์ไม่ต้องกังวลว่าผู้ให้บริการพุชคือใคร แนวคิดคือช่วยให้ไม่จำเป็นต้องลงชื่อสมัครใช้คีย์ API และส่งข้อมูลที่จัดรูปแบบพิเศษ เช่น ต้องทำด้วย FCM
Chrome เป็นเบราว์เซอร์แรกที่ใช้ Push API และ FCM ไม่รองรับ Web Push Protocol ซึ่งเป็นเหตุผลที่ Chrome ต้องใช้ gcm_sender_id
และคุณต้องใช้ API ที่เหลือสำหรับ FCM
เป้าหมายสุดท้ายของ Chrome คือการเปลี่ยนไปใช้ Web Push Protocol กับ Chrome และ FCM
ในระหว่างนี้ คุณต้องตรวจหาปลายทาง "https://fcm.googleapis.com/fcm/send" และจัดการแยกจากปลายทางอื่นๆ เช่น จัดรูปแบบข้อมูลเพย์โหลดในลักษณะที่เฉพาะเจาะจงและเพิ่มคีย์การให้สิทธิ์
วิธีใช้งานโปรโตคอลการพุชเว็บ
ขณะนี้ Firefox Nightly กำลังดำเนินการพุชและมีแนวโน้มที่จะเป็นเบราว์เซอร์แรกที่ใช้ Web Push Protocol
คำถามที่พบบ่อย
ข้อมูลจำเพาะอยู่ที่ไหน
https://slightlyoff.github.io/ServiceWorker/spec/service_worker/ https://w3c.github.io/push-api/ https://notifications.spec.whatwg.org/
ฉันจะป้องกันการแจ้งเตือนซ้ำได้ไหมหากตัวตนบนเว็บของฉันมีต้นทางหลายแห่ง หรือหากฉันมีทั้งการแสดงข้อมูลบนเว็บและเนทีฟ
ยังไม่มีวิธีแก้ปัญหานี้ในขณะนี้ แต่คุณสามารถติดตามความคืบหน้าใน Chromium
สถานการณ์ที่เหมาะสมคือการใช้รหัสประเภทหนึ่งสำหรับอุปกรณ์ของผู้ใช้ จากนั้นฝั่งเซิร์ฟเวอร์จะจับคู่กับรหัสการสมัครใช้บริการของแอปที่มาพร้อมเครื่องและเว็บแอป แล้วตัดสินใจว่าจะส่งข้อความพุชไปยังรหัสใด คุณอาจทำเช่นนี้ผ่านขนาดหน้าจอ รูปแบบอุปกรณ์ การแชร์คีย์ที่สร้างขึ้นระหว่างเว็บแอปกับแอปเนทีฟ แต่ทั้ง 2 วิธีนี้มีทั้งข้อดีและข้อเสีย
ทำไมฉันจึงต้องมี gcm_sender_id
ซึ่งจำเป็นต้องใช้เพื่อให้ Chrome, Opera สำหรับ Android และเบราว์เซอร์ Samsung ใช้ Firebase Cloud Messaging (FCM) API ได้ เป้าหมายคือการใช้ Web Push Protocol เมื่อมาตรฐานได้รับการสรุปแล้วและ FCM รองรับ
เหตุผลที่ไม่ควรใช้ Web Socket หรือ เหตุการณ์ที่ส่งจากเซิร์ฟเวอร์ (EventSource)
ข้อดีของการใช้ข้อความ Push คือแม้ว่าหน้าเว็บจะปิดอยู่ แต่ Service Worker ก็จะตื่นขึ้นและแสดงการแจ้งเตือนได้ Web Sockets และ EventSource จะปิดการเชื่อมต่อเมื่อหน้าเว็บหรือเบราว์เซอร์ปิด
จะเกิดอะไรขึ้นหากฉันไม่ต้องการการนำส่งเหตุการณ์ในเบื้องหลัง
หากคุณไม่ต้องการการนำส่งในเบื้องหลัง Web Sockets ก็เป็นตัวเลือกที่ยอดเยี่ยม
ฉันจะใช้ Push โดยไม่แสดงการแจ้งเตือนได้เมื่อใด (เช่น Push แบบไม่มีเสียงในเบื้องหลัง)
ยังไม่มีลำดับเวลาว่าฟีเจอร์นี้จะพร้อมใช้งานเมื่อใด แต่เรามีความตั้งใจที่จะใช้การซิงค์เบื้องหลัง และแม้ว่าจะยังไม่ได้ตัดสินใจหรือระบุข้อกำหนด แต่ก็มีบางประเด็นที่พูดถึงการเปิดใช้ Push แบบเงียบด้วยการซิงค์เบื้องหลัง
เหตุใดจึงต้องใช้ HTTPS ฉันจะแก้ปัญหานี้ในระหว่างการพัฒนาได้อย่างไร
Service Worker ต้องใช้ต้นทางที่ปลอดภัยเพื่อให้แน่ใจว่าสคริปต์ Service Worker มาจากต้นทางที่ต้องการและไม่ได้มาจากการคุกคามแบบคนกลาง ซึ่งในปัจจุบันหมายความว่าการใช้ HTTPS ในเว็บไซต์ที่เผยแพร่อยู่ แม้ว่า localhost จะทำงานในระหว่างการพัฒนาก็ตาม
การรองรับเบราว์เซอร์มีลักษณะอย่างไร
Chrome รองรับในเวอร์ชันเสถียร และ Mozilla กำลังพัฒนาฟีเจอร์นี้ใน Firefox Nightly ดูข้อมูลเพิ่มเติมได้ที่ข้อบกพร่องในการใช้งาน Push API และติดตามการใช้งานการแจ้งเตือนได้ที่นี่
ฉันจะนำการแจ้งเตือนออกหลังจากผ่านไปแล้วระยะเวลาหนึ่งได้ไหม
ขณะนี้ยังดำเนินการไม่ได้ แต่เรากำลังวางแผนที่จะเพิ่มการรองรับเพื่อดูรายการการแจ้งเตือนที่มองเห็นได้ในขณะนี้ หากคุณมี Use Case ในการกําหนดเวลาหมดอายุสําหรับการแจ้งเตือนหลังจากที่แสดงการสร้างแล้ว เราอยากทราบ Use Case นั้น โปรดเพิ่มความคิดเห็นและเราจะส่งต่อให้ทีม Chrome
หากคุณต้องการให้ระบบหยุดส่งข้อความ Push ถึงผู้ใช้หลังจากระยะเวลาหนึ่งเท่านั้น และไม่สนใจว่าการแจ้งเตือนจะแสดงอยู่นานแค่ไหน คุณสามารถใช้พารามิเตอร์ Time to Live (ttl) ของ FCM ได้ โปรดดูข้อมูลเพิ่มเติมที่นี่
การรับส่งข้อความ Push ใน Chrome มีข้อจำกัดอะไรบ้าง
ข้อจำกัดบางประการที่ระบุไว้ในโพสต์นี้
- การใช้ CCM ของ Chrome เป็นบริการ Push จะทำให้เกิดข้อกำหนดที่เป็นกรรมสิทธิ์จำนวนหนึ่ง เรากำลังดำเนินการร่วมกันเพื่อดูว่าจะยกเลิกข้อจำกัดบางรายการเหล่านี้ได้หรือไม่ในอนาคต
- คุณต้องแสดงการแจ้งเตือนเมื่อได้รับข้อความ Push
- Chrome บนเดสก์ท็อปมีข้อควรระวังว่า หาก Chrome ไม่ทำงาน ผู้ใช้ก็จะไม่ได้รับข้อความพุช ซึ่งแตกต่างจาก ChromeOS และ Android ที่ผู้ใช้จะได้รับข้อความ Push เสมอ
เราควรใช้ API สิทธิ์ไหม
Permission API มีการใช้งานใน Chrome แต่อาจไม่พร้อมใช้งานในเบราว์เซอร์บางรุ่น ดูข้อมูลเพิ่มเติมได้ที่นี่
เหตุใด Chrome จึงไม่เปิดแท็บก่อนหน้าเมื่อฉันคลิกการแจ้งเตือน
ปัญหานี้มีผลกับหน้าเว็บที่ Service Worker ไม่ได้ควบคุมอยู่ในขณะนี้เท่านั้น ดูข้อมูลเพิ่มเติมได้ที่นี่
จะเกิดอะไรขึ้นหากการแจ้งเตือนล้าสมัยตามเวลาที่อุปกรณ์ของผู้ใช้ได้รับข้อความ Push ไว้
คุณต้องแสดงการแจ้งเตือนทุกครั้งที่ได้รับข้อความ Push ในสถานการณ์ที่คุณต้องการส่งการแจ้งเตือนแต่มีประโยชน์เฉพาะระยะเวลาหนึ่งเท่านั้น คุณสามารถใช้พารามิเตอร์ "time_to_live" ใน CCM เพื่อที่ FCM จะไม่ส่งข้อความพุชหากผ่านเวลาหมดอายุ
ดูรายละเอียดเพิ่มเติมได้ที่นี่
จะเกิดอะไรขึ้นหากฉันส่งข้อความพุช 10 ข้อความแต่ต้องการให้อุปกรณ์ได้รับข้อความเพียงข้อความเดียว
FCM มีพารามิเตอร์ "collapse_key" ที่คุณสามารถใช้เพื่อบอก FCM ให้แทนที่ข้อความที่รอดำเนินการซึ่งมี "collapse_key" เดียวกันด้วยข้อความใหม่