الاشتراك في قناة مستخدم

تتمثل الخطوة الأولى في الحصول على إذن من المستخدم لإرسال رسائل فورية إليه، وبعد ذلك يمكننا الحصول على PushSubscription.

واجهة برمجة تطبيقات JavaScript لإجراء ذلك واضحة ومباشرة، لذلك لننتقل إلى من خلال التدفق المنطقي.

رصد الميزات

نحتاج أولاً إلى التحقّق ممّا إذا كان المتصفّح الحالي يتيح رسائل الدفع. يمكننا التحقق مما إذا يتم دعمها من خلال عمليتي تحقق بسيطتين.

  1. ابحث عن serviceWorker على navigator.
  2. ابحث عن 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;
}

في الوقت الذي يزداد فيه دعم المتصفح بسرعة لكل من مشغّل الخدمات من الأفضل دائمًا تمييز رسائل الدفع تحسينها تدريجيًا

تسجيل مشغّل الخدمات

نعرف من خلال الكشف عن الميزة أن كلاً من مشغّلي الخدمة وخدمة Push معتمد. الخطوة التالية هي "التسجيل" عامل الخدمة.

عندما نسجِّل مشغّل خدمات، نخبر المتصفّح بمكان ملف مشغّل الخدمات. لا يزال الملف عبارة عن JavaScript فقط، لكن المتصفح "سيمنحه إمكانية الوصول" إلى مشغّل الخدمات واجهات برمجة التطبيقات، بما في ذلك Push. للدقة بشكل أكبر، يشغِّل المتصفح الملف في مشغّل خدمات. محددة.

لتسجيل مشغّل خدمات، يُرجى الاتصال بالرقم 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.js. المتصفِّح وراء الكواليس اتّخاذ الخطوات التالية بعد الاتصال بـ register():

  1. نزِّل ملف مشغّل الخدمات.

  2. شغِّل JavaScript.

  3. في حال تشغيل كل البيانات بشكل صحيح ولم تكن هناك أخطاء، سيتم عرض الوعد من قِبل register(). سيتم حله. في حال وجود أخطاء من أي نوع، سيتم رفض الوعد.

في حال رفض register()، يُرجى مراجعة JavaScript بحثًا عن أخطاء إملائية أو أخطاء في "أدوات مطوري البرامج في Chrome".

عندما يتم حلّ الخطأ register()، يتم عرض رمز الاستجابة ServiceWorkerRegistration. سنستخدم هذا التسجيل للوصول إلى PushManager API.

توافق المتصفّح مع واجهة برمجة تطبيقات PushManager

دعم المتصفح

  • Chrome: 42.
  • الحافة: 17.
  • Firefox: 44
  • Safari: 16-

المصدر

جارٍ طلب الإذن

لقد سجّلنا مشغّل الخدمات لدينا وجاهزنا لتسجيل اشتراك المستخدم، والخطوة التالية هي الحصول إذن من المستخدم لإرسال رسائل الدفع إليه.

تتميز واجهة برمجة التطبيقات للحصول على إذن بالبساطة نسبيًا، ولكن يتمثل الجانب السلبي في واجهة برمجة التطبيقات تم تغييرها مؤخرًا من معاودة الاتصال إلى عرض وعد. تشير رسالة الأشكال البيانية هو أنه لا يمكننا تحديد إصدار واجهة برمجة التطبيقات الذي يتم تنفيذه من خلال في المتصفح، لذا عليكم تطبيق كليهما والتعامل معهما.

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

وبعد تسجيل مشغّل الخدمات وحصولنا على إذن، يمكننا اشتراك مستخدم عن طريق يَتِمُّ الْآنَ الِاتِّصَالْ بِـ 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، يتكون من المعلمات المطلوبة والاختيارية.

لنلقِ نظرة على جميع الخيارات التي يمكننا تقديمها.

خيارات userمرئي فقط

عند إضافة الإشعارات الفورية إلى المتصفحات، لم يكن هناك شك حيال ما إذا كان يجب على المطورين قادرًا على إرسال رسالة فورية وعدم إظهار إشعار. يشار إلى هذا عادةً على أنه صامت الدفع، وذلك لأنّ المستخدم لا يعرف بحدوث شيء في الخلفية.

كانت المخاوف هي أنّ المطوّرين قد يقومون بأمور سيئة مثل تتبع الموقع الجغرافي لأحد المستخدمين على موقع بدون أن يعرف المستخدم.

لتجنب هذا السيناريو ومنح مؤلفي المواصفات وقتًا للتفكير في أفضل السبل لدعم هذا تمت إضافة الخيار userVisibleOnly وأصبح إدخال القيمة true رمزيًا مع المتصفح على أن تطبيق الويب سيعرض إشعارًا في كل مرة تم استلامها (أي بدون دفع صامت).

في الوقت الحالي، يجب تمرير قيمة تبلغ true. إذا لم تقم بتضمين الحقل سيظهر لك الخطأ التالي عند الضغط على مفتاح أو بطاقة واحدة (userVisibleOnly) في false:

لا يتيح Chrome حاليًا سوى Push API للاشتراكات التي ستنتج عن ذلك الرسائل المرئية للمستخدم. يمكنك الإشارة إلى ذلك عن طريق الاتصال pushManager.subscribe({userVisibleOnly: true}) بدلاً من ذلك. عرض https://goo.gl/yqv4Q4 لمزيد من التفاصيل

يبدو حاليًا أنّه لن يتم مطلقًا تنفيذ الدفع الصامت الشامل في Chrome. بدلاً من ذلك، يستكشف مؤلفو المواصفات فكرة واجهة برمجة تطبيقات الميزانية التي ستسمح لتطبيقات الويب عدد الرسائل الفورية الصامتة استنادًا إلى استخدام تطبيق ويب.

خيار applicationServerKey

ذكرنا بإيجاز "مفاتيح خادم التطبيقات" في القسم السابق. "التطبيق مفاتيح الخادم" تستخدمها خدمة الدفع لتحديد اشتراك التطبيق لمستخدم التأكد من أن التطبيق نفسه يراسل هذا المستخدم.

مفاتيح خادم التطبيقات هي زوج من المفاتيح العامة والخاصة تكون فريدة لتطبيقك. يجب أن يبقى المفتاح الخاص سريًا للتطبيق ويمكن مشاركة المفتاح العام بحرية.

الخيار applicationServerKey الذي يتم تمريره إلى مكالمة subscribe() هو الخيار المتاح للجميع للتطبيق. المفتاح. ويمرر المتصفح هذا إلى خدمة الدفع عند اشتراك المستخدم، ما يعني الدفع ربط المفتاح العام لتطبيقك بـ PushSubscription للمستخدم.

يوضح الرسم التخطيطي التالي هذه الخطوات.

  1. يتم تحميل تطبيق الويب في متصفّح عند الاتصال بـ subscribe()، ويتم عرضه بشكل علني. مفتاح خادم التطبيقات.
  2. ثم يقوم المتصفح بطلب الشبكة إلى خدمة الدفع التي ستنشئ نقطة نهاية، نربط نقطة النهاية هذه بالمفتاح العام للتطبيقات وإعادة نقطة النهاية إلى المتصفح.
  3. سيضيف المتصفح نقطة النهاية هذه إلى PushSubscription، والتي يتم عرضها عبر وعد subscribe().

صورة توضيحية لمفتاح خادم التطبيق العام يُستخدَم في كلمة "الاشتراك"
.

عندما تريد إرسال رسالة فورية في وقت لاحق، عليك إنشاء عنوان تفويض. الذي يحتوي على معلومات موقّعة باستخدام المفتاح الخاص لخادم التطبيقات الخاص بك. عندما تتلقّى خدمة 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()، سيطلب المتصفّح الأذونات المتاحة لك. يكون هذا مفيدًا إذا كانت واجهة المستخدم تعمل مع هذا التدفق، ولكن إذا كنت تريد المزيد (وأعتقد أن معظم المطوّرين سيفعلون ذلك)، بل يلتزمون بـ 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 على القيم المستخدمة لتشفير بيانات الرسالة المُرسَلة مع رسالة الدفع. (سنناقشه لاحقًا في هذا القسم).

إعادة الاشتراك بانتظام لتجنّب انتهاء الصلاحية

عند الاشتراك في ميزة الإشعارات الفورية، غالبًا ما تتلقّى 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);
    });
}

الأسئلة الشائعة

إليك بعض الأسئلة الشائعة التي طرحها المستخدمون في هذه المرحلة:

هل يمكنني تغيير خدمة الإرسال التي يستخدمها المتصفح؟

لا، حيث يتم تحديد خدمة الإرسال بواسطة المتصفح وكما رأينا مع اتصال subscribe()، سيُجري المتصفِّح طلبات الشبكة إلى خدمة الإرسال المباشر لاسترداد التفاصيل التي يتألف منها PushSubscription.

يستخدم كل متصفح خدمة Push Service مختلفة، أليس هناك واجهات برمجة تطبيقات مختلفة؟

يجب أن تتوقع جميع خدمات الدفع واجهة برمجة التطبيقات نفسها.

تُسمى واجهة برمجة التطبيقات الشائعة هذه بروتوكول Web Push ويصف طلب الشبكة سيحتاج تطبيقك إلى إجرائه لعرض رسالة فورية.

في حال اشتراك مستخدم على كمبيوتر سطح المكتب، هل يكون مشتركًا على هاتفه أيضًا؟

لا يهمّني ذلك. يجب على المستخدم التسجيل للدفع على كل متصفح يريد تلقّي الرسائل على ومن الجدير بالذكر أيضًا أن هذا سيتطلب المستخدم الذي يمنح إذنًا على كل جهاز.

الخطوات التالية

الدروس التطبيقية حول الترميز