השלב הראשון הוא לקבל הרשאה מהמשתמש לשלוח הודעות בדחיפה ואז נוכל
מצביעים על PushSubscription
.
ה-API של JavaScript כדי לעשות זאת הוא ישיר יחסית, אז בואו בתהליך הלוגיקה.
זיהוי תכונות
קודם כל, אנחנו צריכים לבדוק אם הדפדפן הנוכחי באמת תומך בהעברת הודעות דחיפה. אנחנו יכולים לבדוק אם: הודעות Push נתמכות בשתי בדיקות פשוטות.
- מחפשים את האפשרות serviceWorker ב-navigator.
- מחפשים את PushManager ב-window.
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 וגם הודעות דחיפה, תמיד כדאי להציג לזיהוי לשפר בהדרגה.
רישום Service Worker
בעזרת התכונה הזו אנחנו יודעים שנתמכים גם ב-Service Workers וגם ב-Push. השלב הבא היא 'לרשום' הוא קובץ השירות (service worker).
כשאנחנו רושמים קובץ שירות (service worker), אנחנו אומרים לדפדפן איפה נמצא קובץ ה-Service Worker. הקובץ הוא עדיין JavaScript בלבד, אבל הדפדפן יעניק לו גישה ל-Service Worker ממשקי API, כולל Push. ליתר דיוק, הדפדפן מריץ את הקובץ ב-Service Worker. הסביבה.
כדי לרשום קובץ שירות (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()
:
מורידים את קובץ Service Worker.
מריצים את ה-JavaScript.
אם הכול פועל כמו שצריך ואין שגיאות, ההבטחה מוחזרת על ידי
register()
פותרים את הבעיה. אם יש שגיאות מכל סוג שהוא, ההבטחה תידחה.
אם
register()
נדחה, צריך לבדוק שוב אם יש ב-JavaScript שגיאות הקלדה או שגיאות בכלי הפיתוח ל-Chrome.
כשהפונקציה register()
מצליחה, היא מחזירה ServiceWorkerRegistration
. נשתמש בכתובת הזו
רישום כדי לגשת ל-PushManager API.
תאימות דפדפנים של PushManager API
נשלחה בקשה להרשאה
רשמנו את ה-Service Worker ואנחנו מוכנים לרשום את המשתמש. השלב הבא הוא הרשאה מהמשתמש לשלוח אליו הודעות בדחיפה.
ה-API לקבלת הרשאה הוא פשוט יחסית, החיסרון הוא ה-API שונה לאחרונה מביצוע קריאה חוזרת (callback) לחזרה של Promise. היא שלא הצלחנו לזהות איזו גרסה של ה-API הוטמעה על ידי ולכן צריך להטמיע את שניהם ולטפל בשניהם.
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()
השיטה הזו תציג למשתמש הנחיה:
אחרי שהמשתמש יגיב לבקשת ההרשאה על ידי לחיצה על 'אישור', 'חסימה' או פשוט סגירה,
נציג את התוצאה כמחרוזת: '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;
});
}
כשקוראים ל-method subscribe()
, אנחנו מעבירים באובייקט options, שמורכב משניהם
פרמטרים נדרשים ופרמטרים אופציונליים.
נסתכל על כל האפשרויות שנוכל להעביר.
אפשרויות userViewOnly
כאשר דחיפה נוספה לראשונה לדפדפנים, הייתה חוסר ודאות לגבי השאלה אם על מפתחים לא ניתן לשלוח הודעה ולא להציג התראה. מצב כזה נקרא בדרך כלל מצב שקט לדחוף, כי המשתמש לא יודע שמשהו קרה ברקע.
החשש היה שמפתחים עלולים לעשות פעולות מזיקות, כמו לעקוב אחר מיקום של משתמש באופן שוטף, ללא ידיעת המשתמש.
כדי להימנע מהתרחיש הזה ולתת לכותבי המפרט זמן לבדוק מהי הדרך הטובה ביותר לתמוך בכך
התכונה userVisibleOnly
נוספה והעברת ערך של true
היא סימבולי
עם הדפדפן על כך שאפליקציית האינטרנט תציג התראה בכל פעם שנשלחת דחיפה
התקבלו (כלומר לא דחיפה שקטה).
כרגע חובה להעביר את הערך true
. אם לא כוללים את
מפתח או כרטיס של userVisibleOnly
ב-false
תתקבל השגיאה הבאה:
Chrome תומך כרגע ב-Push API רק עבור מינויים
הודעות גלויות למשתמש. כדי לציין זאת, אפשר להתקשר
יש גם אפשרות pushManager.subscribe({userVisibleOnly: true})
. צפייה
אפשר לקרוא פרטים נוספים בכתובת https://goo.gl/yqv4Q4.
נראה שדחיפה שקטה גורפת אף פעם לא תיושם ב-Chrome. במקום זאת, מחברים של מפרטים בודקים את המושג 'ממשק API לתקציב', שיאפשר לאפליקציות אינטרנט מספר ההודעות השקטות בדחיפה, על סמך השימוש באפליקציית אינטרנט.
אפשרות applicationServerKey
הזכרנו בקצרה "מפתחות של שרת אפליקציות" בסעיף הקודם. "יישום מפתחות שרת" משמשים בשירות Push כדי לזהות את האפליקציה שרושם משתמש לוודא שאותה אפליקציה מעבירה הודעות למשתמש.
מפתחות של שרת אפליקציות הם זוג מפתחות ציבוריים ופרטיים ייחודיים לאפליקציה שלכם. צריך לשמור את המפתח הפרטי כסוד לאפליקציה שלך, ואפשר לשתף את המפתח הציבורי בחופשיות.
האפשרות applicationServerKey
שמועברת לקריאה של subscribe()
היא ציבורית של האפליקציה
מקש. הדפדפן מעביר את הכתובת הזו לשירות דחיפה כשהוא נרשם למשתמש, כלומר
השירות יכול לקשר את המפתח הציבורי של האפליקציה ל-PushSubscription
של המשתמש.
התרשים הבא ממחיש את השלבים האלה.
- אפליקציית האינטרנט שלך נטענת בדפדפן ומבצעת קריאה ל-
subscribe()
, ומעבירה את התוכן הציבורי שלך מפתח שרת האפליקציות. - לאחר מכן הדפדפן שולח בקשת רשת לשירות Push שיייצר נקודת קצה (endpoint), לשייך את נקודת הקצה למפתח הציבורי של האפליקציות ולהחזיר את נקודת הקצה בדפדפן.
- הדפדפן יוסיף את נקודת הקצה הזו ל-
PushSubscription
, שמוחזר דרך הבטחה שלsubscribe()
.
כשרוצים לשלוח הודעה בדחיפה בשלב מאוחר יותר, צריך ליצור כותרת הרשאה שיכיל מידע חתום באמצעות המפתח הפרטי של שרת האפליקציה שלך. כאשר שירות ה-Push מקבל בקשה לשלוח הודעה, והוא יכול לאמת את כותרת ההרשאה החתומה הזו באמצעות חיפוש המפתח הציבורי שמקושר לנקודת הקצה שמקבלת את הבקשה. אם החתימה היא תקין, שירות ה-Push יודע שהוא אמור להגיע משרת האפליקציות עם מפתח פרטי תואם. זהו אמצעי אבטחה שמונע מאנשים אחרים לשלוח הודעות הודעות למשתמשים של אפליקציה.
מבחינה טכנית, השדה applicationServerKey
הוא אופציונלי. אבל בדרך כלל,
ב-Chrome נדרש קוד, ויכול להיות שדפדפנים אחרים ידרשו זאת
לעתיד. ב-Firefox, ההגדרה הזו אופציונלית.
המפרט שמגדיר מה צריך להיות המפתח של שרת האפליקציות הוא מפרט VAPID. בכל פעם שקוראים משהו שקשור ל"מפתחות של שרת אפליקציות" או "מפתחות VAPID", צריך רק לזכור שהם אותו הדבר.
איך יוצרים מפתחות של שרת אפליקציות
אפשר ליצור קבוצה ציבורית ופרטית של מפתחות לשרת אפליקציות בכתובת web-push-codelab.glitch.me או להשתמש שורת הפקודה web-push כדי ליצור מפתחות באמצעות הפעולות הבאות:
$ npm install -g web-push
$ web-push generate-vapid-keys
צריך ליצור את המפתחות האלה רק פעם אחת עבור האפליקציה, אבל חשוב לוודא מפתח פרטי פרטי. (כן, בדיוק אמרתי את זה.)
הרשאות ו-Subscribe()
יש תופעת לוואי אחת של קריאה לsubscribe()
. אם לאפליקציית האינטרנט אין הרשאות עבור
מוצגות התראות בזמן ביצוע השיחה אל subscribe()
, הדפדפן יבקש את
עבורך. זו אפשרות שימושית אם ממשק המשתמש עובד עם התהליך הזה, אבל רוצים
שליטה (ולדעתי רוב המפתחים כן), נשארים עם Notification.requestPermission()
API
שבהם השתמשנו קודם.
מה זה PushSubscription?
אנחנו קוראים לפונקציה 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
מכיל את הערכים שמשמשים להצפנה של נתוני הודעות שנשלחו באמצעות הודעת Push.
(נדון בכך בהמשך בקטע זה).
חידוש המינוי באופן קבוע כדי למנוע תפוגה
כשנרשמים לקבלת התראות, מקבלים בדרך כלל PushSubscription.expirationTime
מתוך null
. בתיאוריה, המשמעות היא שתוקף המינוי לא יפוג אף פעם (בניגוד לזמן שבו מקבלים DOMHighResTimeStamp
, שמציין את נקודת הזמן המדויקת שבה המינוי בתוקף). עם זאת, במקרים רבים דפדפנים עדיין מאפשרים לתוקף מינוי לפוג, למשל אם לא התקבלו התראות במשך זמן רב יותר, או אם הדפדפן מזהה שהמשתמש לא משתמש באפליקציה שיש לה הרשאה לשליחת התראות. דפוס אחד למניעת מצבים כאלה הוא הרשמה מחדש למשתמש בכל פעם שמתקבלת התראה, כפי שמוצג בקטע הקוד הבא. לשם כך, עליך לשלוח התראות בתדירות גבוהה מספיק כדי שהמינוי לא יפוג באופן אוטומטי. כמו כן, עליך לשקול בזהירות רבה את היתרונות והחסרונות של קבלת התראה לגיטימית מפני הפצת ספאם למשתמש באופן לא מכוון, רק כדי שתוקף המינוי לא יפוג. בסופו של דבר, אל תנסו להילחם בדפדפן במאמציו להגן על המשתמש מפני מינויים להתראות שנשכחו במשך זמן רב.
/* In the Service Worker. */
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
// Display notification or handle data
// Example: show a notification
const title = 'New Notification';
const body = 'You have new updates!';
const icon = '/images/icon.png';
const tag = 'simple-push-demo-notification-tag';
event.waitUntil(
self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag
})
);
// Attempt to resubscribe after receiving a notification
event.waitUntil(resubscribeToPush());
});
function resubscribeToPush() {
return self.registration.pushManager.getSubscription()
.then(function(subscription) {
if (subscription) {
return subscription.unsubscribe();
}
})
.then(function() {
return self.registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY_HERE')
});
})
.then(function(subscription) {
console.log('Resubscribed to push notifications:', subscription);
// Optionally, send new subscription details to your server
})
.catch(function(error) {
console.error('Failed to resubscribe:', error);
});
}
שליחת מינוי לשרת שלך
לאחר שיהיה לכם מינוי Push תוכלו לשלוח אותו לשרת שלכם. זה תלוי בך
לעשות את זה, אבל טיפ קטנטן הוא להשתמש ב-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
בשרת שלנו, אפשר לשלוח את המשתמש
הודעה כלשהי מתי שנרצה.
חידוש המינוי באופן קבוע כדי למנוע תפוגה
כשנרשמים לקבלת התראות, מקבלים בדרך כלל PushSubscription.expirationTime
מתוך null
. בתיאוריה, המשמעות היא שתוקף המינוי לא יפוג אף פעם (בניגוד לזמן שבו מקבלים DOMHighResTimeStamp
, שמציין את נקודת הזמן המדויקת שבה המינוי בתוקף). עם זאת, במקרים רבים דפדפנים עדיין מאפשרים לתוקף מינוי לפוג, למשל אם לא התקבלו התראות במשך זמן רב או אם הדפדפן מזהה שהמשתמש לא משתמש באפליקציה שיש לה את ההרשאה לשליחת התראות. דפוס אחד למניעת מצבים כאלה הוא הרשמה מחדש למשתמש בכל פעם שמתקבלת התראה, כפי שמוצג בקטע הקוד הבא. לשם כך, עליך לשלוח התראות בתדירות גבוהה מספיק כדי שתוקף המינוי לא יפוג באופן אוטומטי. בנוסף, עליך לשקול בזהירות רבה את היתרונות והחסרונות של צורכי התראה לגיטימית מפני הפצת ספאם של המשתמש, רק כדי שתוקף המינוי לא יפוג. בסופו של דבר, אל תנסו להילחם בדפדפן במאמציו להגן על המשתמש מפני מינויים להתראות שנשכחו במשך זמן רב.
/* In the Service Worker. */
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
// Display notification or handle data
// Example: show a notification
const title = 'New Notification';
const body = 'You have new updates!';
const icon = '/images/icon.png';
const tag = 'simple-push-demo-notification-tag';
event.waitUntil(
self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag
})
);
// Attempt to resubscribe after receiving a notification
event.waitUntil(resubscribeToPush());
});
function resubscribeToPush() {
return self.registration.pushManager.getSubscription()
.then(function(subscription) {
if (subscription) {
return subscription.unsubscribe();
}
})
.then(function() {
return self.registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY_HERE')
});
})
.then(function(subscription) {
console.log('Resubscribed to push notifications:', subscription);
// Optionally, send new subscription details to your server
})
.catch(function(error) {
console.error('Failed to resubscribe:', error);
});
}
שאלות נפוצות
כמה שאלות נפוצות שאנשים שאלו בשלב זה:
האם אפשר לשנות את שירות ה-Push שמשמש את הדפדפן?
לא. שירות ה-Push נבחר על ידי הדפדפן וכפי שראינו עם
שיחת subscribe()
, הדפדפן ישלח בקשות רשת לשירות ה-Push
כדי לאחזר את הפרטים שמרכיבים את ה-PushSubscription.
כל דפדפן משתמש ב-Push Service אחר, נכון?
כל שירותי ה-Push יצפו באותו API.
ה-API הנפוץ הזה נקרא פרוטוקול דחיפה באינטרנט ומתאר את בקשת הרשת האפליקציה שלך תצטרך לבצע כדי להפעיל הודעת דחיפה.
אם נרשמתי למשתמש במחשב, האם הוא נרשם גם דרך הטלפון?
לצערי, לא. המשתמש חייב להירשם ל-push בכל דפדפן שהוא רוצה קבלת הודעות מופעלות. חשוב גם לציין שהדבר ידרוש שהמשתמש מעניק הרשאה בכל מכשיר.
מה השלב הבא?
- סקירה כללית של התראות באינטרנט
- כיצד פועלת דחיפת הודעות
- הרשמה של משתמש
- הרשאות UX
- שליחת הודעות באמצעות ספריות דחיפת הודעות מדפי אינטרנט
- פרוטוקול דחיפה באינטרנט
- טיפול באירועי Push
- הצגת התראה
- אופן ההתנהלות של ההתראות
- דפוסים נפוצים של התראות
- שאלות נפוצות בנושא התראות
- בעיות נפוצות ובאגים בדיווח