مشترک کردن کاربر در اعلان‌های لحظه‌ای

برای ارسال پیام‌های لحظه‌ای، ابتدا باید از کاربر اجازه بگیرید و سپس دستگاه او را در سرویس لحظه‌ای مشترک کنید. این شامل استفاده از JavaScript API برای دریافت شیء PushSubscription است که سپس آن را به سرور خود ارسال می‌کنید.

میانای برنامه‌سازی کاربردی جاوا اسکریپت این فرایند را به‌طور مستقیم مدیریت می‌کند. این راهنما کل جریان را توضیح می‌دهد، ازجمله تشخیص ویژگی، درخواست اجازه، و مدیریت فرایند اشتراک.

تشخیص ویژگی

ابتدا بررسی کنید که آیا مرورگر از پیام‌رسانی فشاری پشتیبانی می‌کند یا نه. با دو بررسی می‌توانید پشتیبانی از ارسال را بررسی کنید:

  • 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;
}

درحالی‌که پشتیبانی مرورگر از کارگر سرویس و پیام‌رسانی فشاری درحال افزایش است، همیشه هر دو ویژگی را شناسایی کنید و به‌تدریج برنامه خود را بهبود ببخشید.

ثبت‌نام عامل خدماتی

پس‌از تشخیص ویژگی، می‌دانید که از سرویس‌کارها و پیام‌رسانی لحظه‌ای پشتیبانی می‌شود. سپس، کارگر خدمات خود را ثبت کنید.

وقتی یک کارگر خدماتی را ثبت می‌کنید، مکان فایل کارگر خدماتی را به مرورگر می‌گویید. این فایل یک فایل جاوا اسکریپت است، اما مرورگر به آن اجازه دسترسی به میاناهای برنامه‌سازی کاربردی کارگر سرویس، ازجمله پیام‌رسانی لحظه‌ای، را می‌دهد. به‌طور مشخص، مرورگر فایل را در محیط کارگر سرویس اجرا می‌کند.

برای ثبت کردن کارمند خدمات، 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. جاوا اسکریپت را اجرا کنید.

  3. اگر فایل به‌درستی و بدون خطا اجرا شود، وعده برگردانده‌شده توسط register() حل می‌شود. اگر خطایی رخ دهد، promise رد می‌شود.

توجه: اگر register() رد شد، جاوا اسکریپت خود را برای غلط‌های املایی یا خطاها در Chrome DevTools بررسی کنید.

وقتی register() حل‌وفصل شود، ServiceWorkerRegistration را برمی‌گرداند. از این ثبت برای دسترسی به PushManager API استفاده می‌کنید.

سازگاری مرورگر PushManager API

Browser Support

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

Source

درخواست اجازه

پس‌از ثبت کردن کارگر سرویس و دریافت اجازه، از کاربر اجازه بگیرید تا پیام‌های لحظه‌ای ارسال کنید.

میانای برنامه‌سازی کاربردی برای دریافت اجازه ساده است. اما API اخیراً از دریافت callback به برگرداندن Promise تغییر کرده است. چون نمی‌توانید تعیین کنید که مرورگر کدام نسخه میانای برنامه‌سازی کاربردی را پیاده‌سازی می‌کند، باید هر دو نسخه را پیاده‌سازی و مدیریت کنید.

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 را که شامل پارامترهای الزامی و اختیاری است ارسال می‌کنید.

این بخش گزینه‌هایی را که می‌توانید ارسال کنید شرح می‌دهد.

گزینه‌های 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 پیاده‌سازی نخواهد شد. درعوض، نویسندگان مشخصات درحال بررسی یک میانای برنامه‌سازی کاربردی بودجه هستند که به برنامه‌های وب اجازه می‌دهد تعداد مشخصی پیام لحظه‌ای بی‌صدا براساس استفاده از برنامه وب ارسال کنند.

گزینه applicationServerKey

این سند قبلاً به کلیدهای سرور برنامه اشاره کرده است. سرویس Push از کلیدهای سرور برنامه برای شناسایی برنامه‌ای که کاربر در آن مشترک شده است و برای اطمینان از اینکه همان برنامه به کاربر پیام می‌دهد استفاده می‌کند.

کلیدهای سرور برنامه یک جفت کلید عمومی و خصوصی هستند که برای برنامه شما منحصربه‌فرد هستند. کلید خصوصی را برای برنامه‌تان مخفی نگه دارید و کلید عمومی را آزادانه هم‌رسانی کنید.

گزینه applicationServerKey که به تماس subscribe() منتقل شده است، کلید عمومی برنامه شما است. مرورگر این کلید را هنگام مشترک کردن کاربر به سرویس push ارسال می‌کند، که این کار سرویس push را قادر می‌سازد کلید عمومی برنامه شما را به PushSubscription کاربر مرتبط کند.

نمودار زیر این مراحل را نشان می‌دهد.

  1. برنامه وب خود را در مرورگر بار کنید و subscribe() را فراخوانی کنید و کلید سرور برنامه عمومی خود را ارسال کنید.
  2. سپس مرورگر یک درخواست شبکه به سرویس Push ارسال می‌کند که یک نقطه پایانی تولید می‌کند، این نقطه پایانی را با کلید عمومی برنامه شما مرتبط می‌کند، و نقطه پایانی را به مرورگر برمی‌گرداند.
  3. مرورگر این نقطه پایانی را به PushSubscription اضافه می‌کند که subscribe() promise برمی‌گرداند.

نموداری که نشان می‌دهد چگونه از کلید عمومی سرور برنامه در روش `subscribe()` ‎ استفاده می‌شود.

وقتی پیام لحظه‌ای ارسال می‌کنید، سرصفحه مجوز ایجاد کنید که حاوی اطلاعات امضاشده با کلید خصوصی سرور برنامه‌تان باشد. وقتی سرویس push درخواستی برای ارسال پیام push دریافت می‌کند، این سرصفحه مجوز امضاشده را با جستجوی کلید عمومی پیوندشده به نقطه پایانی دریافت‌کننده درخواست اعتبارسنجی می‌کند. اگر امضا معتبر باشد، سرویس push متوجه می‌شود که درخواست از سرور برنامه با کلید خصوصی منطبق ارسال شده است. این یک اقدام امنیتی است که مانع از ارسال پیام توسط دیگران به کاربران برنامه شما می‌شود.

نموداری که نحوه استفاده از کلید سرور برنامه خصوصی را هنگام ارسال پیام نشان می‌دهد.

ازنظر فنی، applicationServerKey اختیاری است. بااین‌حال، ساده‌ترین پیاده‌سازی در Chrome به آن نیاز دارد و ممکن است مرورگرهای دیگر در آینده به آن نیاز داشته باشند. این کار در Firefox اختیاری است.

مشخصات VAPID کلید سرور برنامه را تعریف می‌کند. وقتی به کلیدهای سرور برنامه یا کلیدهای VAPID اشاره می‌شود، به‌خاطر داشته باشید که این دو یکی هستند.

ایجاد کلیدهای سرور برنامه

می‌توانید بااستفاده از خط فرمان 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 نشانی وب سرویس ارسال است. برای راه‌اندازی پیام لحظه‌ای، درخواست POST به این نشانی وب ارسال کنید.

شیء 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);
    });
}

پرسشگان

در اینجا چند سؤال رایج آمده است:

آیا می‌توانید سرویس فشاری مورداستفاده مرورگر را تغییر دهید؟

نه، مرورگر سرویس ارسال را انتخاب می‌کند. همان‌طور که در این سند با فراخوانی subscribe() بحث شد، مرورگر درخواست‌های شبکه را به سرویس push ارسال می‌کند تا جزئیاتی را که PushSubscription را تشکیل می‌دهند بازیابی کند.

آیا سرویس‌های ارسال متفاوت از میاناهای برنامه‌سازی کاربردی متفاوتی استفاده می‌کنند؟

همه سرویس‌های Push انتظار API یکسانی دارند.

این میانای برنامه‌سازی کاربردی رایج که پروتکل Push وب نامیده می‌شود، درخواست شبکه‌ای را که برنامه شما برای راه‌اندازی پیام Push ایجاد می‌کند توصیف می‌کند.

اگر کاربری را در رایانه مشترک کنید، آیا در تلفنش هم مشترک می‌شود؟

نه. کاربر باید برای پیام‌رسانی فشاری در هر مرورگری که می‌خواهد پیام دریافت کند ثبت‌نام کند. این کار همچنین مستلزم آن است که کاربر در هر دستگاه اجازه اعطا کند.

مراحل بعدی

Codelabs