☰
华为Web Push接入实战:从Service Worker到后端API全链路解析
2026/10/13 19:08:01 网站建设 项目流程

作为从业者,聊聊华为Web Push的接入。

华为Web Push,简单说就是华为消息推送能力在Web浏览器环境下的完整落地。过去给Web站点做消息触达,要么自己维护WebSocket长连接,要么接入第三方推送,前者在移动端浏览器上经常被系统杀掉进程,后者在华为设备上又可能因为省电策略出现消息延迟。华为Web Push解决的就是这个问题:浏览器在页面关闭状态下依然能收到通知,并且借助厂商系统级通道,到达率和及时性都比自建通道可靠不少。这篇文章我从账号配置、前端Service Worker接入、后端API调用到问题排查,把完整链路串一遍,适合正在评估或已经决定接入的开发者参考,前后端同学都能找到自己关心的那部分。

1. 整体架构与设计思路

1.1 Web推送的基本运转逻辑

先说清楚Web推送是怎么工作的。标准Web Push协议里有三个角色:浏览器、推送服务、应用服务器。浏览器里常驻一个Service Worker脚本,它是消息到达时唯一能“醒过来”处理通知的地方。推送服务负责把消息从应用服务器转投给浏览器,这个服务由浏览器厂商或系统厂商运营。应用服务器也就是我们的后端,通过一段受信认证用标准协议把消息发给推送服务。

用户第一次访问站点时,前端通过pushManager.subscribe()发起订阅,浏览器会返回一个Endpoint地址和一组加密密钥。这个Endpoint可以理解为这台浏览器在该推送服务上的“门牌号”,后端保存下来,之后发消息就是往这个地址投递。

华为Web Push本质上是把标准Web Push协议中的推送服务替换成了华为自家的推送通道,同时在前端能力和服务端API上做了适配。好处在于,在华为设备上这个通道和系统级推送是一体的,省电策略不会随便拦截;网页关掉、浏览器退到后台,消息依然能到达。

1.2 为什么选华为Web Push而不是自建通道

我看过不少团队在自建推送和接入厂商通道之间反复横跳。自建WebSocket的思路很直观:自己维护一条长连接,后端能随时下推。但实际运营下来有几个问题绕不过去:

  • 移动端浏览器对长期后台连接并不友好,页面切到后台后浏览器会冻结定时器、断掉WebSocket,消息就断了。
  • 一台设备的并发连接要维持心跳,量一大,服务器带宽和连接数成本明显上涨。
  • 用户关掉页面标签后,网页级连接直接失效,完全收不到消息。

华为Web Push把连接维护这件事交给了系统和浏览器底层,应用服务器只需要在需要时调用发送接口。用户关掉页面也能收到通知,这个体验差异对于资讯、导购、协同工具这类站点非常关键。当然,它不是万能药,如果你需要的是浏览器内实时双向通信,WebSocket仍然有用;但单向触达提醒的场景,Web推送是更经济可靠的选择。

1.3 整体接入链路的三个环节

整个接入链路可以分为前端、服务端和平台配置三块:

  • 平台配置:在开发者后台创建Web应用,开通推送服务,拿到App ID、App Secret和VAPID密钥。
  • 前端:注册Service Worker,请求通知权限,订阅推送,拿到Token后上报给后端。
  • 后端:用App ID和App Secret换取Access Token,调用发送接口向指定Token投递消息。

这三块环环相扣。最容易出问题的不是某一个环节特别难,而是信息不对齐——前端用了A密钥,后端用B密钥;或者前端Token拿不到,后端盲目发消息报错。后面的内容就按这个链路顺序展开。

2. 接入前的准备工作:账号、应用与密钥

2.1 创建Web应用并开通推送服务

在开发者后台创建应用这一步其实不复杂,但很多人在“应用类型选什么”上容易犹豫。Web Push接入时,应用类型要选择Web,不是Android也不是快应用。创建成功后,在“开发服务”菜单下找到“推送服务”,点击开通。不同开发者账号的界面布局可能略有差异,但入口名一般是“Push Kit”或“推送服务”,找开发服务那一栏基本不会错。

开通后要关注的是应用信息页里的两个值:App ID和App Secret。App ID相当于应用在平台上的身份证,App Secret是调用服务端API时的密钥,这两个值一走完注册流程就能看到。

注意:App Secret等同于密码,不能出现在前端代码里,更不能提交到仓库。如果泄露了,后台要支持重置,重置后老密钥立刻失效,所有依赖它获取Token的服务端逻辑都得同步换。

2.2 生成VAPID密钥对

VAPID是Web Push标准里的应用服务器身份标识机制,核心是一对公钥和私钥。浏览器在订阅时会校验你传进来的公钥,后端发消息时用私钥签名,推送服务通过公钥验证请求来源。华为Web Push沿用这套机制,所以在后台的推送服务配置页里,需要生成VAPID密钥对。

生成完会得到两个字符串:公钥用来放在前端订阅代码里,私钥用于后端拼接发送请求时的认证信息。有的开发者文档里也把VAPID公钥叫applicationServerKey,理解了这一点,看到不同叫法就不会发懵。

这里有一个实操中容易踩的坑:VAPID密钥对生成之后,后台一般允许重新生成,但一旦重新生成,所有前端已订阅的Token和新的订阅都会按新密钥走,旧Token可能全部失效。所以千万不要在生产环境随意重置这个密钥,确需重置时,要做好批量清洗旧Token的准备。

2.3 环境要求与兼容性清单

Web Push对运行环境有硬性要求,接入前先对照确认:

项目要求
协议站点必须是HTTPS,本地开发可用localhost豁免
浏览器Chrome、Edge、Firefox等主流浏览器,版本需支持Service Worker和PushManager
权限用户必须同意通知权限后才能订阅成功
前后端通信需要有后端接口接收Token并下发消息

兼容性层面,苹果Safari的历史版本不支持标准Web Push,目前在桌面端较新版本才逐步支持。如果产品的主要用户大量使用Safari,需要提前评估这个限制,或者给不支持的浏览器做降级处理,比如站内提示替代方案。这个问题不只是华为Web Push有,所有基于Web Push标准的方案都会遇到。

3. 前端接入核心实现

3.1 注册Service Worker

前端接入的第一步是注册Service Worker脚本,这个脚本是整个推送接收的中枢。代码通常放在页面入口:

if ('serviceWorker' in navigator && 'PushManager' in window) { window.addEventListener('load', function () { navigator.serviceWorker.register('/sw.js') .then(function (registration) { console.log('Service Worker 注册成功'); window.registration = registration; }) .catch(function (error) { console.error('Service Worker 注册失败', error); }); }); }

注册成功后,registration对象就是后续订阅推送的入口。注册时机建议放在load事件之后,避免影响页面首屏渲染。sw.js的路径需要注意,/sw.js代表站点根目录,它的控制范围默认为根目录下所有页面。如果站点部署在子路径,比如/app/,那么路径要相应调整为/app/sw.js,否则作用域不够,页面无法触发订阅。

Service Worker文件本身需要放在服务器上,并且响应头不能禁用JS执行,这个一般默认没问题,但公司内部的CDN或网关偶尔会做静态资源改写,遇到订阅报错时可以先查响应头。

3.2 订阅流程与Token获取

注册完成之后,就是向浏览器发起订阅请求。订阅方法接收两个核心参数:

  • userVisibleOnly:固定为true,含义是每条推送消息都必须对用户可见,不能做静默推送。
  • applicationServerKey:就是VAPID公钥,需要转成Uint8Array格式。

把VAPID公钥从Base64字符串转换为二进制数组,是前端代码里最容易被忽略的小细节。标准Web Push API要求的不是普通字符串,而是一个字节数组,转换工具函数可以直接复用:

function urlBase64ToUint8Array(base64String) { const padding = '='.repeat((4 - base64String.length % 4) % 4); const base64 = (base64String + padding) .replace(/-/g, '+') .replace(/_/g, '/'); const rawData = window.atob(base64); const outputArray = new Uint8Array(rawData.length); for (let i = 0; i < rawData.length; ++i) { outputArray[i] = rawData.charCodeAt(i); } return outputArray; }

订阅调用如下:

async function subscribePush() { const registration = await navigator.serviceWorker.ready; const subscription = await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY) }); // 这里拿到的 subscription.endpoint 就是要传给后端的推送Token return subscription.endpoint; }

这里有一个我个人的实操体会:有些开发者在检查订阅状态时,会直接用pushManager.getSubscription()判断是否已订阅。这个API本身没问题,但如果用户之前用旧公钥订阅过,后面公钥换了,getSubscription()会返回一个已经失效的订阅对象。稳妥做法是拿到订阅后,校验它的endpoint前缀是否和当前推送服务域名匹配,不匹配就走重新订阅流程。

3.3 消息接收与通知展示

订阅拿到Token后,消息到达浏览器时,Service Worker会触发push事件。事件处理函数里要做两件事:解析消息负载,调用showNotification展示通知。

self.addEventListener('push', function (event) { let payload = {}; try { payload = event.data ? event.data.json() : {}; } catch (e) { payload = { title: '新消息', body: '请点击查看' }; } const options = { body: payload.body || '', icon: payload.icon || '/logo.png', badge: payload.badge || '/badge.png', data: payload.data || {} }; event.waitUntil( self.registration.showNotification(payload.title || '新消息', options) ); });

event.waitUntil是Service Worker里必须的用法,它告诉浏览器在Promise完成之前不要终止Service Worker。直接同步展示通知虽然有时也能工作,但不规范,在低端机上可能通知还没弹出来Service Worker就被回收了。

通知点击事件同样要处理:

self.addEventListener('notificationclick', function (event) { event.notification.close(); const targetUrl = event.notification.data.url || '/'; event.waitUntil( clients.matchAll({ type: 'window', includeUncontrolled: true }).then(function (windowClients) { for (let i = 0; i < windowClients.length; i++) { if ('focus' in windowClients[i]) { windowClients[i].focus(); return; } } return clients.openWindow(targetUrl); }) ); });

这个逻辑不复杂,但建议每个接Web Push的团队都配上。用户点击通知后没有反馈,是通知到达率之外最影响体验的环节。有多个窗口打开时优先聚焦已有窗口,没有才新开,这是网页端的通用交互习惯。

3.4 通知权限引导的实操细节

通知权限是浏览器弹窗请求的,用户拒绝后,后续再次调用subscribe()会直接失败,不会再次弹窗。这个限制让权限引导策略变得很重要。

我的做法是:不在页面首次访问时请求权限,而是在用户完成某个有明确动机的动作后再请求,比如订阅资讯、提交订单时。如果用户已经拒绝过,页面里要提供一个“通知已关闭,请在浏览器设置中开启”的引导按钮,而不是反复调接口触发无意义的失败。

另外,浏览器地址栏旁边会有通知权限的图标状态,用户可以通过那里管理权限。前端无法强制重新弹出授权窗口,只能引导用户手动修改。这个体验细节,产品同学和开发同学最好提前对齐。

4. 后端发送消息的实现

4.1 获取Access Token

后端发送消息前,先要拿到一个Access Token。华为Push服务用的是OAuth 2.0的客户端凭证模式,也就是用App ID和App Secret去换一个临时凭证。

请求示例:

curl -X POST https://oauth-login.cloud.huawei.com/oauth2/v3/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id=您的AppID" \ -d "client_secret=您的AppSecret"

返回结果里包含access_token和expires_in字段。Token通常有一个明确的有效期,一般是3600秒左右,不要太教条地每次都现取,也不要一次缓存永久使用。我一般习惯在服务端用内存或者Redis缓存这个Token,在有效期还剩五分钟时主动刷新,避免并发高峰时所有请求同时去取Token造成瞬时压力。

获取失败时要注意排查是不是App ID和App Secret抄串了,尤其是App Secret末尾的字符容易复制漏掉。还有一个场景是账号下的服务没开通,接口会返回权限类的错误,这时候回到控制台确认推送服务状态。

4.2 发送消息的接口结构

拿Token后,调用发送接口:

curl -X POST https://push-api.cloud.huawei.com/v1/{appId}/messages:send \ -H "Content-Type: application/json" \ -H "Authorization: Bearer {access_token}" \ -d '{ "message": { "token": ["用户的推送Token"], "notification": { "title": "订单发货提醒", "body": "您的商品已出库,点击查看物流信息" }, "web": { "h5_url": "https://example.com/order/123" } } }'

token字段是目标设备的推送Token,就是前端订阅后拿到的endpoint,可以传一个字符串,也可以传数组来实现批量发送。不过批量发送有数量上限,超过上限就要拆分多批。notification对象控制通知栏展示的标题和正文。web.h5_url是Web端特有的字段,用户点击通知后跳转的落地页地址,不填的话点击通知可能没有反应。

响应里一般会返回成功或失败的状态码,成功码是80000000。看到其他码别慌,对照官方错误码表排查,大多数是Token无效或参数格式问题。

4.3 消息类型与参数选择

Web Push场景里,消息主要分两类。一类是通知消息,发出去后由浏览器直接展示通知,Service Worker的push事件也能收到负载,但即使不写任何处理逻辑,通知也能展示。另一类是数据消息,只携带业务数据,不会主动弹通知,需要Service Worker里自行解析并调用showNotification,适合需要先做业务处理再决定要不要展示的场景。

实际项目中,通知消息能覆盖大多数需求。数据消息用在需要个性化处理的场景,比如消息里带订单ID,前端先查询本地缓存再决定通知文案。这两类的接口写法差别不大,主要是notification这个字段的存在与否,以及data字段的封装方式。

优先级参数也值得一提。华为Push接口支持通过android和web字段控制消息的展示方式和行为,比如Web端可以配置消息的生存时间。给用户发验证码类通知时,建议设置较短的超时时间,避免消息延迟后还在设备上弹出;给用户发运营活动通知时,超时时间可以适当放宽,让消息在用户下次联网时仍然到达。

4.4 Token的存储与生命周期管理

前端上报的推送Token,后端需要用一张表或一个独立的存储空间维护。字段至少包括:用户ID、Token、设备信息、创建时间、最后活跃时间。每次用户登录时,前端应该重新拉取一下自己的Token状态,后端做一次对账,避免用户换设备后旧Token一直占着位置。

Token失效的场景主要有:

  • 用户清除了浏览器站点数据。
  • 用户主动撤销了通知权限。
  • 浏览器重装或系统重置。
  • 服务器端VAPID密钥重新生成。

后端发送遇到Token无效的错误时,应该把对应的Token记录下来,并在一定时间后统一清理。这块如果不做,时间一长,Token表里可能积累大量僵尸数据,批量发送时白白耗费请求量。

5. 常见问题与排查技巧实录

5.1 高频问题速查表

现象可能原因处理方式
页面提示浏览器不支持推送浏览器版本过旧,或不支持Service Worker升级浏览器,或在老浏览器上做降级
订阅时报错页面非HTTPS环境部署证书,本地开发可用localhost调试
Token获取成功但后端发送返回无效TokenVAPID密钥不匹配,或Token已被清检查前后端密钥配置,重新订阅
消息发送返回成功但设备收不到通知权限被用户关闭,或消息超时时间过短检查用户权限状态,调整消息策略
点击通知无反应Service Worker未处理notificationclick在sw.js中补充点击事件处理
后端Access Token获取失败App Secret错误或服务未开通核对控制台配置,确认推送服务已开通

5.2 调试思路与工具链

Web Push的调试链路比较长,前端、中间通道、后端各管一段,问题定位的关键是先确定故障在哪一段。

最直接的方法是看Service Worker调试面板。打开开发者工具,在Application标签下找到Service Workers,这里能看到Service Worker是否激活、有没有报错。Push Notification标签可以手动模拟推送事件,用来测试sw.js里的展示逻辑是否正常。这种方式不依赖后端,非常适合先验证前端链路。

后端调试相对更枯燥。发送接口返回的响应码是重要线索,失败时响应里会带具体原因。我的排查顺序是:先确认Access Token是否有效,再确认Token格式是否正确,最后在控制台查一下设备是否有投递记录。华为开发者后台的推送服务页面通常有消息统计,可以按时间查到消息的状态,到达数和点击数都能看到,这个是排查到达率问题的重要数据来源。

5.3 到达率与体验优化心得

平台能力再好,使用方式不当也会把到达率做崩。几个我实际验证过的做法:

第一,避免高频率无差别推送。用户授权通知并不代表愿意接受骚扰,如果站点连续发几条无关消息,用户大概率会在系统设置里关掉权限。权限一旦关了,后续消息全部石沉大海。推送的频次控制应该写在产品逻辑里,而不是单纯依赖技术通道。

第二,消息内容里尽量带上明确的落地页。点击通知后到达一个和通知内容强相关的页面,用户才知道这个通知的价值。需要登录才能查看的页面,落地页最好带上登录态跳转参数,否则用户点击后看到登录页,很容易直接流失。

第三,置空或精简通知图标。Web通知在桌面端和移动端显示尺寸不同,图标太大或太小都会导致系统对通知卡片做缩放,观感不佳。建议准备一个尺寸适中的Logo图标,并在前端订阅时把图标地址配置到通知参数里。

第四,定时任务发消息时要考虑用户时区。如果是面向全国用户的站点,凌晨批量发通知对用户是个打扰,即便消息本身有价值,体验也大打折扣。

我个人的体会是,华为Web Push接入本身并不复杂,前后端加起来一天左右就能跑通主流程,难的是把通知权限当成真正需要经营的用户触点来设计。很多项目做完接入后就不管了,结果用户关权限率非常高,最后得出“推送没用”的结论。实际上,从权限引导、消息文案到落地页体验,每一环都在影响最终效果。如果你正在做接入,建议把“用户收到消息后的行为”纳入上线后的数据观察,持续迭代推送策略,这才是这套能力真正的价值所在。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询