集成WCF Service Callback与Web Push API实现离线Web通知
嘿,这个需求我之前折腾过类似的,刚好给你梳理清楚完整流程!要实现浏览器没打开也能收到WCF的回调通知,核心就是把WCF的回调消息转成Web Push能识别的格式,再结合Service Worker处理离线推送。下面一步步来:
一、先搞定基础环境:HTTPS是刚需
Web Push API强制要求HTTPS环境(localhost开发时例外),所以你的Web应用和WCF服务最好都部署在HTTPS环境下,不然浏览器会直接拦截推送请求,这点一定要注意。
二、集成Service Worker:离线处理的核心
Service Worker是浏览器后台运行的脚本,专门用来处理离线事件,包括推送通知。
- 在Web应用主页面注册Service Worker
// 先检查浏览器是否支持Service Worker和Push API if ('serviceWorker' in navigator && 'PushManager' in window) { window.addEventListener('load', async () => { try { // 注册根目录下的sw.js文件,注意路径对作用域的影响 const registration = await navigator.serviceWorker.register('/sw.js'); console.log('Service Worker注册成功:', registration); // 后续的推送订阅会用到这个registration对象 } catch (error) { console.error('Service Worker注册失败:', error); } }); }
- 创建
sw.js文件处理推送事件
// 监听推送消息到来的事件 self.addEventListener('push', (event) => { // 解析推送携带的JSON数据,没有的话用默认值 const data = event.data?.json() || { title: '新通知', body: '您有一条新消息' }; const notificationOptions = { body: data.body, icon: '/assets/notification-icon.png', // 可选:通知显示的图标 badge: '/assets/notification-badge.png', // 可选:系统状态栏的小图标 data: data.payload // 携带额外数据,比如WCF回调的详细内容 }; // 显示通知,waitUntil确保Service Worker不会提前终止 event.waitUntil( self.registration.showNotification(data.title, notificationOptions) ); }); // 可选:监听通知点击事件,点击后打开应用页面 self.addEventListener('notificationclick', (event) => { event.notification.close(); event.waitUntil( clients.openWindow('/') // 打开应用主页,也可以跳转到具体详情页 ); });
三、实现推送订阅:把用户信息同步给WCF
要让WCF能给用户发推送,得先把用户的推送订阅信息保存到服务端,关联到具体用户账号。
- 在前端获取用户的推送订阅
在Service Worker注册成功后,添加订阅逻辑:
async function subscribeUser(registration) { // 先申请用户的通知权限,必须用户同意才能订阅 const permission = await Notification.requestPermission(); if (permission !== 'granted') { throw new Error('用户拒绝了通知权限,无法接收推送'); } // VAPID公钥(后面会说怎么生成) const vapidPublicKey = '你的VAPID公钥字符串'; // 把Base64格式的公钥转换成Uint8Array,Push API要求的格式 const convertedKey = urlBase64ToUint8Array(vapidPublicKey); // 创建推送订阅 const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, // 必须设为true:所有推送都要对用户可见 applicationServerKey: convertedKey }); // 将订阅信息发送给WCF服务,保存到数据库(关联用户ID) await fetch('/api/SubscribeToPush', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(subscription) }); } // 辅助函数:把URL安全的Base64转成Uint8Array 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))); }
- 生成VAPID密钥对
VAPID用来验证你的服务器身份,防止恶意推送。可以用Node.js的web-push工具生成:
# 先全局安装web-push npm install web-push -g # 生成密钥对 web-push generate-vapid-keys
生成后会得到公钥(给前端用)和私钥(保存在WCF服务端,绝对不能暴露给前端)。
四、WCF服务触发Web Push通知
当WCF需要回调用户时,取出之前保存的用户订阅信息,调用Web Push服务的API发送通知。这里用C#代码示例:
using System.Net.Http; using System.Text; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Cryptography; using Microsoft.IdentityModel.Tokens; public async Task SendPushNotificationToUser(string userId, string title, string body, object payload) { // 从数据库取出该用户的推送订阅信息 var subscription = await GetUserPushSubscription(userId); if (subscription == null) return; var vapidPrivateKey = "你的VAPID私钥"; var vapidPublicKey = "你的VAPID公钥"; var subject = "mailto:your-support@example.com"; // 你的联系方式,浏览器会显示给用户 // 构造推送内容 var pushContent = new { title = title, body = body, payload = payload }; var jsonContent = JsonSerializer.Serialize(pushContent); var content = new StringContent(jsonContent, Encoding.UTF8, "application/json"); // 生成VAPID签名的JWT令牌 var securityKey = new ECDsaSecurityKey(ECDsa.CreateFromPem(vapidPrivateKey)); var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.EcdsaSha256); var jwtToken = new JwtSecurityToken( issuer: null, audience: new Uri(subscription.Endpoint).GetLeftPart(UriPartial.Authority), claims: new[] { new Claim("sub", subject) }, expires: DateTime.UtcNow.AddHours(1), signingCredentials: credentials ); var jwt = new JwtSecurityTokenHandler().WriteToken(jwtToken); // 发送推送请求到Web Push服务 using var client = new HttpClient(); client.DefaultRequestHeaders.Add("Authorization", $"vapid t={jwt}, k={vapidPublicKey}"); client.DefaultRequestHeaders.Add("TTL", "3600"); // 消息存活时间(秒),如果用户离线,会缓存到浏览器上线再推送 var response = await client.PostAsync(subscription.Endpoint, content); response.EnsureSuccessStatusCode(); } // 对应前端传来的订阅信息的实体类 public class PushSubscription { public string Endpoint { get; set; } public PushSubscriptionKeys Keys { get; set; } } public class PushSubscriptionKeys { public string P256dh { get; set; } public string Auth { get; set; } }
五、关键注意事项
- 权限问题:必须先获取用户的通知权限,否则订阅会失败,一定要友好引导用户开启权限。
- Service Worker作用域:
sw.js最好放在根目录,不然只能处理该目录下页面的推送事件。 - VAPID密钥安全:私钥绝对不能泄露到前端,否则别人可以冒充你的服务器发推送。
- 离线测试:测试时可以关闭浏览器,用WCF触发推送,再打开浏览器就能在系统通知栏看到消息了。
这样一套流程走下来,就能实现浏览器未运行时接收WCF的回调通知啦!如果某个环节卡壳,比如VAPID签名出错或者Service Worker注册失败,可以再细化排查~
内容的提问来源于stack exchange,提问作者SiD
相关产品推荐
相关产品推荐

