A primeira etapa é conseguir a permissão do usuário para enviar mensagens push e, em seguida,
usar um PushSubscription
.
A API JavaScript para fazer isso é razoavelmente simples, então vamos analisar o fluxo lógico.
Detecção de recursos
Primeiro, precisamos verificar se o navegador atual realmente oferece suporte a mensagens push. Para conferir se o push é compatível com duas verificações simples,
- Verifique se há serviceWorker no Navigator.
- Procure PushManager na janela window.
if (!('serviceWorker' in navigator)) {
// Service Worker isn't supported on this browser, disable or hide UI.
return;
}
if (!('PushManager' in window)) {
// Push isn't supported on this browser, disable or hide UI.
return;
}
Embora o suporte ao navegador esteja crescendo rapidamente para service workers e mensagens push, é sempre uma boa ideia detectar ambos e aprimorar progressivamente.
Registrar um service worker
Com a detecção de recursos, sabemos que os service workers e Push são compatíveis. A próxima etapa é "registrar" nosso service worker.
Quando registramos um service worker, informamos ao navegador onde está o arquivo do nosso service worker. O arquivo ainda é apenas JavaScript, mas o navegador " dará acesso" às APIs do service worker, incluindo push. Para ser mais exato, o navegador executa o arquivo em um ambiente de service worker.
Para registrar um service worker, chame navigator.serviceWorker.register()
, transmitindo o caminho para
nosso arquivo. Assim:
function registerServiceWorker() {
return navigator.serviceWorker
.register('/service-worker.js')
.then(function (registration) {
console.log('Service worker successfully registered.');
return registration;
})
.catch(function (err) {
console.error('Unable to register service worker.', err);
});
}
Essa função informa ao navegador que temos um arquivo de service worker e onde ele está localizado. Nesse
caso, o arquivo do service worker está em /service-worker.js
. Em segundo plano, o navegador
realiza as seguintes etapas depois de chamar register()
:
Faça o download do arquivo do service worker.
Execute o JavaScript.
Se tudo funcionar corretamente e não houver erros, a promessa retornada por
register()
será resolvida. Se houver erros de qualquer tipo, a promessa vai ser rejeitada.
Se o
register()
for rejeitado, verifique se há erros de digitação / erros no Chrome DevTools.
Quando register()
for resolvido, ele retornará uma ServiceWorkerRegistration
. Vamos usar esse
registro para acessar a API PushManager.
Compatibilidade do navegador com a API PushManager
Solicitando permissão
Registramos nosso service worker e estamos prontos para inscrever o usuário. A próxima etapa é receber permissão do usuário para enviar mensagens push.
A API para receber permissão é relativamente simples. A desvantagem é que a API mudou recentemente de receber um callback para retornar uma promessa. O problema é que não podemos saber qual versão da API é implementada pelo navegador atual. Portanto, é necessário implementar ambos e lidar com ambos.
function askPermission() {
return new Promise(function (resolve, reject) {
const permissionResult = Notification.requestPermission(function (result) {
resolve(result);
});
if (permissionResult) {
permissionResult.then(resolve, reject);
}
}).then(function (permissionResult) {
if (permissionResult !== 'granted') {
throw new Error("We weren't granted permission.");
}
});
}
No código acima, o snippet importante é a chamada para
Notification.requestPermission()
. Este método exibirá uma solicitação ao usuário:
Quando o usuário interagir com a solicitação de permissão pressionando Permitir, Bloquear ou apenas fechá-la,
receberemos o resultado como uma string: 'granted'
, 'default'
ou 'denied'
.
No exemplo de código acima, a promessa retornada por askPermission()
é resolvida se a permissão
for concedida. Caso contrário, geramos um erro fazendo com que a promessa seja rejeitada.
Um caso extremo que você precisa lidar é se o usuário clica no botão "Bloquear". Se isso acontecer, seu app da Web não poderá solicitar a permissão do usuário novamente. Eles precisarão "desbloquear" manualmente o app mudando o estado de permissão, que está oculto em um painel de configurações. Pense bem em como e quando pedir permissão ao usuário, porque se ele clicar em "Bloquear", essa não será uma maneira fácil de reverter essa decisão.
A boa notícia é que a maioria dos usuários fica feliz em dar permissão, desde que saibam por que ela está sendo pedida.
Mais adiante, veremos como alguns sites populares pedem permissão.
Inscrever um usuário com o PushManager
Depois de registrar o service worker e receber permissão, podemos inscrever um usuário
chamando registration.pushManager.subscribe()
.
function subscribeUserToPush() {
return navigator.serviceWorker
.register('/service-worker.js')
.then(function (registration) {
const subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(
'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
),
};
return registration.pushManager.subscribe(subscribeOptions);
})
.then(function (pushSubscription) {
console.log(
'Received PushSubscription: ',
JSON.stringify(pushSubscription),
);
return pushSubscription;
});
}
Ao chamar o método subscribe()
, transmitimos um objeto options, que consiste em parâmetros obrigatórios e opcionais.
Vamos conferir todas as opções que podemos transmitir.
Opções userVisibleOnly
Quando o push foi adicionado pela primeira vez aos navegadores, não havia certeza se os desenvolvedores deveriam poder enviar uma mensagem push e não mostrar uma notificação. Isso é comumente chamado de push silencioso, porque o usuário não sabe que algo aconteceu em segundo plano.
A preocupação era que os desenvolvedores poderiam fazer coisas desagradáveis, como rastrear a localização de um usuário continuamente sem que ele soubesse.
Para evitar esse cenário e dar aos autores das especificações tempo para considerar a melhor forma de oferecer suporte a esse
recurso, a opção userVisibleOnly
foi adicionada e transmitir um valor de true
é um acordo simbólico com o navegador de que o app da Web mostrará uma notificação sempre que um push for
recebido (ou seja, nenhum push silencioso).
No momento, você precisa transmitir um valor de true
. Se você não incluir a chave userVisibleOnly
ou transmitir false
, este erro será exibido:
No momento, o Chrome só é compatível com a API Push para assinaturas que resultarão em mensagens visíveis ao usuário. Você pode indicar isso chamando
pushManager.subscribe({userVisibleOnly: true})
. Consulte https://goo.gl/yqv4Q4 para mais detalhes.
Atualmente, parece que push silenciosa geral nunca será implementada no Google Chrome. Em vez disso, os autores de especificações estão explorando a noção de uma API de orçamento que permitirá aos apps da Web um determinado número de mensagens push silenciosas com base no uso de um aplicativo da Web.
opção applicationServerKey
Mencionamos brevemente "chaves de servidor de aplicativos" na seção anterior. As "chaves de servidor de aplicativos" são usadas por um serviço de push para identificar o aplicativo que está assinando um usuário e garantir que o mesmo aplicativo esteja enviando mensagens para ele.
As chaves do servidor de aplicativos são um par de chaves pública e privada exclusiva para seu aplicativo. A chave privada precisa ser mantida em segredo para o aplicativo, e a chave pública pode ser compartilhada livremente.
A opção applicationServerKey
transmitida para a chamada subscribe()
é a chave pública
do aplicativo. O navegador transmite isso para um serviço de push durante a inscrição do usuário, o que significa que o serviço
de push pode vincular a chave pública do seu aplicativo ao PushSubscription
do usuário.
O diagrama abaixo ilustra essas etapas.
- Seu app da Web é carregado em um navegador, e você chama
subscribe()
, transmitindo a chave pública do servidor de aplicativos. - Em seguida, o navegador faz uma solicitação de rede para um serviço de push, que vai gerar um endpoint, associar esse endpoint à chave pública do aplicativo e retornar o endpoint ao navegador.
- O navegador vai adicionar esse endpoint ao
PushSubscription
, que é retornado pela promessasubscribe()
.
Quando você quiser enviar uma mensagem push mais tarde, vai precisar criar um cabeçalho Authorization que vai conter informações assinadas com a chave privada do servidor de aplicativos. Quando recebe uma solicitação para enviar uma mensagem push, o serviço de push pode validar esse cabeçalho Authorization assinado procurando a chave pública vinculada ao endpoint que recebe a solicitação. Se a assinatura for válida, o serviço de push saberá que precisa ter vindo do servidor de aplicativos com a chave privada correspondente. Ele é basicamente uma medida de segurança que impede qualquer pessoa que envie mensagens para os usuários de um aplicativo.
Tecnicamente, o applicationServerKey
é opcional. No entanto, ela é necessária para a implementação mais fácil no Chrome, e outros navegadores podem precisar dele no futuro. Ele é opcional no Firefox.
A especificação que define qual deve ser a chave do servidor de aplicativos é a especificação VAPID. Sempre que você ler algo referente a "chaves do servidor de aplicativos" ou "chaves VAPID", lembre-se de que são a mesma coisa.
Como criar chaves do servidor de aplicativos
Para criar um conjunto público e privado de chaves do servidor de aplicativos, acesse web-push-codelab.glitch.me ou use a linha de comando web-push para gerar chaves da seguinte forma:
$ npm install -g web-push
$ web-push generate-vapid-keys
Você só precisa criar essas chaves uma vez para o aplicativo. Apenas certifique-se de manter a chave privada privada. (Sim, acabei de dizer isso.)
Permissões e Subscribe()
Há um efeito colateral de chamar subscribe()
. Se o app da Web não tiver permissões para
mostrar notificações no momento de chamar subscribe()
, o navegador vai solicitar as
permissões para você. Isso é útil se a interface funcionar com esse fluxo, mas se você quiser mais
controle (e acho que a maioria dos desenvolvedores vai), use a API Notification.requestPermission()
usada anteriormente.
O que é um PushSubscription?
Chamamos subscribe()
, transmitimos algumas opções e, em troca, recebemos uma promessa que é resolvida em um
PushSubscription
, resultando em um código como este:
function subscribeUserToPush() {
return navigator.serviceWorker
.register('/service-worker.js')
.then(function (registration) {
const subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(
'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
),
};
return registration.pushManager.subscribe(subscribeOptions);
})
.then(function (pushSubscription) {
console.log(
'Received PushSubscription: ',
JSON.stringify(pushSubscription),
);
return pushSubscription;
});
}
O objeto PushSubscription
contém todas as informações necessárias para enviar uma mensagem
push a esse usuário. Se você imprimir o conteúdo usando JSON.stringify()
, verá o
seguinte:
{
"endpoint": "https://some.pushservice.com/something-unique",
"keys": {
"p256dh":
"BIPUL12DLfytvTajnryr2PRdAgXS3HGKiLqndGcJGabyhHheJYlNGCeXl1dn18gSJ1WAkAPIxr4gK0_dQds4yiI=",
"auth":"FPssNDTKnInHVndSTdbKFw=="
}
}
O endpoint
é o URL dos serviços de push. Para acionar uma mensagem push, faça uma solicitação POST
para esse URL.
O objeto keys
contém os valores usados para criptografar os dados da mensagem enviados com uma mensagem push, que abordaremos mais adiante nesta seção.
Renove a assinatura regularmente para evitar a expiração
Ao se inscrever para receber notificações push, você geralmente recebe um PushSubscription.expirationTime
de null
. Na teoria, isso significa que a assinatura nunca expira, ao contrário de quando você recebe um DOMHighResTimeStamp
, que informa o momento exato em que ela expira. No entanto, na prática, é comum que os navegadores ainda deixem as assinaturas expirarem, por exemplo, se nenhuma notificação push for recebida por mais tempo ou se o navegador detectar que o usuário não está usando um app com a permissão de notificações push. Um padrão para evitar isso é inscrever o usuário novamente a cada notificação recebida, conforme mostrado no snippet a seguir. Isso exige que você envie notificações com frequência suficiente para que o navegador não expire automaticamente a assinatura. Além disso, você deve avaliar com cuidado as vantagens e desvantagens das necessidades de notificação legítimas em relação ao envio de spam involuntário ao usuário para que a assinatura não expire. No final, você não deve tentar combater o navegador em seus esforços para proteger o usuário de inscrições de notificação esquecidas há muito tempo.
/* In the Service Worker. */
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
// Display notification or handle data
// Example: show a notification
const title = 'New Notification';
const body = 'You have new updates!';
const icon = '/images/icon.png';
const tag = 'simple-push-demo-notification-tag';
event.waitUntil(
self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag
})
);
// Attempt to resubscribe after receiving a notification
event.waitUntil(resubscribeToPush());
});
function resubscribeToPush() {
return self.registration.pushManager.getSubscription()
.then(function(subscription) {
if (subscription) {
return subscription.unsubscribe();
}
})
.then(function() {
return self.registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY_HERE')
});
})
.then(function(subscription) {
console.log('Resubscribed to push notifications:', subscription);
// Optionally, send new subscription details to your server
})
.catch(function(error) {
console.error('Failed to resubscribe:', error);
});
}
Enviar uma assinatura para seu servidor
Quando tiver uma assinatura push, você vai precisar enviá-la ao seu servidor. Você decide como
fazer isso, mas uma pequena dica é usar JSON.stringify()
para extrair todos os dados necessários do
objeto de assinatura. Como alternativa, você pode agrupar o mesmo
resultado manualmente da seguinte forma:
const subscriptionObject = {
endpoint: pushSubscription.endpoint,
keys: {
p256dh: pushSubscription.getKeys('p256dh'),
auth: pushSubscription.getKeys('auth'),
},
};
// The above is the same output as:
const subscriptionObjectToo = JSON.stringify(pushSubscription);
O envio da assinatura é feito na página da Web, desta forma:
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 do nó recebe essa solicitação e salva os dados em um banco de dados para uso posterior.
app.post('/api/save-subscription/', function (req, res) {
if (!isValidSaveRequest(req, res)) {
return;
}
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.',
},
}),
);
});
});
Com os detalhes do PushSubscription
no nosso servidor, podemos enviar uma mensagem
ao usuário sempre que quisermos.
Renove a assinatura regularmente para evitar a expiração
Ao se inscrever para receber notificações push, você geralmente recebe um PushSubscription.expirationTime
de null
. Na teoria, isso significa que a assinatura nunca expira, ao contrário de quando você recebe um DOMHighResTimeStamp
, que informa o momento exato em que ela expira. Na prática, no entanto, é comum que os navegadores ainda deixem as assinaturas expirarem, por exemplo, se nenhuma notificação push for recebida por muito tempo ou se o navegador detectar que o usuário não está usando o app que tem a permissão de notificações push. Um padrão para evitar isso é inscrever o usuário novamente a cada notificação recebida, conforme mostrado no snippet a seguir. Isso exige que você envie notificações com frequência suficiente para que o navegador não expire automaticamente a assinatura. Além disso, você deve avaliar com cuidado as vantagens e desvantagens das necessidades legítimas de notificação em relação ao envio de spam ao usuário para que a assinatura não expire. No final, você não deve tentar combater o navegador em seus esforços para proteger o usuário de inscrições de notificação esquecidas há muito tempo.
/* In the Service Worker. */
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
// Display notification or handle data
// Example: show a notification
const title = 'New Notification';
const body = 'You have new updates!';
const icon = '/images/icon.png';
const tag = 'simple-push-demo-notification-tag';
event.waitUntil(
self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag
})
);
// Attempt to resubscribe after receiving a notification
event.waitUntil(resubscribeToPush());
});
function resubscribeToPush() {
return self.registration.pushManager.getSubscription()
.then(function(subscription) {
if (subscription) {
return subscription.unsubscribe();
}
})
.then(function() {
return self.registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array('YOUR_PUBLIC_VAPID_KEY_HERE')
});
})
.then(function(subscription) {
console.log('Resubscribed to push notifications:', subscription);
// Optionally, send new subscription details to your server
})
.catch(function(error) {
console.error('Failed to resubscribe:', error);
});
}
Perguntas frequentes
Algumas perguntas comuns que as pessoas fizeram até agora:
Posso alterar o serviço de push que um navegador usa?
Não. O serviço de push é selecionado pelo navegador e, como vimos na chamada subscribe()
, o navegador fará solicitações de rede ao serviço de push para recuperar os detalhes que compõem o PushSubscription.
Cada navegador usa um serviço de push diferente, não há APIs diferentes?
Todos os serviços de push esperam a mesma API.
Essa API comum é chamada de Web Push Protocol e descreve a solicitação de rede que seu aplicativo precisará fazer para acionar uma mensagem push.
Se eu inscrever um usuário pelo computador, ele também será inscrito pelo celular?
Infelizmente, não. Um usuário precisa se registrar para push em cada navegador em que quer receber mensagens. Vale ressaltar que isso exigirá que o usuário conceda a permissão em cada dispositivo.
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