دليل Push API

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

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

باستخدام Push API ، يمكنك إرسال رسائل إلى المستخدمين ، ودفعهم من الخادم إلى العميل ، حتى عندما لا يتصفح المستخدم الموقع.

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

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

هل هو مدعوم بشكل جيد؟

واجهة برمجة التطبيقات Push هي إضافة حديثة إلى واجهات برمجة التطبيقات للمتصفح ، وهي مدعومة حاليًا من قبل Chrome (سطح المكتب والجوال) و Firefox و Opera منذ عام 2016 ، و Edge منذ الإصدار 17 (أوائل 2018). تعرف على المزيد حول الحالة الحالية لدعم المتصفحات علىhttps://caniuse.com/#feat=push-api

IE لا يدعمها ، وSafari له تطبيقه الخاص.

نظرًا لأن Chrome و Firefox يدعمانه ، فإن ما يقرب من 60 ٪ من المستخدمين الذين يتصفحون على سطح المكتب يمكنهم الوصول إليه ، لذا فهو مناسب تمامًا.آمنةليستخدم.

كيف تعمل

ملخص

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

تعتبر ميزة "الدفع والإشعارات" مفهومًا منفصلاً وواجهة برمجة تطبيقات (API) ، مختلطة أحيانًا بسببدفع الإخطاراتالمصطلح المستخدم في iOS. بشكل أساسي ، يتم استدعاء Notifications API عند تلقي حدث دفع باستخدام Push API.

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

الحصول على إذن المستخدم

الخطوة الأولى في العمل مع Push API هي الحصول على إذن المستخدم لتلقي البيانات منك.

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

هناك 6 خطوات:

  1. تحقق مما إذا كان عمال الخدمة مدعومين
  2. تحقق مما إذا كانت واجهة برمجة تطبيقات Push مدعومة
  3. سجل عامل خدمة
  4. طلب إذن من المستخدم
  5. اشترك للمستخدم واحصل على كائن PushSubscription
  6. أرسل كائن PushSubscription إلى الخادم الخاص بك

تحقق مما إذا كان عمال الخدمة مدعومين

if (!('serviceWorker' in navigator)) {
  // Service Workers are not supported. Return
  return
}

تحقق مما إذا كانت واجهة برمجة تطبيقات Push مدعومة

if (!('PushManager' in window)) {
  // The Push API is not supported. Return
  return
}

سجل عامل خدمة

يقوم هذا الرمز بتسجيل عامل الخدمة الموجود فيworker.jsوضع الملف في جذر المجال:

window.addEventListener('load', () => {
  navigator.serviceWorker.register('/worker.js')
  .then((registration) => {
    console.log('Service Worker registration completed with scope: ',
      registration.scope)
  }, (err) => {
    console.log('Service Worker registration failed', err)
  })
})

لمعرفة المزيد حول كيفية عمل عمال الخدمة بالتفصيل ، تحقق مندليل عمال الخدمة.

طلب إذن من المستخدم

الآن وقد تم تسجيل عامل الخدمة ، يمكنك طلب الإذن.

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

الرمز هو التالي ، داعياNotification.requestPermission().

const askPermission = () => {
  return new Promise((resolve, reject) => {
    const permissionResult = Notification.requestPermission((result) => {
      resolve(result)
    })
    if (permissionResult) {
      permissionResult.then(resolve, reject)
    }
  })
  .then((permissionResult) => {
    if (permissionResult !== 'granted') {
      throw new Error('Permission denied')
    }
  })
}

الpermissionResultالقيمة عبارة عن سلسلة يمكن أن يكون لها قيمة: -granted-default-denied

يتسبب هذا الرمز في قيام المتصفح بإظهار مربع حوار الأذونات:

The browser permission dialogue

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

اشترك للمستخدم واحصل على كائن PushSubscription

إذا أعطانا المستخدم الإذن ، فيمكننا الاشتراك فيه والاتصال بهregistration.pushManager.subscribe().

const APP_SERVER_KEY = 'XXX'

window.addEventListener(‘load’, () => { navigator.serviceWorker.register(’/worker.js’) .then((registration) => { askPermission().then(() => { const options = { userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(APP_SERVER_KEY) } return registration.pushManager.subscribe(options) }).then((pushSubscription) => { // we got the pushSubscription object } }, (err) => { console.log(‘Service Worker registration failed’, err) }) })

APP_SERVER_KEYهو سلسلة تسمىمفتاح خادم التطبيقأومفتاح VAPID- يحدد المفتاح العام للتطبيق ، وهو جزء من زوج مفاتيح عام / خاص.

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

أرسل كائن PushSubscription إلى الخادم الخاص بك

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

نقوم أولاً بإنشاء تمثيل JSON للكائن

const subscription = JSON.stringify(pushSubscription)

ويمكننا إرسالها إلى خادمنا باستخدامجلب API:

const sendToServer = (subscription) => {
  return fetch('/api/subscription', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(subscription)
  })
  .then((res) => {
    if (!res.ok) {
      throw new Error('An error occurred')
    }
    return res.json()
  })
  .then((resData) => {
    if (!(resData.data && resData.data.success)) {
      throw new Error('An error occurred')
    }
  })
}

sendToServer(subscription)

من جانب الخادم ، فإن/api/subscriptionتتلقى نقطة النهاية طلب POST ويمكنها تخزين معلومات الاشتراك في وحدة التخزين الخاصة بها.

كيف يعمل جانب الخادم

حتى الآن تحدثنا فقط عن جانب العميل: الحصول على إذن المستخدم ليتم إخطاره في المستقبل.

ماذا عن الخادم؟ ما الذي يجب أن يفعله وكيف يتفاعل مع العميل؟

تستخدم هذه الأمثلة من جانب الخادم Express.js (http://expressjs.com/) كإطار عمل HTTP أساسي ، ولكن يمكنك كتابة معالج واجهة برمجة تطبيقات Push من جانب الخادم بأي لغة أو إطار عمل

تسجيل اشتراك عميل جديد

عندما يرسل العميل اشتراكًا جديدًا ، تذكر أننا استخدمنا/api/subscriptionنقطة نهاية HTTP POST ، إرسال تفاصيل كائن PushSubscription بتنسيق JSON ، في النص.

نقوم بتهيئة Express.js:

const express = require('express')
const app = express()

تتأكد وظيفة الأداة المساعدة هذه من أن الطلب صالح ، ويحتوي على نص وخاصية نقطة نهاية ، وإلا فإنها ترجع خطأ إلى العميل:

const isValidSaveRequest = (req, res) => {
  if (!req.body || !req.body.endpoint) {
    res.status(400)
    res.setHeader('Content-Type', 'application/json')
    res.send(JSON.stringify({
      error: {
        id: 'no-endpoint',
        message: 'Subscription must have an endpoint'
      }
    }))
    return false
  }
  return true
}

تقوم وظيفة الأداة التالية بحفظ الاشتراك في قاعدة البيانات ، وإرجاع الوعد الذي تم حله عند اكتمال عملية الإدراج (أو فشلها). الinsertToDatabaseالوظيفة عنصرًا نائبًا ، فلن ندخل في هذه التفاصيل هنا:

const saveSubscriptionToDatabase = (subscription) => {
  return new Promise((resolve, reject) => {
    insertToDatabase(subscription, (err, id) => {
      if (err) {
        reject(err)
        return
      }
  <span style="color:#a6e22e">resolve</span>(<span style="color:#a6e22e">id</span>)
})

}) }

نستخدم هذه الوظائف في معالج طلب POST أدناه. نتحقق مما إذا كان الطلب صالحًا ، ثم نحفظ الطلب ثم نعيد ملفdata.success: trueالرد على العميل ، أو خطأ:

app.post('/api/subscription', (req, res) => {
  if (!isValidSaveRequest(req, res)) {
    return
  }

saveSubscriptionToDatabase(req, res.body) .then((subscriptionId) => { res.setHeader(‘Content-Type’, ‘application/json’) res.send(JSON.stringify({ data: { success: true } })) }) .catch((err) => { res.status(500) res.setHeader(‘Content-Type’, ‘application/json’) res.send(JSON.stringify({ error: { id: ‘unable-to-save-subscription’, message: ‘Subscription received but failed to save it’ } })) }) })

app.listen(3000, () => { console.log(‘App listening on port 3000’) })

إرسال رسالة دفع

الآن بعد أن سجل الخادم العميل في قائمته ، يمكننا إرسال رسائل Push إليه. دعنا نرى كيف يعمل ذلك عن طريق إنشاء مثال مقتطف رمز يجلب جميع الاشتراكات ويرسل رسالة دفع إليها جميعًا في نفس الوقت.

نحن نستخدم مكتبة لأن ملفبروتوكول دفع الويبيكونمركب، و lib يسمح لنا باستخراج الكثير من التعليمات البرمجية ذات المستوى المنخفض التي تتأكد من أنه يمكننا العمل بأمان وبشكل صحيح للتعامل مع أي حالة حافة.

يستخدم هذا المثال الامتدادweb-push Node.jsمكتبة (https://github.com/web-push-libs/web-push) للتعامل مع إرسال رسالة Push

نقوم أولاً بتهيئة ملفweb-pushlib ، وقمنا بإنشاء مجموعة من المفاتيح الخاصة والعامة ، وقمنا بتعيينها على أنها تفاصيل VAPID:

const webpush = require('web-push')
const vapidKeys = webpush.generateVAPIDKeys()

const PUBLIC_KEY = ‘XXX’ const PRIVATE_KEY = ‘YYY’

const vapidKeys = { publicKey: PUBLIC_KEY, privateKey: PRIVATE_KEY }

webpush.setVapidDetails( mailto:[email protected], vapidKeys.publicKey, vapidKeys.privateKey )

ثم قمنا بإعداد ملفtriggerPush()الطريقة المسؤولة عن إرسال حدث الدفع إلى العميل. انها فقط تدعوwebpush.sendNotification()ويلتقط أي خطأ. إذا كان خطأ الإرجاع رمز حالة HTTP هو410مما يعنيذهب، نحذف هذا المشترك من قاعدة البيانات.

const triggerPush = (subscription, dataToSend) => {
  return webpush.sendNotification(subscription, dataToSend)
  .catch((err) => {
    if (err.statusCode === 410) {
      return deleteSubscriptionFromDatabase(subscription._id)
    } else {
      console.log('Subscription is no longer valid: ', err)
    }
  })
}

نحن لا ننفذ الحصول على الاشتراكات من قاعدة البيانات ، لكننا نتركها كعنصر أساسي:

const getSubscriptionsFromDatabase = () => {
  //stub
}

إن رمز الكود هو رد الاتصال لطلب POST إلى/api/pushنقطة النهاية:

app.post('/api/push', (req, res) => {
  return getSubscriptionsFromDatabase()
  .then((subscriptions) => {
    let promiseChain = Promise.resolve()
    for (let i = 0; i < subscriptions.length; i++) {
      const subscription = subscriptions[i]
      promiseChain = promiseChain.then(() => {
        return triggerPush(subscription, dataToSend)
      })
    }
    return promiseChain
  })
  .then(() => {
    res.setHeader('Content-Type', 'application/json')
    res.send(JSON.stringify({ data: { success: true } }))
  })
  .catch((err) => {
    res.status(500)
    res.setHeader('Content-Type', 'application/json')
    res.send(JSON.stringify({
      error: {
        id: 'unable-to-send-messages',
        message: `Failed to send the push ${err.message}`
      }
    }))
  })
})

ما يفعله الكود أعلاه هو: يحصل على جميع الاشتراكات من قاعدة البيانات ، ثم يتكرر عليها ، ويستدعيtriggerPush()وظيفة شرحناها من قبل.

بمجرد الانتهاء من الاشتراكات ، نعيد استجابة JSON الناجحة ، ما لم يحدث خطأ ونعيد الخطأ 500.

في العالم الحقيقي…

من غير المحتمل أن تقوم بإعداد خادم Push الخاص بك إلا إذا كان لديك حالة استخدام خاصة جدًا ، أو كنت ترغب فقط في تعلم التكنولوجيا أو ترغب في DIY. بدلاً من ذلك ، تريد عادةً استخدام أنظمة أساسية مثل OneSignal (https://onesignal.com) التي تتعامل مع أحداث Push بشفافية لجميع أنواع المنصات ، بما في ذلك Safari و iOS ، مجانًا.

تلقي حدث دفع

عندما يتم إرسال حدث Push من الخادم ، كيف يحصل عليه العميل؟

إنه أمر طبيعيJavaScriptمستمع الحدث ، علىpushالحدث الذي يعمل داخل عامل الخدمة:

self.addEventListener('push', (event) => {
  // data is available in event.data
})

event.dataيحتوي علىPushMessageDataالذي يعرض طرقًا لاسترداد بيانات الدفع التي يرسلها الخادم ، بالتنسيق الذي تريده:

ستستخدم عادة ملفاتevent.data.json().

عرض الإخطار

هنا نتقاطع قليلاً معواجهة برمجة تطبيقات الإخطارات، ولكن لسبب وجيه ، كإحدى حالات الاستخدام الرئيسية لواجهة برمجة تطبيقات Push هي عرض الإشعارات.

داخل موقعناpushمستمع الحدث في عامل الخدمة ، نحتاج إلى عرض الإشعار للمستخدم ، وإخبار الحدث بالانتظار حتى يعرضه المتصفح قبل أن يتم إنهاء الوظيفة. نقوم بتمديد عمر الحدث حتى ينتهي المتصفح من عرض الإشعار (حتى يتم حل الوعد) ، وإلا فقد يتم إيقاف عامل الخدمة في منتصف المعالجة:

self.addEventListener('push', (event) => {
  const promiseChain = self.registration.showNotification('Hey!')
  event.waitUntil(promiseChain)
})

المزيد عن الإخطارات فيدليل API للإخطارات.


المزيد من دروس المتصفح: