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

כדי לשלוח הודעות, קודם צריך לקבל הרשאה מהמשתמש ואז לרשום את המכשיר שלו לשירות הודעות. כדי לעשות את זה, צריך להשתמש ב-JavaScript API כדי לקבל אובייקט PushSubscription, ואז לשלוח אותו לשרת.

ה-JavaScript API מנהל את התהליך הזה בצורה פשוטה. המדריך הזה מסביר את כל התהליך, כולל זיהוי תכונות, בקשת הרשאה וניהול תהליך ההרשמה.

זיהוי תכונות

קודם כל, בודקים אם הדפדפן תומך בהודעות דחיפה. אפשר לבדוק אם יש תמיכה בהתראות בשתי דרכים:

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

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

חובה להעביר את הערך 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

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

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

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

התרשים הבא ממחיש את השלבים האלה.

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

תרשים שממחיש איך המפתח הציבורי של שרת האפליקציה משמש ב-method‏ `subscribe()‎`.

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

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

מבחינה טכנית, ה-applicationServerKey הוא אופציונלי. עם זאת, ההטמעה הפשוטה ביותר ב-Chrome דורשת את זה, ודפדפנים אחרים עשויים לדרוש את זה בעתיד. ב-Firefox זה אופציונלי.

מפרט ה-VAPID מגדיר את מפתח שרת האפליקציה. כשאתם רואים אזכורים של מפתחות שרת אפליקציות או מפתחות VAPID, זכרו שהם זהים.

יצירת מפתחות לשרת האפליקציה

כדי ליצור קבוצה של מפתחות ציבוריים ופרטיים לשרת האפליקציה, אפשר להשתמש בשורת הפקודה של web-push וליצור מפתחות באופן הבא:

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

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

הרשאות ו-subscribe()‎

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

סקירה כללית של 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 מכיל את כל המידע שנדרש כדי לשלוח הודעות Push למשתמש הזה. אם מדפיסים את התוכן באמצעות JSON.stringify(), רואים את הדברים הבאים:

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

‫endpoint היא כתובת ה-URL של שירות ההתראות. כדי להפעיל הודעת Push, שולחים בקשת POST לכתובת ה-URL הזו.

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

שרת 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);
    });
}

שאלות נפוצות

ריכזנו כאן כמה שאלות נפוצות:

האם אפשר לשנות את שירות ההתראות שבו הדפדפן משתמש?

לא, הדפדפן בוחר את שירות ההתראות. כמו שמוסבר במסמך הזה לגבי הקריאה ל-subscribe(), הדפדפן שולח בקשות לאחזור מהרשת לשירות ההתראות כדי לאחזר את הפרטים שמרכיבים את ה-PushSubscription.

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

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

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

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

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

השלבים הבאים

Codelabs