透過網路推播程式庫傳送訊息

Matt Gaunt

使用網路推送時,觸發推送訊息的其中一個痛點之一 「fiddly」。如要觸發推送訊息,應用程式必須提出 POST 要求 網頁推送服務 通訊協定。如要在所有服務中使用推送功能 因此您需要使用 VAPID (又稱為應用程式伺服器金鑰),基本上需要設定標頭內含證明值的標頭 應用程式就能傳送訊息給使用者如要透過推送訊息傳送資料,資料需 加密和特定標頭 以便瀏覽器正確解密郵件。

觸發推送的主要問題是如果您遇到問題,就很難診斷出問題所在 問題。這項支援與時俱進、支援更多瀏覽器,但實在沒那麼簡單。適用對象 因此,我強烈建議使用程式庫來處理加密、格式和 觸發您的推送訊息

如果你真的想瞭解程式庫的用途 ,目前我們將探討如何管理訂閱項目,以及如何使用 提出推送要求

在本節中,我們將使用 web-push Node. 程式庫。其他語言可能會有差異 就不會太相似我們正在查看節點,因為它是 JavaScript,應該是 大多數讀者都能輕鬆閱讀

我們將逐步進行下列步驟:

  1. 將訂閱項目傳送至我們的後端並儲存。
  2. 擷取已儲存的訂閱項目並觸發推送訊息。

儲存訂閱項目

從資料庫儲存及查詢 PushSubscription 的內容,會因以下因素而異: 而伺服器端語言和資料庫也會有所幫助,不過若能查看 舉例說明實踐方法

在示範網頁中,系統會發出簡單的 POST 要求,將 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.');
      }
    });
}

示範中的 Express 伺服器有一個相符的要求事件監聽器, /api/save-subscription/ 端點:

app.post('/api/save-subscription/', function (req, res) {

在這個路徑中,我們會驗證訂閱,只確認要求有效且未充分 垃圾:

const isValidSaveRequest = (req, res) => {
  // Check the request body has at least an endpoint.
  if (!req.body || !req.body.endpoint) {
    // Not a valid subscription.
    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;
};

如果訂閱項目有效,系統就會儲存訂閱項目,並傳回適當的 JSON 回應:

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.',
        },
      }),
    );
  });

這個示範使用 nedb 儲存訂閱, 但您可以使用您選擇的任何資料庫。我們只會將此做為 不必進行任何設定在實際工作環境中,建議你使用更可靠的項目。(我經常 繼續使用狀態良好的舊版 MySQL)。

function saveSubscriptionToDatabase(subscription) {
  return new Promise(function (resolve, reject) {
    db.insert(subscription, function (err, newDoc) {
      if (err) {
        reject(err);
        return;
      }

      resolve(newDoc._id);
    });
  });
}

傳送推送訊息

傳送推送訊息時,我們最終需要一些事件來觸發 向使用者傳送訊息常見的做法是建立管理頁面 設定及觸發推送訊息不過,您可以建立一個在本機或 其他方法則可存取 PushSubscription 的清單,並執行程式碼 觸發推送訊息

示範時,有一個「管理員喜歡」可以觸發推送作業的網頁這只是示範 公開頁面。

我會逐步說明進行示範操作的各個步驟。孩子會不會 步驟,包括所有新手節點的使用者。

討論訂閱使用者時,我們提到了applicationServerKey subscribe() 個選項。位於後端,需要使用這個私密金鑰

在示範中,這些值會像在 Node 應用程式中一樣加入 (我知道,但其實只需要, 就沒有任何魔法):

const vapidKeys = {
  publicKey:
    'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
  privateKey: 'UUxI4O8-FbRouAevSmBQ6o18hgE4nSG3qwvJTfKc-ls',
};

接下來,我們需要為節點伺服器安裝 web-push 模組:

npm install web-push --save

接著,在 Node 指令碼中,我們需要 web-push 模組 如下所示:

const webpush = require('web-push');

我們現在可以開始使用 web-push 模組了。首先,我們必須向 web-push 模組說明 應用程式伺服器金鑰(請記住,它們也稱為 VAPID 金鑰,因為這就是您的名稱 其他規格)

const vapidKeys = {
  publicKey:
    'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
  privateKey: 'UUxI4O8-FbRouAevSmBQ6o18hgE4nSG3qwvJTfKc-ls',
};

webpush.setVapidDetails(
  'mailto:web-push-book@gauntface.com',
  vapidKeys.publicKey,
  vapidKeys.privateKey,
);

請注意,我們也加入了「mailto:」字串。這個字串必須是網址或 mailto 建立一個 Fitbit 帳戶。此資訊實際上會傳送到網路推送服務 觸發推送作業的要求這麼做的原因是,當網路推送服務需要 與寄件者聯繫時,他們也會提供一些資訊,

這樣,web-push 模組已可供使用,下一步就是觸發推送訊息。

此示範內容會使用假示管理控制台來觸發推送訊息。

管理頁面的螢幕截圖。

按一下 [觸發推送訊息]按鈕會向 /api/trigger-push-msg/ 發出 POST 要求, 也就是後端傳送推送訊息的信號,因此我們在 表示此端點:

app.post('/api/trigger-push-msg/', function (req, res) {

收到這項要求後,我們會從資料庫中擷取訂閱項目,然後 因此都會觸發推送訊息

return getSubscriptionsFromDatabase().then(function (subscriptions) {
  let promiseChain = Promise.resolve();

  for (let i = 0; i < subscriptions.length; i++) {
    const subscription = subscriptions[i];
    promiseChain = promiseChain.then(() => {
      return triggerPushMsg(subscription, dataToSend);
    });
  }

  return promiseChain;
});

接著 triggerPushMsg() 函式就能使用網頁推送程式庫,傳送訊息到 現有的訂閱項目。

const triggerPushMsg = function (subscription, dataToSend) {
  return webpush.sendNotification(subscription, dataToSend).catch((err) => {
    if (err.statusCode === 404 || err.statusCode === 410) {
      console.log('Subscription has expired or is no longer valid: ', err);
      return deleteSubscriptionFromDatabase(subscription._id);
    } else {
      throw err;
    }
  });
};

呼叫 webpush.sendNotification() 會傳回承諾。如果 訊息傳送成功,承諾可以解決, 不必採取任何行動如果保證遭拒,您就必須檢查 錯誤,因為系統會告知您 PushSubscription 是否仍為 有效或無效

如要判斷推送服務發出的錯誤類型,建議您查看狀態碼。發生錯誤 訊息在推送服務之間有所不同,某些則比其他訊息實用。

本例中,程式碼會檢查狀態碼 404410,這些是以下項目的 HTTP 狀態碼: 找不到和「消失」如果我們收到以下其中一項,代表訂閱項目已過期 或已失效在這些情況下,我們必須從資料庫中移除訂閱項目。

若是其他錯誤,我們僅 throw err,會以 triggerPushMsg()拒絕。

我們會在下一節探討網路推送時 通訊協定的詳細資料

循環執行訂閱後,我們需要傳回 JSON 回應。

.then(() => {
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-send-messages',
    message: `We were unable to send messages to all subscriptions : ` +
        `'${err.message}'`
    }
}));
});

以下是幾個主要的導入步驟:

  1. 建立 API,從網頁將訂閱項目傳送到後端 以便儲存至資料庫
  2. 建立 API,用於觸發推送訊息 (在本例中為 API)。
  3. 從我們的後端擷取所有訂閱項目 並使用其中一個網路推送內容,將訊息傳送至每個訂閱項目 程式庫

無論您的後端為何 (節點、PHP、Python 等),都必須完成實作推送作業的步驟 保持不變

接下來,這些網頁推送程式庫到底有什麼作用?

後續步驟

程式碼研究室