Laravel 中实现 Web Push(网页推送通知)
在 Laravel 中实现 Web Push(网页推送通知)可以通过结合 Laravel 的通知系统和专门的 Web Push 扩展包来完成。以下是详细的实现步骤:
一、准备工作
Web Push 依赖以下技术:
- Service Worker:运行在浏览器后台的脚本,负责接收推送消息
- VAPID:用于服务器与浏览器之间的身份验证
- Push API:浏览器提供的接收推送的接口
- Notification API:用于显示系统通知
我们将使用 laravel-notification-channels/webpush 扩展包,它简化了 Laravel 中 Web Push 的实现。
二、安装与配置
1. 安装扩展包
bash
composer require laravel-notification-channels/webpush
2. 发布配置文件和迁移文件
bash
php artisan vendor:publish --provider="NotificationChannels\WebPush\WebPushServiceProvider"
这会生成:
- 配置文件:
config/webpush.php - 迁移文件:
database/migrations/xxxx_xx_xx_xxxxxx_create_web_push_subscriptions_table.php
3. 生成 VAPID 密钥
VAPID(Voluntary Application Server Identification)用于验证服务器身份,运行以下命令生成:
bash
php artisan webpush:vapid
生成的密钥会自动写入 .env 文件:
env
WEBPUSH_VAPID_PUBLIC_KEY=your-public-key
WEBPUSH_VAPID_PRIVATE_KEY=your-private-key
WEBPUSH_VAPID_SUBJECT=mailto:your-email@example.com # 通常是管理员邮箱
4. 运行迁移
创建存储用户订阅信息的数据表:
bash
php artisan migrate
三、创建订阅逻辑
1. 定义用户模型关联
在 User 模型中添加与订阅的关联:
php
// app/Models/User.php
use NotificationChannels\WebPush\Models\WebPushSubscription;
public function webPushSubscriptions()
{
return $this->hasMany(WebPushSubscription::class);
}
2. 创建订阅控制器
新建控制器处理用户订阅请求:
bash
php artisan make:controller WebPushController
实现订阅和取消订阅的逻辑:
php
// app/Http/Controllers/WebPushController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use NotificationChannels\WebPush\Models\WebPushSubscription;
class WebPushController extends Controller
{
// 保存用户订阅
public function store(Request $request)
{
$request->validate([
'endpoint' => 'required|url',
'keys.p256dh' => 'required',
'keys.auth' => 'required',
]);
$subscription = WebPushSubscription::updateOrCreate(
[
'endpoint' => $request->endpoint,
],
[
'user_id' => auth()->id(), // 关联当前登录用户
'public_key' => $request->keys['p256dh'],
'auth_token' => $request->keys['auth'],
'content_encoding' => 'aesgcm',
]
);
return response()->json(['success' => true]);
}
// 取消订阅
public function destroy($endpoint)
{
WebPushSubscription::where('endpoint', $endpoint)->delete();
return response()->json(['success' => true]);
}
}
3. 注册路由
在 routes/web.php 中添加路由:
php
use App\Http\Controllers\WebPushController;
Route::post('/web-push/subscribe', [WebPushController::class, 'store'])->middleware('auth');
Route::delete('/web-push/unsubscribe/{endpoint}', [WebPushController::class, 'destroy'])->middleware('auth');
四、创建推送通知类
1. 生成通知类
bash
php artisan make:notification NewsNotification
2. 实现 Web Push 通知
php
// app/Notifications/NewsNotification.php
namespace App\Notifications;
use Illuminate\Notifications\Notification;
use NotificationChannels\WebPush\WebPushChannel;
use NotificationChannels\WebPush\WebPushMessage;
class NewsNotification extends Notification
{
private $news;
public function __construct($news)
{
$this->news = $news; // 传递需要推送的内容
}
// 指定通知渠道
public function via($notifiable)
{
return [WebPushChannel::class];
}
// 构建 Web Push 消息
public function toWebPush($notifiable, $notification)
{
return (new WebPushMessage)
->title('新消息通知')
->body($this->news['content'])
->icon('/icon.png') // 通知图标
->action('查看详情', 'view_details') // 通知按钮
->data([
'url' => route('news.show', $this->news['id']), // 点击通知跳转的链接
])
->options(['TTL' => 60 * 15]); // 消息存活时间(15分钟)
}
}
五、前端实现(关键)
1. 注册 Service Worker
在 public 目录下创建 service-worker.js:
javascript
// public/service-worker.js
self.addEventListener('push', function(event) {
const data = event.data?.json() ?? { title: '新通知' };
const options = {
body: data.body,
icon: data.icon || '/icon.png',
data: { url: data.url }, // 点击通知跳转的URL
actions: data.actions || [],
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
});
// 点击通知事件
self.addEventListener('notificationclick', function(event) {
event.notification.close();
// 跳转到指定页面
event.waitUntil(
clients.openWindow(event.notification.data.url || '/')
);
});
2. 前端订阅逻辑
在页面中添加订阅代码(可放在 Blade 模板或 Vue/React 组件中):
html
预览
<!-- 引入 Web Push 库 -->
<script src="https://cdn.jsdelivr.net/npm/web-push@3.6.7/build/web-push.min.js"></script>
<script>
// 初始化订阅
async function initializeWebPush() {
// 检查浏览器支持
if (!('serviceWorker' in navigator && 'PushManager' in window)) {
console.log('浏览器不支持 Web Push');
return;
}
try {
// 注册 Service Worker
const registration = await navigator.serviceWorker.register('/service-worker.js');
// 获取 VAPID 公钥(从后端获取)
const publicKey = '{{ config('webpush.vapid.public_key') }}';
// 订阅推送
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true, // 所有推送都必须显示给用户
applicationServerKey: urlBase64ToUint8Array(publicKey)
});
// 将订阅信息发送到后端保存
await fetch('/web-push/subscribe', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': '{{ csrf_token() }}'
},
body: JSON.stringify(subscription)
});
console.log('订阅成功');
} catch (error) {
console.error('订阅失败:', error);
}
}
// 辅助函数:将 VAPID 公钥转换为 Uint8Array
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
const rawData = atob(base64);
return Uint8Array.from([...rawData].map(char => char.charCodeAt(0)));
}
// 页面加载时初始化
window.addEventListener('load', initializeWebPush);
</script>
六、发送推送通知
在需要发送通知的地方(如控制器)调用通知:
php
// 示例:当有新消息时发送推送
public function sendNewsNotification()
{
$user = auth()->user(); // 目标用户
$news = [
'id' => 1,
'content' => '这是一条新消息通知!'
];
$user->notify(new \App\Notifications\NewsNotification($news));
return back()->with('message', '通知已发送');
}
七、测试与注意事项
- HTTPS 环境:Web Push 仅在 HTTPS 环境下工作(本地开发可使用
localhost)。 - 浏览器支持:主流浏览器(Chrome、Firefox、Edge)均支持,Safari 需 v16+。
- 用户授权:首次使用需用户允许通知权限。
- 调试工具:使用浏览器的「应用程序」面板查看 Service Worker 和推送状态。
- 通知图标:建议准备 192x192px 的图标,放在
public目录下。
通过以上步骤,你可以在 Laravel 中实现完整的 Web Push 功能,向用户发送实时通知,提升用户活跃度和互动性。

浙公网安备 33010602011771号