Invio di messaggi con le librerie web push

Matt Gaunt

Uno dei problemi quando si lavora con il web push è che l'attivazione di un messaggio push è estremamente "complicato". Per attivare un messaggio push, un'applicazione deve effettuare una richiesta POST a un push che segue il push web del protocollo. Per utilizzare push su tutti browser su cui devi usare VAPID (note anche come chiavi server delle applicazioni) che in sostanza richiedono l'impostazione di un'intestazione con un valore che dimostri la tua applicazione può inviare messaggi a un utente. Per inviare dati con un messaggio push, i dati devono essere criptati e intestazioni specifiche devono essere aggiunti in modo che il browser possa decriptare il messaggio correttamente.

Il problema principale dell'attivazione del push è che, se riscontri un problema, è difficile da diagnosticare risolvere il problema. Questo sta migliorando con il tempo e il supporto di più browser, ma è tutt'altro che semplice. Per Per questo motivo, consiglio vivamente di usare una libreria per gestire la crittografia, la formattazione l'attivazione del tuo messaggio push.

Se vuoi davvero sapere cosa fanno le librerie, ne parleremo nella prossima sezione. Per il momento, esamineremo la gestione degli abbonamenti e l'uso di libreria push web esistente per effettuare le richieste push.

In questa sezione utilizzeremo il nodo web-push libreria di Google. Le altre lingue avranno differenze, non saranno troppo diverse. Stiamo esaminando il nodo, dato che è JavaScript e dovrebbe essere più accessibili per i lettori.

Il sistema esegue i seguenti passaggi:

  1. Invia un abbonamento al nostro backend e salvalo.
  2. Recupera le sottoscrizioni salvate e attiva un messaggio push.

Salvataggio degli abbonamenti in corso...

Il salvataggio e l'esecuzione di query su PushSubscription da un database variano a seconda la lingua e il database lato server, ma potrebbe essere utile vedere un esempio di come fare.

Nella pagina web demo, PushSubscription viene inviato al nostro backend mediante una semplice richiesta POST:

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

Il server Express nella nostra demo ha un listener di richieste corrispondente per il valore Endpoint /api/save-subscription/:

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

In questo modo convalidiamo l'abbonamento per assicurarci che la richiesta sia corretta e non completa di rifiuti:

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

Se l'abbonamento è valido, dobbiamo salvarlo e restituire un'app Risposta 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.',
        },
      }),
    );
  });

Questa demo utilizza nedb per archiviare gli abbonamenti, è una un semplice database basato su file, ma puoi usare qualsiasi database di tua scelta. Lo usiamo solo come non richiede alcuna configurazione. Per la produzione dovresti usare qualcosa di più affidabile. (tendo a utilizza il buon vecchio MySQL.)

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

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

Invio di messaggi push

Quando si tratta di inviare un messaggio push, alla fine abbiamo bisogno di un evento per attivare il processo inviando un messaggio agli utenti. Un approccio comune è creare una pagina di amministrazione che ti consenta configurare e attivare il messaggio push. ma potresti creare un programma da eseguire localmente o che consente di accedere all'elenco di PushSubscription ed eseguire il codice per attivare il messaggio push.

La nostra demo ha un "Mi piace" per gli amministratori che ti consente di attivare un push. Poiché è solo una demo, pagina pubblica.

Analizzerò tutti i passaggi necessari per far funzionare la demo. Questi saranno bambini passaggi in modo che tutti possano seguirli, inclusi i nuovi utenti di Node.

Quando abbiamo parlato dell'iscrizione di un utente, abbiamo trattato l'aggiunta di applicationServerKey alla subscribe() opzioni. È sul backend che abbiamo bisogno di questa chiave privata.

Nella demo, questi valori vengono aggiunti alla nostra app Node in questo modo (codice noioso che conosco, ma che non c'è magia):

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

Ora dobbiamo installare il modulo web-push per il nostro server Node:

npm install web-push --save

Quindi, nel nostro script Node, è necessario il modulo web-push in questo modo:

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

Ora possiamo iniziare a utilizzare il modulo web-push. Per prima cosa dobbiamo parlare del modulo web-push le chiavi del nostro server delle applicazioni. Ricorda che sono note anche come chiavi VAPID perché è questo il nome della specifica).

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

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

Tieni presente che abbiamo incluso anche "mailto:" stringa. Questa stringa deve essere un URL o un indirizzo mailto . Queste informazioni verranno effettivamente inviate al servizio push web come parte la richiesta di attivare un push. Il motivo è che se un servizio push web ha bisogno contattare il mittente, il quale avrà delle informazioni che gli consentiranno di farlo.

Ora che il modulo web-push è pronto per essere utilizzato, il passaggio successivo consiste nell'attivare un messaggio push.

La demo utilizza il pannello di amministrazione di finzione per attivare i messaggi push.

Screenshot della pagina di amministrazione.

Facendo clic su "Messaggio push di attivazione" effettua una richiesta POST a /api/trigger-push-msg/, che è il segnale per il nostro backend che invia messaggi push, quindi creiamo il percorso express per questo endpoint:

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

Quando riceviamo questa richiesta, prendiamo le sottoscrizioni dal database e per ciascuno attiviamo un messaggio push.

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

La funzione triggerPushMsg() può quindi utilizzare la libreria web-push per inviare un messaggio alla abbonamento fornito.

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

La chiamata a webpush.sendNotification() restituirà una promessa. Se è stato inviato correttamente, la promessa si risolverà non dobbiamo fare nulla. Se la promessa rifiuta, è necessario esaminare le in quanto ti comunicherà se PushSubscription è ancora valida o meno.

Per determinare il tipo di errore di un servizio push, è preferibile osservare il codice di stato. Errore I messaggi variano a seconda dei servizi push e alcuni sono più utili di altri.

In questo esempio, viene controllato i codici di stato 404 e 410, che sono i codici di stato HTTP "Non trovato" e "Gone". Se ne riceviamo uno, significa che l'abbonamento è scaduto o non è più valido. In questi scenari, dobbiamo rimuovere le sottoscrizioni dal nostro database.

In caso di altri errori, throw err, farà in modo che la promessa venga restituita Rifiuto di triggerPushMsg():

Tratteremo alcuni degli altri codici di stato nella sezione successiva, quando esamineremo il report push sul web. del protocollo in modo più dettagliato.

Dopo aver eseguito il loop delle sottoscrizioni, dobbiamo restituire una risposta in formato 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}'`
    }
}));
});

Abbiamo esaminato i principali passaggi di implementazione:

  1. Crea un'API per inviare abbonamenti dalla nostra pagina web al nostro backend in modo da poterli salvare in un database.
  2. Crea un'API per attivare l'invio di messaggi push (in questo caso, API richiamata dal pannello di amministrazione simulato).
  3. Recupera tutte le sottoscrizioni dal nostro backend e inviare un messaggio a ciascuna sottoscrizione con uno dei metodi web-push librerie.

Indipendentemente dal backend (nodo, PHP, Python, ...), i passaggi per l'implementazione del push in modo che sia la stessa.

Poi, cosa fanno esattamente per noi queste librerie web-push?

Passaggi successivi

Codelab