Angular v15 PWA推送通知仅PC可用,求全设备无Node实现方案
全设备推送通知实现方案(Angular 15 PWA + .NET 7,无Node依赖)
核心问题先排查
先解决现有移动端失效的常见诱因:
- 确认移动端浏览器支持Push API与PWA:Chrome、Firefox移动端原生支持,Safari需升级至16.4及以上版本
- 强制HTTPS环境:移动端仅信任HTTPS站点,本地开发可通过ngrok转发本地服务到公网HTTPS地址
- 检查Service Worker状态:移动端需确保SW完成注册并激活,可在浏览器开发者工具
Application > Service Workers面板查看
完整实现步骤
1. 清理非PWA依赖
卸载仅支持PC的ng-push-notification包,改用Angular PWA原生能力:
npm uninstall ng-push-notification
2. 配置Angular PWA基础
确保@angular/service-worker已正确集成:
- 在
angular.json中确认projects.[你的项目名].architect.build.options.serviceWorker设为true - 在
app.module.ts注册Service Worker:
import { ServiceWorkerModule } from '@angular/service-worker'; import { environment } from '../environments/environment'; @NgModule({ imports: [ // 其他模块 ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production, registrationStrategy: 'registerWhenStable:30000' }) ] }) export class AppModule { }
3. 客户端推送订阅逻辑(兼容移动端)
在Angular组件或服务中实现订阅流程:
import { SwPush } from '@angular/service-worker'; import { HttpClient } from '@angular/common/http'; constructor(private swPush: SwPush, private http: HttpClient) {} async subscribeToPush() { if (!this.swPush.isEnabled) { alert('当前环境不支持推送通知,请确保浏览器开启PWA支持'); return; } // 请求通知权限 const permission = await Notification.requestPermission(); if (permission !== 'granted') { alert('需开启通知权限才能接收推送'); return; } // 从.NET后端获取VAPID公钥 const vapidPublicKey = await this.http.get<string>('/api/push/vapidpublickey').toPromise(); const convertedKey = this.urlBase64ToUint8Array(vapidPublicKey); // 发起推送订阅 const subscription = await this.swPush.requestSubscription({ serverPublicKey: convertedKey }); // 将订阅信息提交到后端存储 await this.http.post('/api/push/subscribe', subscription).toPromise(); } // 转换VAPID公钥格式的辅助函数 private urlBase64ToUint8Array(base64String: string): Uint8Array { 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))); }
4. .NET 7后端实现(无Node依赖)
集成WebPush库
通过NuGet安装推送依赖:
Install-Package WebPush
生成VAPID密钥对(仅需生成一次,存储到appsettings.json):
using WebPush; var vapidKeys = VapidHelper.GenerateVapidKeys(); // 示例:将公钥和私钥写入appsettings.json // "VapidPublicKey": "生成的公钥", // "VapidPrivateKey": "生成的私钥"
订阅存储接口
创建API接收客户端订阅信息并存储(可使用SQL Server、Redis等):
[ApiController] [Route("api/push")] public class PushController : ControllerBase { private readonly IPushSubscriptionRepository _subscriptionRepo; public PushController(IPushSubscriptionRepository subscriptionRepo) { _subscriptionRepo = subscriptionRepo; } [HttpPost("subscribe")] public async Task<IActionResult> SaveSubscription([FromBody] PushSubscription subscription) { await _subscriptionRepo.UpsertSubscription(subscription); return Ok(); } }
推送发送逻辑
编写发送推送的方法,处理订阅过期等异常:
public async Task SendPushNotification(PushSubscription subscription, string title, string content) { var vapidDetails = new VapidDetails( "mailto:your-contact@example.com", Configuration["VapidPublicKey"], Configuration["VapidPrivateKey"] ); var webPushClient = new WebPushClient(); var payload = JsonSerializer.Serialize(new { title, content }); var payloadBytes = Encoding.UTF8.GetBytes(payload); try { await webPushClient.SendNotificationAsync(subscription, payloadBytes, vapidDetails); } catch (WebPushException ex) { // 订阅过期时从存储中移除 if (ex.StatusCode == HttpStatusCode.Gone) { await _subscriptionRepo.DeleteSubscription(subscription.Endpoint); } } }
5. 移动端适配细节
- 完善Web App Manifest:确保
manifest.webmanifest包含name、short_name、start_url、display: standalone,让用户可将PWA添加到移动端主屏幕 - 配置通知行为:在
ngsw-config.json中设置通知图标、点击逻辑:
{ "notificationOptions": { "icon": "assets/icons/icon-192x192.png", "badge": "assets/icons/badge-72x72.png" }, "push": { "showNotifications": true, "backgroundNotification": "cache-and-update" } }
- iOS Safari适配:iOS 16.4+版本需用户手动将PWA添加到主屏幕,才能接收推送通知
验证方案
- 本地测试:用ngrok转发本地HTTPS服务到公网,在移动端访问转发地址完成订阅测试
- 生产验证:部署到HTTPS服务器,测试移动端添加主屏幕后的推送接收情况
内容的提问来源于stack exchange,提问作者RowdyCodeU
相关产品推荐
相关产品推荐

