如何在Rails 8中使用自动生成的Service Worker实现Web推送通知
如何在Rails 8中使用自动生成的Service Worker实现Web推送通知
嘿,刚好我最近折腾过Rails 8的这个自动生成的Service Worker,它已经帮你把推送通知的核心事件监听写好了,剩下的就是补全前后端的订阅、存储和发送流程,我一步步给你说清楚:
1. 先确认自动生成的Service Worker就绪
你看到的app/views/pwa/service-worker.js文件完全不用改!它已经帮你实现了两个核心逻辑:
push事件:接收后端发来的通知数据,调用浏览器的通知API显示弹窗notificationclick事件:处理用户点击通知后的行为——如果对应页面已经打开就聚焦,否则打开指定路径
咱们只需要确保这个Service Worker能被浏览器正常注册就行,Rails 8已经帮你配置好了基础的路由,不用额外改配置。
2. 前端实现推送订阅功能
接下来要在前端写代码,让用户授权通知权限,生成订阅信息并传给后端存储。咱们可以把代码放在一个控制器里(比如用Rails的Import Maps或者Turbo):
// 比如放在app/javascript/controllers/push_notifications_controller.js document.addEventListener('DOMContentLoaded', async () => { // 先检查浏览器是否支持Web推送 if (!('serviceWorker' in navigator) || !('PushManager' in window)) { console.log('你的浏览器不支持Web推送通知哦'); return; } try { // 注册Rails自动生成的Service Worker const registration = await navigator.serviceWorker.register('/pwa/service-worker.js'); // 请求用户授权通知权限 const permission = await Notification.requestPermission(); if (permission !== 'granted') { alert('得允许通知权限才能收到推送哦'); return; } // 这里填后面后端生成的VAPID公钥,先记着,后面会说怎么生成 const vapidPublicKey = '替换成你的VAPID公钥'; const convertedKey = urlBase64ToUint8Array(vapidPublicKey); // 创建推送订阅对象 const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, // 要求所有推送都对用户可见 applicationServerKey: convertedKey }); // 把订阅信息发给后端存储 await fetch('/push_subscriptions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content }, body: JSON.stringify(subscription) }); console.log('已经成功订阅推送通知啦'); } catch (error) { console.error('订阅失败了:', error); } }); // 辅助函数:把VAPID公钥转成浏览器需要的格式 function urlBase64ToUint8Array(base64String) { const padding = '='.repeat((4 - (base64String.length % 4)) % 4); const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/'); const rawData = window.atob(base64); return Uint8Array.from([...rawData].map(char => char.charCodeAt(0))); }
3. 后端配置VAPID密钥并存储订阅信息
Web Push需要用VAPID密钥验证后端身份,防止恶意推送,咱们用webpush gem来处理:
第一步:生成VAPID密钥
先在Gemfile里加一行:
gem 'webpush'
然后跑bundle install,接着打开Rails控制台(rails c),运行以下代码生成密钥:
require 'webpush' vapid_keys = Webpush.generate_key puts "VAPID公钥: #{vapid_keys.public_key}" puts "VAPID私钥: #{vapid_keys.private_key}"
把输出的公钥和私钥存到环境变量里(比如.env文件),别直接写进代码里:
VAPID_PUBLIC_KEY=你的公钥内容 VAPID_PRIVATE_KEY=你的私钥内容
第二步:创建存储订阅的模型和控制器
生成一个模型来存用户的订阅信息:
rails generate model PushSubscription endpoint:string p256dh:string auth:string user:references rails db:migrate
这里的user:references是关联你的用户模型(如果不需要关联用户可以删掉),然后创建控制器处理前端的订阅请求:
# app/controllers/push_subscriptions_controller.rb class PushSubscriptionsController < ApplicationController skip_before_action :verify_authenticity_token, only: [:create] # 或者确保CSRF Token正确传递 def create subscription_params = JSON.parse(request.body.read) PushSubscription.create!( endpoint: subscription_params['endpoint'], p256dh: subscription_params['keys']['p256dh'], auth: subscription_params['keys']['auth'], user_id: current_user&.id # 关联当前用户,没有的话删掉这行 ) head :ok end end
最后在config/routes.rb里加路由:
Rails.application.routes.draw do # ... 其他路由 post '/push_subscriptions', to: 'push_subscriptions#create' end
4. 后端发送推送通知
现在就可以在业务逻辑里给用户发推送了,比如用户下单后、收到评论后,咱们可以写一个工具方法:
# 可以放在app/services/push_notification_service.rb里,或者某个控制器里 def send_push_notification(subscription, title, options) # 构造符合Service Worker要求的 payload webpush_payload = { title: title, options: { body: options[:body], icon: options[:icon] || '/assets/icon.png', # 可选,设置通知的图标 data: { path: options[:path] || '/' # 点击通知后跳转的路径 } } } # 调用webpush gem发送通知 Webpush.payload_send( endpoint: subscription.endpoint, message: JSON.stringify(webpush_payload), p256dh: subscription.p256dh, auth: subscription.auth, vapid: { public_key: ENV['VAPID_PUBLIC_KEY'], private_key: ENV['VAPID_PRIVATE_KEY'] } ) end
调用示例(比如在订单创建后):
# 假设你有一个订单创建的动作 def create @order = Order.create(order_params) # 给当前用户发推送 subscription = PushSubscription.find_by(user_id: current_user.id) if subscription send_push_notification(subscription, '订单创建成功', { body: '你的订单已经提交,正在处理中', path: "/orders/#{@order.id}" }) end # ... 其他逻辑 end
5. 测试注意事项
- 必须在HTTPS环境下测试(localhost除外,Chrome等浏览器允许本地用HTTP测试),如果要在真机测试,可以用ngrok把本地服务转发成HTTPS
- 订阅信息可能会过期(比如用户清了浏览器数据、换了设备),发送失败的话可以把对应的订阅记录删掉
- 通知权限用户随时可以关掉,所以前端最好加个提示,引导用户重新开启
这样一套流程走下来,你就能用Rails 8自动生成的Service Worker实现完整的Web推送通知啦!




