Um dos pontos problemáticos ao trabalhar com push da Web é que o acionamento de uma mensagem push é extremamente "fiel". Para acionar uma mensagem push, um aplicativo precisa fazer uma solicitação POST a um serviço de push seguindo o protocolo de push da Web. Para usar push em todos os navegadores, você precisa usar o VAPID (também conhecido como chaves de servidor de aplicativos), que basicamente exige a configuração de um cabeçalho com um valor que comprova que seu aplicativo pode enviar mensagens para um usuário. Para enviar dados com uma mensagem push, eles precisam ser 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ê encontrar um problema, será difícil diagnosticá-lo. Isso está melhorando com o tempo e o suporte mais amplo aos navegadores, mas está longe de ser fácil. Por esse motivo, é altamente recomendável usar uma biblioteca para lidar com a criptografia, formatação e acionamento da sua mensagem push.
Se você realmente quiser saber mais sobre o que as bibliotecas estão fazendo, vamos abordar isso na próxima seção. Por enquanto, vamos analisar o gerenciamento de assinaturas e o uso de uma biblioteca push da Web existente para fazer as solicitações de push.
Nesta seção, usaremos a biblioteca de nós de push da Web. Outros idiomas terão diferenças, mas não serão muito diferentes. Estamos analisando o Node, já que é JavaScript e deve ser o mais acessível para os leitores.
Vamos passar pelas seguintes etapas:
- Envie uma assinatura para nosso back-end e salve-a.
- Recuperar assinaturas salvas e acionar uma mensagem push.
Salvando assinaturas
Salvar e consultar PushSubscription
s de um banco de dados varia de acordo com
a linguagem do lado do servidor e a escolha do 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 com 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 na nossa demonstração tem um listener de solicitações correspondente para o endpoint /api/save-subscription/
:
app.post('/api/save-subscription/', function (req, res) {
Nesse caminho, validamos a assinatura 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, precisaremos salvá-la e retornar uma resposta JSON apropriada:
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 banco de dados simples baseado em arquivos, mas você pode usar qualquer um. Estamos usando esse recurso apenas porque ele não requer configuração. Para produção, é melhor usar algo mais confiável. Eu costumo usar 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);
});
});
}
Envio de mensagens push
Quando se trata de enviar uma mensagem push, precisamos de algum evento para acionar o processo de envio de uma mensagem aos usuários. Uma abordagem comum é criar uma página de administrador para configurar e acionar a mensagem push. No entanto, você pode criar um programa para ser executado localmente ou qualquer
outra abordagem que permita acessar a lista de PushSubscription
s e executar o código para
acionar a mensagem push.
Nossa demonstração tem uma página "como administrador" que permite acionar um push. Como é apenas uma demonstração, é uma página pública.
Vou passar por cada etapa para fazer a demonstração funcionar. São etapas simples para que todos possam acompanhar, incluindo qualquer pessoa nova no Node.
Quando discutimos a inscrição de um usuário, abordamos a adição de um applicationServerKey
às
opções subscribe()
. Precisamos dessa chave privada no back-end.
Na demonstração, esses valores são adicionados ao nosso app Node da seguinte forma (um código entediante, que eu sei, mas só quero que você saiba 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
do nosso servidor Node:
npm install web-push --save
Em seguida, no script do Node, precisamos do módulo web-push
da seguinte maneira:
const webpush = require('web-push');
Agora, podemos começar a usar o módulo web-push
. Primeiro, precisamos informar ao módulo web-push
sobre as chaves do servidor de aplicativos. 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 uma string "mailto:". Essa string precisa ser um URL ou um endereço de e-mail mailto. Na verdade, essa informação será enviada ao serviço de push da Web como parte da solicitação para acionar um push. Isso é feito para que, se um serviço de push da Web precisar entrar em contato com o remetente, ele tenha algumas informações que permitam isso.
Com isso, o módulo web-push
estará pronto para uso. A próxima etapa é acionar uma mensagem push.
A demonstração usa o painel de adm. de simulação para acionar mensagens push.
Clicar no botão "Trigger Push Message" gera uma solicitação POST para /api/trigger-push-msg/
, que é o sinal para nosso back-end enviar mensagens push. Por isso, criamos a rota no Express 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 uma, acionamos uma mensagem 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;
});
A função triggerPushMsg()
pode usar a biblioteca de push da Web para enviar uma mensagem à
assinatura fornecida.
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 a mensagem foi enviada com sucesso, a promessa será resolvida e não será necessário fazer nada. Se a promessa for rejeitada, será necessário examinar o erro, já que ele informará se o PushSubscription
ainda é válido ou não.
Para determinar o tipo de erro de um serviço de push, analise o código de status. As mensagens de erro variam entre os serviços de push, e alguns são mais úteis que outros.
Neste exemplo, ela verifica os códigos de status 404
e 410
, que são os códigos de status HTTP para "Not Found" e "Gone". Se recebermos uma dessas mensagens, isso significa que a assinatura expirou
ou não é mais válida. Nesses casos, precisamos remover as assinaturas do nosso banco de dados.
No caso de algum outro erro, apenas throw err
, o que fará com que a promessa retornada por triggerPushMsg()
seja rejeitada.
Vamos abordar alguns dos outros códigos de status na próxima seção quando analisarmos o protocolo de push da Web com mais detalhes.
Depois de passar pelas 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}'`
}
}));
});
Passamos pelas principais etapas de implementação:
- Crie uma API para enviar assinaturas da nossa página da Web para o back-end e salvá-las em um banco de dados.
- Crie uma API para acionar o envio de mensagens push (neste caso, uma API chamada do painel de administração simulado).
- Recupere todas as assinaturas do nosso back-end e envie uma mensagem para cada uma delas com uma das bibliotecas de push da Web.
Independentemente do back-end (Node, PHP, Python etc.), as etapas para implementar o push serão as mesmas.
A seguir, o que exatamente essas bibliotecas de push da Web estão fazendo por nós?
A seguir
- Visão geral das notificações push da Web
- Como funciona o envio por push
- Como inscrever um usuário
- UX de permissão
- Como enviar mensagens com bibliotecas push da Web
- Protocolo push da Web
- Como processar eventos push
- Exibir uma notificação
- Comportamento das notificações
- Padrões comuns de notificação
- Perguntas frequentes sobre notificações push
- Problemas comuns e como informar bugs