简介
本文是关于使用和操作 Web Push 和 Mozilla 的 Web Push 服务的 系列文章 的一部分。本文并非旨在作为通用指南,而是提供关于如何最佳使用该服务的建议和见解。假设读者具备 Javascript、Python 或其他技术的相关知识。
Web Push 实现问题的一个挑战在于试图找出哪里出了错。Web Push 拥有大量的“移动部件”、系统和组件,它们需要协同工作才能成功发送和接收您的消息。虽然本文无法解决所有可能导致特定 Push 消息失败的原因,但我将尝试为一些常见问题提供工具和指导。
常见问题
错误报告
我们看到最大的问题来自那些没有注意我们提供的返回代码的网站。 之前的文章 提到了如何管理订阅以降低发送永远不会送达的消息的成本。关注返回的错误也很重要。
错误可能会返回如下所示的正文
{
'errno': 102,
'message': 'Request did not validate invalid token',
'code': 404,
'more_info': 'http://autopush.readthedocs.io/en/latest/http.html#error-codes',
'error': 'Not Found'
}
errno
值的列表可以在 Autopush 文档 中找到。(Autopush 是我们运行的开源服务器的名称,用于处理接收和转发推送订阅更新。)服务器试图提供尽可能多的详细信息和帮助,因此,如果您的消息没有发送成功,Autopush 消息可能会提供很大的帮助。
VAPID 问题
另一个潜在问题是对 VAPID 的理解混淆。VAPID 是一个正在开发的规范,用于订阅提供网站进行“自我识别”。这意味着,如果存在重大问题,推送服务器操作员可以联系发送有问题的消息的人员。如果您看到大量“401”状态代码响应消息,则可能是您的 VAPID 身份验证存在问题。
VAPID 还允许网站创建“受限订阅”。这些订阅被锁定,因此只有拥有 VAPID 密钥的一方才能发送它们。这可能有点令人困惑。
简单来说,VAPID 是一块使用密钥进行加密签名的的数据块,该密钥有两部分:您永远不会共享的私钥,用于对 VAPID 令牌进行签名;以及可以安全共享的公钥,用于创建受限订阅。通常,您有一个持续相当长时间的 VAPID 密钥。最好不要让它永远有效,但它肯定可以持续一年左右。我稍后会谈谈其影响。
当您的应用 请求推送端点 时,您可以选择提供您的 VAPID 公钥作为applicationServerKey
。这将创建一个锁定到该密钥的新订阅端点。为了发送成功的订阅请求,您必须使用相应的私钥对 VAPID 块进行签名,并在您的请求中包含公钥。(您在请求中包含公钥的方式在草案 01 和 草案 02 之间 发生了变化。您可能希望找到 一个库 来执行签名。您还需要查阅您选择的推送服务的文档,以查看它们接受哪些格式。大多数推送平台都接受草案 02。)
请注意,VAPID 公钥与您请求的 URL 相关联。这意味着每次要向该 URL 发送数据时,都需要使用相同的密钥对。如果您想更改 VAPID 密钥,则需要获取一个新的端点并丢弃旧的端点。当然,您可以决定如何操作。您的应用可以简单地从已知位置获取新的公钥,生成新的 URL 请求并将新的注册信息发送回您的服务器。这将执行您希望在 pushsubscriptionchange
事件中使用的许多相同代码。
因此,对于 VAPID 401 错误,您需要检查
- 您是否使用了与获取受限订阅时使用的相同的密钥对?
- 您是否已正确签署了您的 VAPID 授权密钥?
- 您是否已在您的请求中包含所有必需的 VAPID 标头?
数据加密
最后,还有实际的消息加密。这可能是最难调试的,因为推送服务器在接受要传递的消息时无法知道加密是否正确。此外,只有在用户代理 (UA) 成功解密消息时,才会由用户代理 (UA) 传递消息。这可以防止您的应用因虚假消息而唤醒,从而可能耗尽电池电量。
同样,解决此类问题的最佳方法是使用 一个库。但这并非总是可行,您可能需要“手动”创建自己的库。有一个 页面 可以帮助您逐步完成加密数据的过程,但它目前仅适用于 Firefox。
请注意,规范中有一些更改可能尚未得到支持。截至发布日期,大多数推送实现都将接受 ECE 草案 03“aesgcm”编码。有一个 提案 建议用户代理报告其支持的加密形式,但这尚未正式被接受。不幸的是,这是使用仍在开发中的技术的弊端之一。如果您希望关注正在提出的更改,则应关注工作组。
不幸的是,推送服务无法检测到许多加密错误。这是因为推送服务无法解密您的消息。幸运的是,可以“查看”客户端内部以查看是否存在错误。
调试桌面
使用 浏览器控制台 来帮助调试桌面。请注意,这与作为 开发者工具面板 一部分显示的“Web 控制台”不同。浏览器控制台显示更高级别的消息,这些消息可能无法在页面或 Web 控制台中看到。
好处是,您将能够看到服务工作者可能记录的消息以及其他推送消息。例如,如果您查看我们使用的测试页面,您可能会在浏览器中看到类似的内容
在该窗口的底部附近,您会注意到来自“sw.js”的四条消息。前三条是console.info()
消息,表明已收到新消息、新消息的内容以及关联的客户端。请注意,第四条消息是console.error()
,显示“服务工作者无法发送消息:错误:没有有效的客户端可发送。”。当服务工作者丢失与其父级声明的关联时,可能会发生这种情况。简而言之:好消息是消息已成功接收,坏消息是它无处可去。
另一个有用的工具是about:debugging#workers
处的服务工作者调试页面。此页面显示所有已注册的服务工作者脚本,并允许您取消注册它们、选择性地发送推送事件以及调试服务工作者脚本。Mozilla Hacks 有更多关于 about:debugging
页面 的详细信息。
调试 Android
调试 Android 上的推送与之类似。与桌面一样,您需要将dom.push.debug
设置为true
,并将dom.push.loglevel
设置为debug
。不幸的是,与桌面不同,没有简单的方法可以从 Android 查看浏览器控制台。消息被记录到 Android 错误日志中,因此可以使用类似<a href="https://developer.android.com.cn/studio/command-line/adb.html">adb</a> logcat
的命令来跟踪错误日志。某些开发者可以使用grep
仅查找包含"Gecko(Push|Console)"
的记录。
遗憾的是,此视图并不那么美观,但显示了与之相同的内容,包括服务工作者的控制台消息,这可能有助于识别和解决错误。值得注意的是,adb 可能会截断非常长的消息。
adb 日志示例可能如下所示
04-24 15:18:04.432 7015 7037 I GeckoPushGCM: Cached GCM token exists: crWSbvAk4e4:APA91bHozW8bSTCrBwPerd7...
04-24 15:18:04.432 7015 7037 D GeckoPushManager: Existing uaid is fresh; no need to request from autopush endpoint.
04-24 15:18:04.488 7015 7037 I GeckoPushManager: Got chid: c0ef2... and endpoint: https://updates.push.services...
04-24 15:18:04.500 7015 7038 I GeckoPush: console.debug: PushServiceAndroidGCM:
04-24 15:18:04.520 7015 7038 I GeckoConsole: put() [object Object]
04-24 15:18:04.521 7015 7038 I Gecko : put()
04-24 15:18:04.522 7015 7038 I Gecko : Object
04-24 15:18:04.522 7015 7038 I Gecko : - pushEndpoint = https://updates.push.services.mozilla.com/wpush/v1/...
04-24 15:18:04.522 7015 7038 I Gecko : - scope = https://jrconlin.github.io/Webpush_QA/
...
04-24 15:18:04.526 7015 7038 I Gecko : console.debug: PushDB:
04-24 15:18:04.528 7015 7038 I GeckoConsole: put: Request successful. Updated record c0ef2...
04-24 15:18:04.528 7015 7038 I Gecko : put: Request successful. Updated record
04-24 15:18:04.528 7015 7038 I Gecko : c0ef2...
04-24 15:18:04.563 7015 7038 I GeckoConsole: receiverKey BNPFRn...
04-24 15:18:04.565 7015 7038 I GeckoConsole: Auth key: 0Gkt8...
04-24 15:18:04.567 7015 7038 I GeckoConsole: data: Amidst the mists and coldest frosts, I thrust my fists against ...
...
04-24 15:18:04.650 7015 7038 I GeckoConsole: echo -ne "\xa\xc6\xfb\xd3\x13\x0e\xa\xa\xa\xad\x59\xad\x71\xa\xa\...
04-24 15:18:04.652 7015 7038 I GeckoConsole: payload 191,40,74,54,29,62,188,190,133,70,86,70,120,194,173,100,62,...
04-24 15:18:04.653 7015 7038 I GeckoConsole: Fetching: https://updates.push.services.mozilla.com/wpush/v1/gAAAAA...
04-24 15:18:04.960 7015 7038 I GeckoConsole: Message sent 201
04-24 15:18:05.753 7015 9696 D GeckoPushGCM: Message received. Processing on background thread.
04-24 15:18:05.754 7015 7037 I GeckoPushService: Google Play Services GCM message received; delivering.
...
04-24 15:18:05.774 7015 7038 I Gecko : console.debug: PushService:
04-24 15:18:05.774 7015 7037 I GeckoPushService: Delivering dom/push message to Gecko!
04-24 15:18:05.777 7015 7038 I GeckoPush: console.debug: PushServiceAndroidGCM:
04-24 15:18:05.781 7015 7038 I GeckoPush: ReceivedPushMessage with data
04-24 15:18:05.782 7015 7038 I GeckoPush: Object
04-24 15:18:05.782 7015 7038 I GeckoPush: - channelID = c0ef2...
04-24 15:18:05.782 7015 7038 I GeckoPush: - con = aesgcm
...
04-24 15:18:05.783 7015 7038 I GeckoPush: console.debug: PushServiceAndroidGCM:
04-24 15:18:05.784 7015 7038 I GeckoConsole: Delivering message to main PushService: [object ArrayBuffer] ...
...
04-24 15:18:05.786 7015 7038 I Gecko : console.debug: PushService:
04-24 15:18:05.787 7015 7038 I GeckoConsole: receivedPushMessage()
04-24 15:18:05.787 7015 7038 I Gecko : receivedPushMessage()
04-24 15:18:05.788 7015 7038 I Gecko : console.debug: PushDB:
04-24 15:18:05.789 7015 7038 I GeckoConsole: getByKeyID()
04-24 15:18:05.789 7015 7038 I Gecko : getByKeyID()
04-24 15:18:05.795 7015 7038 I Gecko : console.debug: PushDB:
04-24 15:18:05.797 7015 7038 I GeckoConsole: getByKeyID: Got record [object Object]
04-24 15:18:05.810 7015 7038 I Gecko : console.debug: PushDB:
04-24 15:18:05.811 7015 7038 I GeckoConsole: update: Update successful c0ef2...
...
04-24 15:18:05.840 7015 7038 I Gecko : console.debug: PushService:
04-24 15:18:05.841 7015 7038 I GeckoConsole: notifyApp() https://jrconlin.github.io/Webpush_QA/
04-24 15:18:05.841 7015 7038 I Gecko : notifyApp()
04-24 15:18:05.841 7015 7038 I Gecko : https://jrconlin.github.io/Webpush_QA/
04-24 15:18:05.848 7015 7038 I GeckoConsole: **** Recv'd a push message:: {"isTrusted":true}
04-24 15:18:05.850 7015 7038 I GeckoConsole: Service worker just got: Amidst the mists and coldest frosts, I ...
04-24 15:18:05.853 7015 7038 I GeckoConsole: Service worker found clients {}
04-24 15:18:05.854 7015 7038 I GeckoConsole: Service worker sending to client... [object WindowClient]
04-24 15:18:05.857 7015 7038 I GeckoConsole: Service Worker sent: {"type":"content","content":"Amidst the mists ...
04-24 15:18:08.033 527 32517 E ResourceManagerService: Rejected removeResource call with invalid pid.
04-24 15:18:05.865 7015 7015 D GeckoToolbar: onTabChanged: LOCATION_CHANGE
请注意,您在这里获得更多信息,包括指示 GCM 接收消息并将其转发给 Gecko 的消息。许多日志消息已删除或修剪以节省发布空间,但这些消息可以帮助隔离问题是出在您的应用中还是网络中。
结论
使用 Push 很有意义,但请记住,它是一项新技术。这意味着事情可能会发生变化,并且可能存在一些需要注意的粗糙部分。与往常一样,我们感谢您发现的错误以及如何使事情变得更容易的建议。如果您有任何疑问,可以在 irc.mozilla.org 的 #push 频道中找到我们。
关于 JR Conlin
Web Push 服务器开发者,感谢您订阅猫事实。
5 条评论