首页/新闻资讯/正文详情

go-app 推送通知(Push Notifications)完整接入指南:从 VAPID 订阅到服务端下发

发布时间:2026/9/25 9:12:48 来源:云帆数科 栏目:资讯中心
go-app 推送通知(Push Notifications)完整接入指南:从 VAPID 订阅到服务端下发
前端Web框架WebAssembly【免费下载链接】go-appA package to build progressive web apps with Go programming language and WebAssembly.项目地址https://gitcode.com/gh_mirrors/go/go-app点击查看免费下载导读推送通知Push Notifications是由远程服务器主动发送、无论应用处于运行还是关闭状态都能展示的系统级通知。在 go-app 构建的 PWA渐进式 Web 应用中接入推送通知需要完成获取订阅 → 注册到推送服务器 → 服务端下发 → 客户端展示与跳转四个环节。读完本文你将掌握基于 Context.Notifications() 的订阅 API、VAPID 公钥配置、HTTP 服务端注册流程以及基于 webpush-go 的服务端下发实现并理解 go-app 内置 Service Worker 对推送消息和点击跳转的处理原理。前置知识本文以 go-app 的通知权限模型为前提建议先阅读 通知启用与本地通知 一文掌握Permission()、RequestPermission()与Notification.Path的用法。一、推送通知的核心流程推送通知与本地通知Local Notification最大的区别在于本地通知由应用自身在浏览器中创建并展示NotificationService.New而推送通知由远程服务器发起即便应用关闭也能送达。go-app 的推送链路共四步获取订阅客户端调用Context.Notifications().Subscribe(VAPIDPublicKey)向浏览器的 Push API 申请一个携带加密密钥的订阅对象NotificationSubscription注册订阅客户端把订阅对象以 JSON 形式 POST 到自建的推送服务器服务器持久化存储服务端下发推送服务器通过 Web Push 协议如 webpush-go 库向各订阅端点发送 JSON 编码的 Notification 结构客户端展示go-app 内置的 Service Worker 监听push事件解析消息并调用registration.showNotification展示用户点击通知后自动导航到Notification.Path指定的页面。从源码结构看订阅与展示的关键实现分别位于 pkg/app/notification.goGo 侧 API与 pkg/app/gen/app.js浏览器侧 JS 桥接下文将逐环节展开。二、获取通知订阅客户端2.1 Subscribe API 与 VAPID 公钥推送订阅通过 Context.Notifications().Subscribe() 获取它需要传入VAPID 公钥VAPID Public Key。VAPIDVoluntary Application Server Identification是 Web Push 协议中用于标识应用服务器身份的机制公钥用于浏览器端订阅加密私钥留在服务器端用于签名下发请求。func (f *foo) enableNotifications(ctx app.Context, e app.Event) { f.notificationPermission ctx.Notifications().RequestPermission() if f.notificationPermission app.NotificationGranted { f.registerNotificationSubscription(ctx) } } func (f *foo) registerNotificationSubscription(ctx app.Context) { sub, err : ctx.Notifications().Subscribe(MY_VAPID_PUBLIC_KEY) if err ! nil { log.Println(subscribing to push notifications failed:, err) return } }Subscribe的底层行为pkg/app/notification.go#L152-L177公钥为空时直接报错vapid public key is empty因此必须传入真实有效的 VAPID 公钥内部调用浏览器桥接函数goappSubscribePushNotificationspkg/app/gen/app.js#L143-L155其本质是pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: vapIDpublicKey })userVisibleOnly: true表示每条推送都必须展示通知Web Push 的强制约束若浏览器不支持推送或订阅失败返回错误push notifications are not supported by the browser或解码失败错误。2.2 订阅前的权限检查订阅前必须确保用户已授予通知权限。推荐的完整流程是先请求权限仅在app.NotificationGranted时继续订阅见上文enableNotifications。通知权限共有四种取值pkg/app/notification.go#L93-L111权限常量说明app.NotificationDefault用户尚未表态浏览器按拒绝处理app.NotificationGranted用户已允许显示通知app.NotificationDenied用户已拒绝app.NotificationNotSupported浏览器不支持通知权限查询与请求分别对应Context.Notifications().Permission()pkg/app/notification.go#L117-L124与RequestPermission()pkg/app/notification.go#L127-L141前者读window.Notification.permission后者触发浏览器弹窗询问并等待用户选择。三、注册订阅到推送服务器客户端获取到订阅对象后必须把它注册到自建的推送通知服务器否则服务器无从得知向谁下发。这一步在 go-app 中就是一次普通的 HTTP POST 请求。因为网络 I/O 不应阻塞 UI 线程官方推荐将注册逻辑放入 ctx.Async() 异步回调中执行func (f *foo) registerNotificationSubscription(ctx app.Context) { sub, err : ctx.Notifications().Subscribe(MY_VAPID_PUBLIC_KEY) if err ! nil { log.Println(subscribing to push notifications failed:, err) return } ctx.Async(func() { var body bytes.Buffer if err : json.NewEncoder(body).Encode(sub); err ! nil { log.Println(encoding notification subscription failed:, err) return } res, err : http.Post(/PUSH_SERVER_ENDPOINT, application/json, body) if err ! nil { log.Println(registering notification subscription failed:, err) return } defer res.Body.Close() }) }要点说明序列化后的订阅对象即为 NotificationSubscription 的 JSON 形态endpoint推送服务端点 URL与keys.auth、keys.p256dh用于消息加密的密钥对/PUSH_SERVER_ENDPOINT替换为你推送服务器上的注册接口地址订阅对象可能在推送服务轮换时失效正式产品中建议在应用启动时重新订阅并同步注册。四、服务端接收并存储订阅推送服务器可以用任意语言实现go-app 对服务端的唯一硬性要求是下发的通知消息必须是 JSON 编码的Notification结构。这里以 Go 官方推荐的 http.Handler webpush-go 为例演示完整服务端实现。首先注册路由并定义处理器结构处理器持有 VAPID 密钥对和内存中的订阅表func main() { // ... http.Handle(/test/notifications/, notificationHandler{ VAPIDPrivateKey: MY_VAPID_PRIVATE_KEY, VAPIDPublicKey: MY_VAPID_PUBLIC_KEY, }) // ... } type notificationHandler struct { VAPIDPrivateKey string VAPIDPublicKey string mutex sync.Mutex subscriptions map[string]webpush.Subscription } func (h *notificationHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch path : path.Base(r.URL.Path); path { case register: h.handleRegistrations(w, r) case test: h.handleTests(w, r) } }注意这里同时持有VAPID 私钥仅存服务器用于签名下发请求与公钥分发给客户端用于订阅私钥绝不能下发到浏览器端。示例中使用map[string]webpush.Subscription按endpoint去重存储生产环境应替换为数据库等持久化存储并考虑订阅过期清理。接收订阅的处理函数从请求体解码webpush.Subscription在互斥锁保护下写入订阅表func (h *notificationHandler) handleRegistrations(w http.ResponseWriter, r *http.Request) { var sub webpush.Subscription if err : json.NewDecoder(r.Body).Decode(sub); err ! nil { w.WriteHeader(http.StatusBadRequest) return } h.mutex.Lock() defer h.mutex.Unlock() if h.subscriptions nil { h.subscriptions make(map[string]webpush.Subscription) } h.subscriptions[sub.Endpoint] sub }五、服务端下发 JSON 编码的 Notification下发处理函数遍历全部已注册订阅为每个订阅并发调用webpush.SendNotification消息体是json.Marshal(app.Notification{...})的结果// handleTests creates and sends a push notification for all the registered // subscriptions. func (h *notificationHandler) handleTests(w http.ResponseWriter, r *http.Request) { h.mutex.Lock() defer h.mutex.Unlock() for _, sub : range h.subscriptions { go func(sub webpush.Subscription) { body, _ : json.Marshal(app.Notification{ Title: Push test from server, Body: go-app push notification number, Path: /mypage }) res, err : webpush.SendNotification(body, sub, webpush.Options{ VAPIDPrivateKey: h.VAPIDPrivateKey, VAPIDPublicKey: h.VAPIDPublicKey, TTL: 30, }) if err ! nil { app.Log(err) return } defer res.Body.Close() }(sub) } }关键点消息体必须为 JSON 编码的app.Notification这是 go-app 端 Service Worker 解析推送的前提TTL: 30表示推送在推送服务上最多保留 30 秒超时未送达则丢弃适合测试场景生产环境按时效需求调整并发go func下发可提升吞吐注意h.mutex的锁定范围只覆盖遍历读取安全示例中的app.Notification来自 pkg/app/notification.go字段完全对齐 Web Notifications API见下文。六、Notification 结构与客户端展示原理6.1 Notification 字段全景go-app 的 Notification 是对 Web Notifications API 的 Go 封装字段如下字段JSON key必填说明Titletitle是通知顶部醒目标题Pathpath否点击通知后应用导航到的 URL 路径Langlang否ISO 两位语言代码Badgebadge否空间有限时代表通知的图片 URLBodybody否标题下方的主内容Tagtag否通知唯一标识用于替换同标签旧通知Iconicon否通知中显示的图标 URLImageimage否通知中展示的大图 URLDatadata否任意附加数据注意goapp键被 go-app 保留Renotifyrenotify否新通知替换旧通知时是否再次提醒用户RequireInteractionrequireInteraction否通知保持激活直到用户操作不自动关闭Silentsilent否静默通知抑制声音与振动Vibratevibrate否设备振动模式数字数组Actionsactions否通知内展示的可操作按钮列表Actions的元素是 NotificationActionAction唯一 ID、Title按钮文案、Icon图标 URL、Path点击该动作后的导航路径。注意Data中的goapp键为 go-app 保留源码注释明确标注用于内部传递path与actions信息请勿占用。6.2 Service Worker 展示与点击跳转原理推送消息到达后go-app 内置的 Service Workerpkg/app/gen/app-worker.js完成展示与跳转push事件pkg/app/gen/app-worker.js#L63-L79解析event.data.json()得到通知对象交给showNotification展示解析失败则静默丢弃showNotificationpkg/app/gen/app-worker.js#L92-L117把Notification字段映射为registration.showNotification(title, { body, icon, badge, actions, data })并将path与动作路径写入data.goapp供点击时取用notificationclickpkg/app/gen/app-worker.js#L119-L158关闭通知后读取data.goapp.path若点击的是某个动作按钮则改用对应动作的Path随后优先聚焦已打开的窗口并postMessage通知 go-app 导航否则clients.openWindow(path)打开新窗口。因此Notification.Path必须是应用内页面的 URL 路径点击通知时应用会自动导航到该路径如/mypage。这正是推送通知从展示到业务落地的关键衔接点。6.3 与本地通知的统一客户端本地通知Context.Notifications().New(app.Notification{...})与推送通知复用同一 Notification 结构与goappNewNotification桥接pkg/app/gen/app.js#L157-L179展示路径一致只是来源不同本地 vs 远程。若 Service Worker 不可用本地通知会退化为new Notification(...)并手动处理onclick导航。七、端到端验证步骤将上述代码组装后可按以下步骤验证整条链路配置 VAPID 密钥对生成 VAPID 公钥/私钥webpush-go 提供了密钥生成工具公钥填到客户端Subscribe()私钥与公钥填到服务端处理器客户端请求权限在组件渲染中根据notificationPermission状态展示启用通知按钮参考 notifications.md 的app.If分支写法点击后RequestPermission()并订阅服务端注册客户端把订阅 JSON POST 到服务端/test/notifications/register服务端下发访问服务端/test/notifications/test触发对所有已注册订阅的推送验证展示与跳转确认应用关闭时也能收到通知点击通知正文跳转/mypage如有动作按钮点击动作跳转对应路径。整个过程无需客户端轮询服务端通过 Web Push 协议直接推送这正是 PWA 离线可用体验的重要组成部分。结语go-app 将 Web Push 的复杂度收敛为三个动作客户端Subscribe()拿订阅、HTTP 注册到自有服务器、服务端下发 JSON 编码的Notification。只要服务端最终下发的是符合 Notification 结构的 JSON 消息推送服务器无论用 Go、Node.js 还是其他语言实现都可以与 go-app 无缝对接。建议结合实际项目将订阅存储、VAPID 密钥管理和订阅过期重订阅纳入工程化设计。赞分享前端Web框架WebAssembly【免费下载链接】go-appA package to build progressive web apps with Go programming language and WebAssembly.项目地址https://gitcode.com/gh_mirrors/go/go-app点击查看免费下载相关推荐Django-Push-Notifications 实现 Web 推送通知完整指南Django Push Notifications 实现 Web 推送通知完整指南 前言 在现代 Web 应用中推送通知已成为提升用户参与度和留存率的重要功能后端webpush-go 实战指南基于 VAPID 与 aes128gcm 的 Web Push 服务端推送加密webpush go 实战指南基于 VAPID 与 aes128gcm 的 Web Push 服务端推送加密 导读 webpush go 是一个专为 Go 语网络安全推送通知神器 - Push Notifications推送通知神器 Push Notifications 在移动应用开发中推送通知是与用户保持连接的关键工具。有了这个开源项目你将拥有一款跨平台的测试利器——Pu上一篇Vant Weapp Switch 开关组件完全指南状态管理、事件机制与源码实现解析下一篇10分钟搭建企业级沟通平台OpenIM Server低代码集成指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

Atlas 300V 24G推理加速卡YOLO部署全流程实战
Atlas 300V 24G推理加速卡YOLO部署全流程实战

1. 先搞清楚:Atlas 300V 24G到底是一张什么卡很多人搜“atlas 300v 24g 是运算加速卡吗”,本质上是在确认一件事:这东西买回来能不能直接插到服务器上跑推理。我的回答是:能,而且它在推理场景里的定位非常清晰——它就… · 2026/9/25 9:12:36

广州合音24mm聚酯纤维吸音板 体育馆吸音工程 生产商批发价
广州合音24mm聚酯纤维吸音板 体育馆吸音工程 生产商批发价

随着国内公共建筑、商业空间、家装产业的快速发展,建筑声学需求持续释放,大众对于空间声环境舒适度的要求不断提升,声学材料行业也逐步从零散化的小品类建材,升级为影响空间使用体验的核心功能性材料。当前国内声学材料市场仍存在… · 2026/9/25 9:12:36

Atlas 300V 24G推理卡实战:从YOLO模型转换到OM部署全流程解析
Atlas 300V 24G推理卡实战:从YOLO模型转换到OM部署全流程解析

入行做AI推理这几年,隔三差五就有人拿着Atlas 300V 24G来问我:这卡到底算不算运算加速卡?能不能直接跑YOLO?每次我都会先反问一句:你说的“运算加速”,是想训练模型,还是只想做部署推理&#xf… · 2026/9/25 9:12:36

CiLocks地理位置回传原理:get.php的GET参数数据全链路拆解
CiLocks地理位置回传原理:get.php的GET参数数据全链路拆解

CiLocks地理位置回传原理:get.php的GET参数数据全链路拆解 【免费下载链接】CiLocks Crack Interface lockscreen, Metasploit and More Android/IOS Hacking 项目地址: https://gitcode.com/GitHub_Trending/ci/CiLocks CiLocks 是一款 Android/iOS 渗透测试… · 2026/9/25 9:45:14

从零自研CRM:销售团队的中枢神经系统设计与落地
从零自研CRM:销售团队的中枢神经系统设计与落地

1. DeskcommCRM 是什么:为什么我把这套系统视为销售团队的“中枢神经系统”做销售管理这行将近十年,我经手过的CRM系统少说也有七八款,从国际大厂到国内各种SaaS都有涉猎。说句实在话,大部分CRM最后都沦为了“录入数据的表格工具”… · 2026/9/25 9:45:08

省市区街道4级MySQL数据库:导入、查询与四级联动实战指南
省市区街道4级MySQL数据库:导入、查询与四级联动实战指南

简介:一份面向MySQL开发者和数据分析师的省市区街道四级行政区域数据压缩包,收录了全国省、市、区、街道的层级关系,适用于地理信息系统、电商配送、市场分析和物流规划等场景。压缩包体积仅1.58MB,共5个sql文件,既提供… · 2026/9/25 9:45:02

Atlas 300V 24G部署YOLOv5全流程:环境搭建、模型转换与性能调优
Atlas 300V 24G部署YOLOv5全流程:环境搭建、模型转换与性能调优

前两天看到有人在搜“atlas 300v 24g 是运算加速卡吗”,紧接着还有一条是“atlas部署yolo”。这两个问题拼在一起,基本就是一张昇腾推理卡从“这玩意到底能不能用”到“怎么把它跑起来”的全过程心态写照。我最近正好在Atlas 300V 24G这张卡上把YOLOv5检… · 2026/9/25 9:44:07

C# 项目接入 OpenClaw 的配置骨架:TaoToken 统一 Key 与 settings.json 实战
C# 项目接入 OpenClaw 的配置骨架:TaoToken 统一 Key 与 settings.json 实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 9:44:01

如何用AI Agent实现日均万行可用代码:工作流与实战指南
如何用AI Agent实现日均万行可用代码:工作流与实战指南

1. 当CEO把AI当成"结对程序员"而不是"代码补全器"第一次看到"日均产出一万行可用代码"这个说法,我的反应和大多数人一样:要么是标题党,要么是把AI生成的垃圾代码也算进去了。但仔细拆解这个数字背后的工作模式… · 2026/9/25 9:44:01

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码