הרשמה של משתמש לקבלת התראות

כדי לשלוח הודעות פוש, קודם צריך לקבל הרשאה מהמשתמש ואז לרשום את המכשיר שלו לשירות פוש. לשם כך, משתמשים ב-JavaScript API כדי לקבל אובייקט 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 ובשליחת הודעות פוש. בשלב הבא, רושמים את קובץ השירות (service worker).

כשרושמים service worker, מציינים לדפדפן את המיקום של קובץ ה-service worker. הקובץ הוא קובץ JavaScript, אבל הדפדפן מעניק לו גישה לממשקי API של Service Worker, כולל הודעות פוש. באופן ספציפי, הדפדפן מריץ את הקובץ בסביבת 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(), הדפדפן מבצע את השלבים הבאים:

  1. מורידים את קובץ ה-service worker.

  2. מריצים את ה-JavaScript.

  3. אם הקובץ פועל בצורה תקינה ללא שגיאות, ההבטחה שמוחזרת על ידי register() מתממשת. אם מתרחשות שגיאות, ההבטחה נדחית.

הערה: אם register() נדחה, כדאי לבדוק את ה-JavaScript בכלי הפיתוח ל-Chrome כדי לוודא שאין בו שגיאות הקלדה או שגיאות אחרות.

כש-register() נפתר, הוא מחזיר ServiceWorkerRegistration. הרישום הזה מאפשר גישה אל PushManager API.

תאימות דפדפן ל-PushManager API

Browser Support

  • Chrome: 42.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 16.

Source

שליחת בקשה להרשאה

אחרי שרושמים את ה-service worker ומקבלים הרשאה, צריך לקבל מהמשתמש הרשאה לשליחת התראות פוש.

ממשק ה-API לקבלת הרשאה הוא פשוט. עם זאת, לאחרונה בוצע שינוי ב-API, והוא מחזיר 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() מציגה למשתמש הצעה לפעולה:

ההודעה לבקשת הרשאה מוצגת ב-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

כששילבנו לראשונה הודעות פוש בדפדפנים, מפתחים לא היו בטוחים לגבי שליחת הודעות פוש בלי להציג התראה. ההודעה הזו נקראת בדרך כלל 'הודעת פוש שקטה' כי המשתמש לא יודע שהתרחש אירוע ברקע.

החשש היה שמפתחים יוכלו לעקוב אחרי מיקום המשתמש באופן רציף בלי ידיעתו.

כדי למנוע את התרחיש הזה ולאפשר לכותבי המפרט לשקול איך הכי טוב לתמוך בתכונה הזו, נוספה האפשרות userVisibleOnly. העברת הערך true היא הסכמה סמלית עם הדפדפן לכך שאפליקציית האינטרנט תציג התראה בכל פעם שהיא מקבלת הודעת פוש (כלומר, לא פוש שקט).

חובה להעביר ערך של true. אם לא כוללים את המפתח userVisibleOnly או לא מעבירים את false, מוצגת השגיאה הבאה:

Chrome currently only supports the Push API for subscriptions that will result
in user-visible messages. You can indicate this by calling
`pushManager.subscribe({userVisibleOnly: true})` instead. See
[https://goo.gl/yqv4Q4](https://goo.gl/yqv4Q4) for more details.

‫Chrome תומך ב-Push API רק עבור מינויים שמובילים להודעות שגלויות למשתמשים. כדי לציין זאת, צריך להתקשר למספר pushManager.subscribe({userVisibleOnly: true}). מידע נוסף זמין בכתובת https://goo.gl/yqv4Q4.

נראה שלא תהיה הטמעה של הודעות פוש שקטות ב-Chrome. במקום זאת, מחברי המפרט בודקים אפשרות ליצור API של תקציב שיאפשר לאפליקציות אינטרנט לשלוח מספר מסוים של הודעות פוש שקטות על סמך השימוש באפליקציית האינטרנט.

האפשרות applicationServerKey

במאמר הזה הוזכרו בעבר מפתחות של שרת אפליקציות. שירות Push משתמש במפתחות של שרת האפליקציה כדי לזהות את האפליקציה שהמשתמש נרשם אליה, וכדי לוודא שאותן הודעות אפליקציה מגיעות למשתמש.

מפתחות שרת האפליקציה הם זוג מפתחות פרטי וציבורי שייחודיים לאפליקציה שלכם. שומרים את הסוד של המפתח הפרטי באפליקציה ומשתפים את המפתח הציבורי באופן חופשי.

האפשרות applicationServerKey שמועברת לקריאה subscribe() היא המפתח הציבורי של האפליקציה. הדפדפן מעביר את המפתח הזה לשירות Push כשמצרפים את המשתמש, וכך שירות ה-Push יכול לקשר את המפתח הציבורי של האפליקציה ל-PushSubscription של המשתמש.

התרשים הבא מדגים את השלבים האלה.

  1. טוענים את אפליקציית האינטרנט בדפדפן ומפעילים את subscribe(), תוך העברת המפתח הציבורי של שרת האפליקציות.
  2. לאחר מכן הדפדפן שולח בקשה לרשת לשירות Push, שמפיק נקודת קצה, משייך את נקודת הקצה הזו למפתח הציבורי של האפליקציה ומחזיר את נקודת הקצה לדפדפן.
  3. הדפדפן מוסיף את נקודת הקצה הזו ל-PushSubscription, שההבטחה subscribe() מחזירה.

תרשים שממחיש איך משתמשים במפתח הציבורי של שרת האפליקציה בשיטה subscribe().

כששולחים הודעת פוש, צריך ליצור כותרת Authorization שמכילה מידע שחתום באמצעות המפתח הפרטי של שרת האפליקציה. כששירות הדחיפה מקבל בקשה לשליחת הודעת דחיפה, הוא מאמת את כותרת ההרשאה החתומה הזו על ידי חיפוש המפתח הציבורי שמקושר לנקודת הקצה שמקבלת את הבקשה. אם החתימה תקפה, שירות הדחיפה יודע שהבקשה הגיעה משרת האפליקציות עם המפתח הפרטי התואם. זהו אמצעי אבטחה שמונע מאנשים אחרים לשלוח הודעות למשתמשים באפליקציה שלכם.

דיאגרמה שממחישה איך משתמשים במפתח השרת של האפליקציה הפרטית כששולחים הודעה.

מבחינה טכנית, התג 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 של שירות ה-Push. כדי להפעיל הודעת פוש, שולחים בקשת 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.');
      }
    });
}

שרת Node.js מקבל את הבקשה הזו ושומר את הנתונים במסד נתונים לשימוש מאוחר יותר.

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. כפי שצוין במסמך הזה לגבי הקריאה subscribe(), הדפדפן שולח בקשות לרשת לשירות הדחיפה כדי לאחזר את הפרטים שמרכיבים את PushSubscription.

האם שירותי Push שונים משתמשים בממשקי API שונים?

כל שירותי ה-Push מצפים לאותו API.

ממשק ה-API הנפוץ הזה, שנקרא Web Push Protocol, מתאר את הבקשה לרשת שהאפליקציה שולחת כדי להפעיל הודעת פוש.

אם רושמים משתמש כמנוי במחשב, האם הוא רשום כמנוי גם בטלפון?

לא. משתמש צריך להירשם לקבלת הודעות פוש בכל דפדפן שבו הוא רוצה לקבל הודעות. בנוסף, המשתמש צריך להעניק הרשאה בכל מכשיר.

השלבים הבאים

Codelabs