You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在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推送通知啦!

火山引擎 最新活动