Atualmente, espera-se que a maior parte dos sites sejam aplicações web dinâmicas que funcionem através de toda a gama de dispositivos e navegadores disponíveis. Já que a forma de acesso mais utilizada atualmente é através de dispositivos móveis, os usuários esperam um nível de interatividade próximo aos apps no desktop. No sentido de melhorar a experiência de usuário nesse aspecto, a funcionalidade de enviar push notifications para os usuários da plataforma é uma maneira de manter o engajamento constante. Nesse post, vamos ver um exemplo de configuração e implementação do envio de notificações através do protocolo VAPID (Voluntary Application Server Identification) utilizando o framework Ruby on Rails.
Como o Web Push funciona?
Para implementar um Webpush você precisa cumprir os seguintes pré-requisitos:
- Um servidor Rails configurado para service workers.
- O recebedor da notificação acessando através de um navegador que suporte Web Push
- Um provedor de serviço de push, como o Google ou Mozilla
Implementando Web Push
Configurando as chaves Vapid
- O primeiro passo para enviar uma mensagem é configurar uma única vez o par de chaves VAPID (pública e privada) para salvar em seu servidor. Essas chaves vão ser usadas para assinar suas requisições à API
- Um arquivo manifest.json que auxilie a identificação da aplicação
- Configurar um service worker e instalar no navegador do usuário. O service worker é inscrito para eventos do tipo push com sua chave VAPID
- Seu servidor precisará fazer requisições à API para enviar as mensagens ao usuário inscrito
- Seu service worker conterá o um evento que será disparado no recebimento da notificação
O diagrama abaixo dá um sobrevoo de todo o fluxo:
Primeiros passos
Você pode utilizar este repositório (créditos ao Lachi Agnew) para acompanhar ou partir da sua aplicação em Rails.
Gems a serem instaladas:
- WebPush
Instale na raiz do projeto através do comando:
1 |
$ gem install webpush |
Essa gem é usada para manipular os dados de push (criptografia e requisições)
- Serviceworkers:
1 |
$ gem install serviceworker-rails |
O Serviceworkers-rails é uma gem que permite que o servidor hospede um arquivo service-worker.js como se ele estivesse no diretório raiz. Um service worker é basicamente um javascript que está sempre executando em plano de fundo, mesmo quando a página não está aberta. Eles têm muitas aplicações úteis além de escutar às notificações push, como carregamento offline e sincronizações. Para aprender mais sobre: https://developers.google.com/web/fundamentals/primers/service-workers/
Configurando as chaves VAPID:
Abra o seu console ruby e execute os comandos abaixo:
Setting up your VAPID keys gives your server a unique signature to sign the messages send out over the subscription.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#No console require 'webpush' # Uma vez, no servidor vapid_key = Webpush.generate_key # Salve essas informações para utilizar na configuração do serviço vapid_key.public_key # => "BC1mp...HQ=" vapid_key.private_key # => "XhGUr...Kec" |
Declarando o manifest.json
O manifest.json é usado para passar as configurações do servidor que está enviando as notificações para o navegador
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# app/assets/javascript { "name":"My App", "short_name":"my-app", "start_url":"/", "icons":[ { "src":"/images/my-push-logo-192x192.png", "sizes":"192x192", "type":"image/png"
} ] } |
Adicione o link na tag head da sua aplicação:
1 2 |
<!-- index.html --> <link rel="manifest" href="/manifest.json" /> |
Configurando o Service Worker
Instale os arquivos do service worker:
1 |
rails g serviceworker:install |
Declare todos os arquivos que você deseja que sejam incluídos com o service worker:
1 2 3 4 5 6 7 8 9 10 |
#config/initializers/serviceworker.rb WebPushDemo::Application.configure do config.serviceworker.routes.draw do # map to assets implicitly match "/serviceworker.js" match "/manifest.json" match "/logo.png" end end |
Configurando o Service Worker para escutar o push
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// app/assets/javascript/serviceworker.js self.addEventListener("push", function(event) { var title = (event.data && event.data.text().split("-")[0]) || "Message recieved"; var body; body = event.data.text().split("-")[1]; var tag = "push-simple-demo-notification-tag"; var icon = 'logo.png'; event.waitUntil( self.registration.showNotification(title, { body: body, icon: icon, tag: tag }) ); }); |
Obtendo as informações da inscrição
Para que o servidor consiga enviar uma notificação, você primeiro precisa se inscrever para a chave VAPID pública.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
//app/views/content/index.html.erb javascript: var vapidPublicKey = new Uint8Array(#{Base64.urlsafe_decode64($vapid_public).bytes}); function checkNotifs(obj){ if (!("Notification" in window)) { //1 console.error("This browser does not support desktop notification"); } // Let's check whether notification permissions have already been granted else if (Notification.permission === "granted") { //2 console.log("Permission to receive notifications has been granted"); getKeys(); } // Otherwise, we need to ask the user for permission else if (Notification.permission !== 'denied') { //3 Notification.requestPermission(function (permission) { // If the user accepts, let's create a notification if (permission === "granted") { //4 console.log("Permission to receive notifications has been granted"); getKeys(); } }); } } function getKeys(){ navigator.serviceWorker.register('/serviceworker.js', {scope: './'}) //5 .then(function(registration) { return registration.pushManager.getSubscription() .then(function(subscription) { if (subscription) { return subscription; } return registration.pushManager.subscribe({ //6 userVisibleOnly: true, applicationServerKey: vapidPublicKey }); }); }).then(function(subscription) { sendKeys(subscription.toJSON()) //7 }); } function sendKeys(subscription){ $.post('/sendkeys, { subscription: subscription, message: 'You clicked a button!' }); } |
É um trecho de código longo, então explicando por partes, conforme delimitado nos comentários:
- Se o navegador não suporta notificações, lança uma exceção e não continua
- Se já tem permissão para notificações, pegar os dados da inscrição
- Se ainda não tem a permissão, requisita para o envio das notificações
- Se o usuário aceitar, obtém os dados da inscrição
- Registra o service worker
- Inscreve o service worker para a chave pública do servidor
- Envia os dados da inscrição para o servidor
Ao enviar os dados para o servidor, no backend, esses devem ser salvos de forma a permitir a identificação do usuário para o qual se deseja enviar a notificação.
Enviando uma notificação push
Finalmente, agora você tem todos os dados necessários para mandar a mensagem ao cliente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# app/services/PushNotifier def self.sendPush(user) @message = get_message() if user.notification_data_id.present? @notification_data = NotificationData.find(user.notification_data_id) Webpush.payload_send(endpoint: @notification_data.endpoint, message: @message, p256dh: @notification_data.p256dh_key, auth: @notification_data.auth_key, ttl: 24 * 60 * 60, vapid: { subject: 'mailto:admin@commercialview.com.au', public_key: $vapid_public, private_key: $vapid_private } ) end end |
Com as informações da inscrições que armazenou no servidor, você pode enviar a notificação usando a gem web push conforme indicado acima. Quaisquer dúvidas ou sugestões, utilize a área de comentários ou entre em contato!
5 Comentários
Daniel Siebra Pereira
3 de Maio de 2020 at 02:38Shooow cara, gostei muito do conteúdo, parabéns!
Joathan
18 de agosto de 2020 at 00:01Ótimo artigo. Como que eu posso validar se o endpoint para envio do push ainda é válido ?
Ronan Lopes
21 de agosto de 2020 at 07:34Obrigado, Joathan! Em uma pesquisa rápida (https://stackoverflow.com/questions/48281148/how-to-check-if-a-push-endpoint-is-still-valid), no backend você não deve conseguir checar a validade do endpoint em termos do usuário que vai receber a notificação. Entretanto, como o endpoint é uma url do firebase cloud messaging, ainda que o endpoint não seja mais válido, isso não gera uma excessão pra sua aplicação, pode fazer um envio “dummy” sem problemas.
Adriano
31 de Março de 2021 at 08:34Opa, como podemos utilizar isso para mandar notificaçoes para o mobile? o browser mobile não aceita o subscriber
Ronan Lopes
31 de Março de 2021 at 15:22Boa tarde, Adriano! Em meus testes consegui utilizar para navegadores em dispositivos móveis sem problemas, inclusive com a possibilidade de customização de ícone, som, etc. Se conseguir descrever o erro no seu processo, talvez consiga te ajudar. Abraço