Como enviar mensagens com bibliotecas push da Web

Um dos pontos problemáticos ao trabalhar com push na Web é que acionar uma mensagem push é extremamente "violino". Para acionar uma mensagem push, um aplicativo precisa fazer uma solicitação POST para um push após o envio por push da Web protocolo. Para usar push em todas você precisa usar VAPID (ou chaves do servidor de aplicativos), que basicamente requer a definição de um cabeçalho com um valor que comprove seu aplicativo pode enviar uma mensagem a um usuário. Para enviar dados com uma mensagem push, eles precisam estar criptografados e cabeçalhos específicos precisam ser adicionados para que o navegador possa descriptografar a mensagem corretamente.

O principal problema com o acionamento de push é que, se você encontra um problema, é difícil diagnosticar o problema. Isso está melhorando com o tempo e a compatibilidade com os navegadores, mas está longe de ser fácil. Para Por isso, recomendamos o uso de uma biblioteca para lidar com a criptografia, formatação o gatilho da mensagem push.

Se você realmente quiser saber o que as bibliotecas estão fazendo, vamos abordar na próxima seção. Por enquanto, vamos aprender a gerenciar assinaturas e usar uma biblioteca push da Web existente para fazer as solicitações de push.

Nesta seção, vamos usar o web-push Node biblioteca. Outros idiomas terão diferenças, mas não serão muito diferentes. Estamos analisando o Node, já que é JavaScript e deve ser a mais acessível para os leitores.

Vamos seguir estas etapas:

  1. Envie uma assinatura para nosso back-end e salve-a.
  2. Recuperar assinaturas salvas e acionar uma mensagem push.

Salvando inscrições

Salvar e consultar PushSubscriptions em um banco de dados varia de acordo com sua escolha de linguagem do lado do servidor e banco de dados, mas pode ser útil ver um exemplo de como isso pode ser feito.

Na página da Web de demonstração, o PushSubscription é enviado ao nosso back-end fazendo uma solicitação POST simples:

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

O servidor Express em nossa demonstração tem um listener de solicitação correspondente para os Endpoint /api/save-subscription/:

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

Nessa rota, validamos a inscrição apenas para garantir que a solicitação esteja correta e não esteja cheia de lixo:

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 a assinatura for válida, precisamos salvá-la e retornar um Resposta 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.',
        },
      }),
    );
  });

Esta demonstração usa o nedb para armazenar as assinaturas, é um um banco de dados simples baseado em arquivos, mas você pode usar o banco de dados que quiser. Estamos usando isso apenas como não exige configuração. Para produção, é melhor usar algo mais confiável. (tendo a fique com o bom e velho MySQL.)

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

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

Como enviar mensagens push

Quando se trata de enviar uma mensagem push, precisamos de algum evento para acionar o processo de enviar uma mensagem aos usuários. Uma abordagem comum é criar uma página de administração que permita configura e aciona a mensagem push. Mas você pode criar um programa para ser executado localmente ou outra abordagem que permita acessar a lista de PushSubscriptions e executar o código para acionar a mensagem push.

Nossa demonstração tem um "gostei" de administrador que permite acionar um push. Como é apenas uma demonstração, página pública.

Apresentarei todas as etapas envolvidas no funcionamento da demonstração. Estes são os bebês para que todos possam acompanhar, incluindo iniciantes no Node.

Quando discutimos a inscrição de um usuário, abordamos a adição de um applicationServerKey à subscribe() opções. Vamos precisar dessa chave privada no back-end.

Na demonstração, esses valores são adicionados ao nosso app Node dessa forma (código chato, eu sei, mas só quero que não há mágica):

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

Em seguida, precisamos instalar o módulo web-push para o servidor de nó:

npm install web-push --save

Em seguida, no script do Node, precisamos do módulo web-push. assim:

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

Agora podemos começar a usar o módulo web-push. Primeiro, precisamos informar o módulo web-push sobre as chaves do servidor de aplicativos. Lembre-se de que elas também são conhecidas como chaves VAPID, porque esse é o nome da especificação.

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

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

Observe que também incluímos um arquivo "mailto:" fio. Essa string precisa ser um URL ou um protocolo mailto endereço de e-mail. Essa informação será enviada para o serviço de push da web como parte do a solicitação para acionar um push. Isso é feito para que, se um serviço de push na Web precisar para entrar em contato com o remetente, essa pessoa tem algumas informações para permitir isso.

Com isso, o módulo web-push está pronto para uso. A próxima etapa é acionar uma mensagem push.

A demonstração usa o painel de administração fictício para acionar mensagens push.

Captura de tela da página Administrador.

Clicando em "Acionar mensagem push" vai fazer uma solicitação POST para /api/trigger-push-msg/, que é o sinal para que nosso back-end envie mensagens push, então criamos a rota expresso para este endpoint:

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

Quando essa solicitação é recebida, pegamos as assinaturas do banco de dados e para cada um deles, uma mensagem push é acionada.

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

A função triggerPushMsg() pode então usar a biblioteca web-push para enviar uma mensagem ao que forneceu uma assinatura.

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

A chamada para webpush.sendNotification() vai retornar uma promessa. Se o for enviada, a promessa vai ser resolvida e há não precisamos fazer nada. Se a promessa for rejeitada, você precisará examinar porque informa se PushSubscription ainda está válidas ou não.

Para determinar o tipo de erro de um serviço de push, é melhor observar o código de status. Erro as mensagens variam entre os serviços de push, e algumas são mais úteis que outras.

Neste exemplo, ele verifica os códigos de status 404 e 410, que são os códigos de status HTTP para "Não encontrado" e "Desaparecido". Se recebermos um desses, significa que a assinatura expirou ou não é mais válida. Nesses cenários, precisamos remover as assinaturas do nosso banco de dados.

No caso de algum outro erro, apenas throw err, que fará a promessa retornada pelo triggerPushMsg() rejeitado.

Abordaremos alguns dos outros códigos de status na próxima seção quando examinarmos o envio do protocolo com mais detalhes.

Depois de percorrer as assinaturas, precisamos retornar uma resposta 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}'`
    }
}));
});

Abordamos as principais etapas de implementação:

  1. Criar uma API para enviar inscrições da nossa página da Web para o nosso back-end para poder salvá-los em um banco de dados.
  2. Crie uma API para acionar o envio de mensagens push (neste caso, uma API chamada a partir do painel de administração fictício).
  3. Recuperar todas as assinaturas do nosso back-end e enviar uma mensagem para cada assinatura com um dos métodos web-push bibliotecas.

Independentemente do back-end (Node, PHP, Python, etc.), as etapas para implementar push são sejam iguais.

Em seguida, o que exatamente essas bibliotecas de push da Web estão fazendo por nós?

A seguir

Code labs