使用 Expo Push Service 发送通知
了解如何调用 Expo Push Service API 从你的服务器发送推送通知。
expo-notifications 库提供了所有客户端的推送通知功能。Expo 还负责将推送通知发送到 FCM 和 APNs,然后它们会将通知发送到特定设备。你所需要做的就是使用 getExpoPushTokenAsync 获取的 ExpoPushToken 向 Expo 推送 API 发送请求。
🌐 The expo-notifications library provides all the client-side functionality for push notifications. Expo also handles sending push notifications off to FCM and APNs which then send them to particular devices. All you need to do is send a request to Expo Push API with the ExpoPushToken you obtain with getExpoPushTokenAsync.
如果你更愿意构建一个直接与 APNs 和 FCM 通信的服务器,请参见 使用 FCM 和 APNs 发送通知。这比使用 Expo 推送服务更复杂,但可以实现更细粒度的控制,并完全访问所有 FCM 和 APNs 功能。
使用服务器发送推送通知
🌐 Send push notifications using a server
在设置好推送通知凭证并添加获取 ExpoPushToken 的逻辑后,你可以使用 HTTPS POST 请求将其发送到 Expo API。你可以通过搭建带有数据库的服务器来完成此操作(或者你也可以编写一个命令行工具来发送,或者直接从你的应用中发送)。
🌐 After you setup your push notification credentials and add logic to get the ExpoPushToken, you can send it to the Expo API using an HTTPS POST request. You can do this by setting up a server with a database (or you can also write a command line tool to send them or send them straight from your app).
Expo 团队和社区已经用几种不同的语言为你创建了后端:
🌐 The Expo team and community have taken care of creating back-ends for you in a few different languages:
| SDKs | Back-end | Maintained by |
|---|---|---|
| expo-server-sdk-node | Node.js | Expo team |
| expo-server-sdk-python | Python | Community |
| expo-server-sdk-ruby | Ruby | Community |
| expo-push-notification-client-rust | Rust | Community |
| expo-notifier | Symfony | Symfony |
| exponent-server-sdk-php | PHP | Community |
| expo-server-sdk-php | PHP | Community |
| exponent-server-sdk-golang | Golang | Community |
| exponent | Golang | Community |
| exponent-server-sdk-elixir | Elixir | Community |
| expo-server-sdk-dotnet | dotnet | Community |
| expo-server-sdk-java | Java | Community |
| laravel-expo-notifier | Laravel | Community |
上面的每个示例服务器都是 Expo Push Service API 的封装器。
🌐 Each of the example servers above is a wrapper around Expo Push Service API.
可靠地实现推送通知
🌐 Implement push notifications reliably
推送通知会通过多个系统从你的服务器传输到接收设备。通知大多数情况下能够成功送达。然而,有时沿途的系统或它们之间的网络连接可能会出现问题。处理错误有助于推送通知更可靠地到达目的地。
🌐 Push Notifications travel through several systems from your server to recipient devices. Notifications are delivered most of the time. However, occasionally there are issues with systems along the way and the network connections between them. Handling errors helps push notifications to arrive at their destinations more reliably.
限制并发连接数
🌐 Limit concurrent connections
在一次发送大量推送通知时,请限制并发连接数。Node SDK 已为你实现了这一功能,并最多打开六个并发连接。这可以平衡你的高峰负载,并帮助 Expo 推送通知服务成功接收推送通知请求。
🌐 When sending a large number of push notifications at once, limit the number of your concurrent connections. The Node SDK implements this for you and opens a maximum of six concurrent connections. This smooths out your peak load and helps the Expo push notification service receive push notification requests successfully.
失败重试
🌐 Retry on failure
发送推送通知的第一步是将它们传递给 Expo 推送通知服务,该服务会内部将它们加入队列,以便发送到 Google(FCM v1)和 Apple(APNs)。这第一步可能会因多种原因失败:
🌐 The first step in sending push notifications is to deliver them to the Expo push notification service, which internally adds them to a queue for delivery to Google (FCM v1) and Apple (APNs). This first step can fail for several reasons:
- 你的服务器和 Expo 推送通知服务之间的网络问题
- Expo 通知服务中断或可用性下降
- 推送凭据配置错误
- 无效的通知负载
其中一些失败是暂时的。例如,如果 Expo 推送通知服务宕机或无法访问,并且你遇到网络错误——HTTP 429 错误(请求过多)或 HTTP 5xx 错误(服务器错误)——可以使用指数回退在重试前等待几秒钟。如果第一次重试失败,则等待更长时间(遵循指数回退)再重试。这让暂时不可用的服务在你再次尝试之前有机会恢复。
🌐 Some of these failures are temporary. For example, if the Expo push notification service is down or unreachable and you get a network error - a HTTP 429 error (Too Many Requests), or a HTTP 5xx error (Server Errors) - use exponential backoff to wait a few seconds before retrying. If the first retry attempt is unsuccessful, wait for longer (follow exponential backoff) and retry again. This lets the temporarily unavailable service recover before you retry.
其他失败不会自行解决。例如,如果你的推送通知负载格式错误,你可能会收到一个 HTTP 400 响应,说明负载的问题。如果你的项目没有推送凭证,或者你在同一个请求中发送不同项目的推送通知,你也会收到错误。
🌐 Other failures will not resolve themselves. For example, if your push notification payload is malformed, you may get an HTTP 400 response explaining the issue with the payload. You will also get an error if there are no push credentials for your project or if you send push notifications for different projects in the same request.
检查推送收据是否有错误
🌐 Check push receipts for errors
Expo 推送通知服务在成功接收通知后会返回 推送票据。推送票据表示 Expo 已接收到你的通知负载,但可能仍需发送它。每个推送票据包含一个票据 ID,你可以稍后使用该 ID 查询 推送回执。在 Expo 尝试将通知发送到 FCM 或 APNs 后,推送回执即可使用。它告诉你通知发送到推送服务提供商是否成功。
🌐 The Expo push notification service responds with push tickets upon successfully receiving notifications. A push ticket indicates that Expo has received your notification payload but may still need to send it. Each push ticket contains a ticket ID, which you later use to look up a push receipt. A push receipt is available after Expo has tried to deliver the notification to FCM or APNs. It tells you whether delivery to the push notification provider was successful.
你必须检查你的推送回执。如果在发送推送通知时出现问题,推送回执是获取关于根本原因信息的最佳方式。例如,回执可能会显示 FCM 或 APNs、Expo 推送通知服务或你的通知有效负载存在问题。
🌐 You must check your push receipts. If there is an issue delivering push notifications, the push receipts are the best way to get information about the underlying cause. For example, the receipts may indicate a problem with FCMs or APNs, the Expo push notification service, or your notification payload.
推送回执还可以告诉你接收设备是否已取消订阅通知(例如,通过撤销通知权限或卸载应用),如果 APNs 或 FCM 返回了该信息。推送回执将包含一个 details → error 字段,其值为 DeviceNotRegistered。在这种情况下,应停止向该设备的推送令牌发送通知,直到它重新向你的服务器注册,以确保你的应用保持良好行为。DeviceNotRegistered 错误仅在 Google 或 Apple 认为设备已取消注册时出现在推送回执中。这需要不确定的时间,并且通常无法通过卸载应用后立即发送推送通知来进行测试。
🌐 Push receipts may also tell you if a recipient device has unsubscribed from notifications (for example, by revoking notification permissions or uninstalling your app) if APNs or FCM responds with that information. The push receipt will contain a details → error field set to DeviceNotRegistered. In this scenario, stop sending notifications to this device's push token until it re-registers with your server, so your app remains a good citizen. The DeviceNotRegistered error appears in push receipts only when Google or Apple deems the device to be unregistered. It takes an undefined amount of time and is often impossible to test by uninstalling your app and sending a push notification shortly after.
我们建议在发送推送通知后 15 分钟检查推送回执。虽然推送回执通常会更早可用,但 15 分钟的时间窗口可以为 Expo 推送通知服务提供充足的时间来生成回执。如果 15 分钟后仍没有推送回执,这可能表示 Expo 推送通知服务出现了错误。最后,推送回执会在 24 小时后被清除。
🌐 We recommend checking push receipts 15 minutes after sending your push notifications. While push receipts are often available much sooner, a 15-minute window gives the Expo push notification service a comfortable amount of time to make the receipts available to you. If after 15 minutes there is no push receipt, this likely indicates an error with the Expo push notification service. Lastly, push receipts are cleared after 24 hours.
服务水平协议
🌐 SLAs
Expo 推送通知服务没有服务水平协议 (SLA),而 FCM 和 APNs 服务也可能偶尔出现中断。按照上述指导操作,你可以使你的应用能够应对临时的服务中断。
🌐 The Expo push notification service does not have an SLA and the FCM and APNs services also may have occasional outages. By following the guidance above, you can make your application robust against temporary service interruptions.
HTTP/2 API
你可能希望直接向我们的 HTTP/2 API 发送请求(此 API 目前不需要任何身份验证),而不是使用前面列出的库之一。
🌐 Instead of using one of the libraries listed earlier, you may want to send requests directly to our HTTP/2 API (this API currently does not require any authentication).
为此,请向 https://exp.host/--/api/v2/push/send 发送一个包含以下 HTTP 头的 POST 请求:
🌐 To do so, send a POST request to https://exp.host/--/api/v2/push/send with the following HTTP headers:
host: exp.host accept: application/json accept-encoding: gzip, deflate content-type: application/json
这是一个使用 cURL 的“hello world”推送通知,你可以通过终端发送(将占位符推送令牌替换为你自己的):
🌐 This is a "hello world" push notification using cURL that you can send using your terminal (replace the placeholder push token with your own):
curl -H "Content-Type: application/json" -X POST "https://exp.host/--/api/v2/push/send" -d '{ "to": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]", "title":"hello", "body": "world" }'
请求体必须是 JSON。它可以是单个 消息对象(如上例所示),也可以是最多包含 100 个消息对象的数组,只要它们都属于同一个项目,如下所示。当你想发送多条消息以高效地减少向 Expo 服务器发出的请求次数时,我们建议使用数组。 以下是发送四条消息的示例请求体:
🌐 The request body must be JSON. It may either be a single message object (as shown in the example above) or an array of up to 100 message objects, as long as they are all for the same project as shown below. We recommend using an array when you want to send multiple messages to efficiently minimize the number of requests you need to make to Expo servers. Here's an example request body that sends four messages:
[ { "to": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]", "sound": "default", "body": "Hello world!" }, { "to": "ExponentPushToken[yyyyyyyyyyyyyyyyyyyyyy]", "badge": 1, "body": "You've got mail" }, { "to": [ "ExponentPushToken[zzzzzzzzzzzzzzzzzzzzzz]", "ExponentPushToken[aaaaaaaaaaaaaaaaaaaaaa]" ], "body": "Breaking news!" } ]
Expo 推送服务还可以选择接受 gzip 压缩的请求体。这可以大大减少发送大量通知所需的上传带宽。Node Expo 服务器 SDK 会自动为你进行请求压缩,并自动限制请求速率以平滑负载,因此我们强烈推荐使用它。
🌐 The Expo Push Service also optionally accepts gzip-compressed request bodies. This can greatly reduce the amount of upload bandwidth needed to send large numbers of notifications. The Node Expo Server SDK automatically gzips requests for you and automatically throttles your requests to smooth out the load, so we highly recommend it.
推票
🌐 Push tickets
上述请求将返回一个包含两个可选字段 data 和 errors 的 JSON 对象。data 将包含一个 推送票据 的数组,顺序与消息发送顺序相同(如果向单个收件人发送单条消息,则为一个推送票据对象)。每个票据中都包含一个 status 字段,用于指示 Expo 是否成功接收到通知,如果成功,还会包含一个 id 字段,可用于稍后检索推送回执。
🌐 The requests above will respond with a JSON object with two optional fields, data and errors. data will contain an array of push tickets in the same order in which the messages were sent (or one push ticket object, if you send a single message to a single recipient). Each ticket includes a status field indicating whether Expo successfully received the notification and, if successful, an id field that can be used to retrieve a push receipt later.
状态为
ok并附有收据 ID 意味着消息已被 Expo 服务器接收,而不是意味着用户已收到该消息(要确认用户是否收到,需要检查 推送回执)。
继续上面的示例,成功的响应正文如下所示:
🌐 Continuing the above example, this is what a successful response body looks like:
{ "data": [ { "status": "ok", "id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" }, { "status": "ok", "id": "YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY" }, { "status": "ok", "id": "ZZZZZZZZ-ZZZZ-ZZZZ-ZZZZ-ZZZZZZZZZZZZ" }, { "status": "ok", "id": "AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA" } ] }
如果单条消息出现错误,但整个请求没有问题,出错消息对应的推送工单状态将为 error,并且描述错误的字段如下所示:
🌐 If there were errors with individual messages, but not the entire request, the bad messages' corresponding push tickets will have a status of error, and fields that describe the error as shown below:
{ "data": [ { "status": "error", "message": "\"ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]\" is not a registered push notification recipient", "details": { "error": "DeviceNotRegistered" } }, { "status": "ok", "id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" } ] }
如果整个请求失败,HTTP 状态码为 4xx 或 5xx,errors 字段将是一个错误对象数组(通常只有一个)。否则,HTTP 状态码将为 200,你的消息将发送到 Android 和 iOS 推送通知服务。
🌐 If the entire request failed, the HTTP status code is 4xx or 5xx and the errors field will be an array of error objects (usually just one). Otherwise, the HTTP status code will be 200 and your messages will be on their way to the Android and iOS push notification services.
推送收据
🌐 Push receipts
在收到一批通知后,Expo 会将每条通知排入队列,以便发送到 Android 和 iOS 的推送通知服务(分别是 FCM 和 APNs)。大多数通知通常会在几秒钟内送达。不过,有时通知的发送可能会花费更长时间,尤其是在 Android 或 iOS 推送通知服务接收和发送通知的时间比平常更长,或 Expo 的推送服务基础设施负载较高时。
🌐 After receiving a batch of notifications, Expo enqueues each notification to deliver to the Android and iOS push notification services (FCM and APNs, respectively). Most notifications are typically delivered within a few seconds. However, sometimes it may take longer to deliver notifications, particularly if the Android or iOS push notification services take longer than usual to receive and deliver notifications or if Expo's Push Service infrastructure is under high load.
一旦 Expo 向 Android 或 iOS 推送通知服务发送通知,Expo 会创建一个推送回执,用于指示 Android 或 iOS 推送通知服务是否成功接收到该通知。如果在发送通知时出现错误,例如凭证错误或服务停机,推送回执将包含有关该错误的更多信息。
🌐 Once Expo delivers a notification to the Android or iOS push notification service, Expo creates a push receipt that indicates whether the Android or iOS push notification service successfully received the notification. If there was an error in delivering the notification, perhaps due to faulty credentials or service downtime, the push receipt will contain more information regarding that error.
要获取推送收据,请向 https://exp.host/--/api/v2/push/getReceipts 发送 POST 请求。请求体 必须是一个 JSON 对象,其中包含一个名为 ids 的字段,该字段是票据 ID 字符串数组:
🌐 To fetch the push receipts, send a POST request to https://exp.host/--/api/v2/push/getReceipts. The request body must be a JSON object with a field name ids that is an array of ticket ID strings:
curl -H "Content-Type: application/json" -X POST "https://exp.host/--/api/v2/push/getReceipts" -d '{ "ids": [ "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY", "ZZZZZZZZ-ZZZZ-ZZZZ-ZZZZ-ZZZZZZZZZZZZ" ] }'
推送回执的 响应体 与推送票据的响应体非常相似;它是一个 JSON 对象,包含两个可选字段 data 和 errors。data 包含回执 ID 与回执的映射。回执包括一个 status 字段,以及两个可选字段 message 和 details(在 "status": "error" 的情况下)。如果请求的回执 ID 没有对应的推送回执,该映射中将不会包含该 ID。成功响应上述请求的示例如下:
🌐 The response body for push receipts is very similar to that of push tickets; it is a JSON object with two optional fields, data and errors. data contains a mapping of receipt IDs to receipts. Receipts include a status field, and two optional message and details fields (in the case where "status": "error"). If there is no push receipt for a requested receipt ID, the mapping won't contain that ID. This is what a successful response to the above request looks like:
{ "data": { "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX": { "status": "ok" }, "ZZZZZZZZ-ZZZZ-ZZZZ-ZZZZ-ZZZZZZZZZZZZ": { "status": "ok" } // When there is no receipt with a given ID (YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY in this // example), the ID is omitted from the response. } }
你必须检查每个推送回执,因为它可能包含你需要解决的错误信息。 例如,如果某个设备不再符合接收通知的条件,苹果的文档建议你停止向该设备发送通知。推送回执中包含有关这些错误的信息。
即使收据中的
status显示ok,这也不能保证设备已经收到消息;推送收据中的 “ok” 意味着 Android(FCM)或 iOS(APNs)推送通知服务已成功接收到通知。例如,如果接收设备已关闭,iOS 或 Android 推送通知服务会尝试发送消息,但设备不一定会接收到。
如果整个请求失败,HTTP 状态码将是 4xx 或 5xx,errors 字段将是一个错误对象数组(通常只有一个)。否则,HTTP 状态码将是 200,你的消息将会发送到用户的设备上。
🌐 If the entire request failed, the HTTP status code will be 4xx or 5xx and the errors field will be an array of error objects (usually just one). Otherwise, the HTTP status code will be 200 and your messages will be on their way to your users' devices.
错误
🌐 Errors
Expo 提供了在整个过程中发生的任何错误的详细信息。下面我们将介绍一些最常见的错误,以便你可以在服务器上实现自动处理这些错误的逻辑。
🌐 Expo provides details regarding any errors that occur during this entire process. We'll cover some of the most common errors below so that you can implement logic to handle them automatically on your server.
如果由于某种原因,Expo无法将消息发送到 Android 或 iOS 推送通知服务,推送回执的详细信息也可能包含特定于服务的信息。这主要用于调试和向 Expo 报告可能的错误。
🌐 If for whatever reason, Expo couldn't deliver the message to the Android or iOS push notification service, the push receipt's details may also include service-specific information. This is useful mostly for debugging and reporting possible bugs to Expo.
个别错误
🌐 Individual errors
在推送票据和推送收据中,查找带有 error 字段的 details 对象。如果存在,它可能是以下值之一,你应按如下方式处理这些错误:
🌐 Inside both push tickets and push receipts, look for a details object with an error field. If present, it may be one of the following values, and you should handle these errors like so:
推送票据错误
🌐 Push ticket errors
DeviceNotRegistered:设备无法再接收推送通知,你应该停止向相应的 Expo 推送令牌发送消息。
推送收据错误
🌐 Push receipt errors
DeviceNotRegistered:设备无法再接收推送通知,你应该停止向相应的 Expo 推送令牌发送消息。MessageTooBig:通知的总负载过大。在 Android 和 iOS 上,总负载最多只能为 4096 字节。MessageRateExceeded:你向指定设备发送消息的频率过高。请实现指数退避,并缓慢重试发送消息。MismatchSenderId:这表明你的 FCM 推送凭证存在问题。FCM 推送凭证包括两部分:你的 FCM 服务器密钥和你的 google-services.json 文件。两者必须与同一个发送者 ID 关联。你可以在 查找服务器密钥的同一位置 找到你的发送者 ID。请检查你项目的 EAS 控制面板中 凭证 > 应用标识符 > 服务凭证 > FCM V1 服务账户密钥 下的服务器密钥,以及你项目的 google-services.json >project_number中的发送者 ID 是否与 Firebase 控制台 项目设置 > 云消息 选项卡 > 云消息 API(旧版) 下显示的相同。InvalidCredentials:你的独立应用的推送通知凭据无效(例如,你可能已撤销它们)。- Android:确保你已按照 上传 FCM V1 服务器凭据 中的说明正确上传了 Firebase 控制台的服务器密钥。
- iOS:运行
eas credentials并按照提示重新生成新的推送通知凭证。如果你撤销了一个 APN 密钥,所有依赖该密钥的应用将无法发送或接收推送通知,直到你上传一个新的密钥来替换它。上传新的 APN 密钥不会更改用户的 Expo 推送令牌。有时,这些错误会包含进一步的细节,显示InvalidProviderToken错误。这实际上与你的 APN 密钥和配置文件都有关系。要解决此错误,你应重新构建应用并重新生成新的推送密钥和配置文件。
想更好地了解 iOS 凭证,包括推送通知凭证,请阅读我们的应用签名文档。
请求错误
🌐 Request errors
如果整个请求在推送票据或推送收据时出现错误,errors 对象可能具有以下值之一,你应该处理这些错误:
🌐 If there's an error with the entire request for either push tickets or push receipts, the errors object might have one of the following values, and you should handle these errors:
TOO_MANY_REQUESTS:你已超过每个项目每秒 600 条通知的请求限制。我们建议在你的服务器上实现速率限制,以防止每秒发送超过 600 条通知(请注意,如果你使用 expo-server-sdk-node,此功能已经实现,并且重试时会使用指数回退)。PUSH_TOO_MANY_EXPERIENCE_IDS:你正在尝试向不同的 Expo 体验发送推送通知,例如@username/projectAAA和@username/projectBBB。请检查details字段,以查看体验名称与请求中关联的推送令牌的映射,并移除属于其他体验的任何令牌。PUSH_TOO_MANY_NOTIFICATIONS:你正尝试在一次请求中发送超过100条推送通知。请确保每次请求只发送100条(或更少)通知。PUSH_TOO_MANY_RECEIPTS:你正尝试在一次请求中获取超过 1000 个推送收据。请确保你只发送最多 1000 个票据 ID 字符串的数组,以获取你的推送收据。
额外的安全保障
🌐 Additional security
你可以要求所有推送请求在发送给你的用户之前,必须附带有效的访问令牌。你可以通过你的EAS 控制面板启用此增强推送安全功能。
🌐 You can require any push requests to be sent with a valid access token before we will deliver them to your users. You can enable this enhanced push security from your EAS Dashboard.
默认情况下,你可以通过发送用户的 Expo 推送令牌以及消息所需的任何文本或附加数据来向用户发送通知。这很容易设置,但**如果令牌泄露,恶意用户将能够伪装成你的服务器并向你的用户发送消息。**我们从未收到过此类报告。然而,为了遵循最佳安全实践,我们提供了在推送令牌旁使用访问令牌作为额外的安全层。
🌐 By default, you can send a notification to your users by sending their Expo Push Token and any text or additional data needed for the message. This is easy to set up, but if the tokens are leaked, a malicious user would be able to impersonate your server and send their message to your users. We have never had an instance of this report. However, to follow best security practices, we offer the use of an access token alongside the push token as an additional layer of security.
如果你正在使用 expo-server-sdk-node,请至少升级到 v3.6.0,并在构造函数中将你的 accessToken 作为选项传入。否则,在向我们的推送 API 发送任何请求时,请传入头信息 'Authorization': 'Bearer ${accessToken}'。
🌐 If you're using the expo-server-sdk-node, upgrade to at least v3.6.0 and pass your accessToken as an option in the constructor. Otherwise, pass in the header 'Authorization': 'Bearer ${accessToken}' with any requests to our push API.
在启用推送安全性后,任何在没有有效访问令牌的情况下发送的请求都会导致错误,错误代码为:UNAUTHORIZED。
🌐 Any requests sent without a valid access token after you enable push security will result in an error with code: UNAUTHORIZED.
格式
🌐 Formats
消息请求格式
🌐 Message request format
每条消息必须是具有指定字段的 JSON 对象(仅 to 字段是必需的):
🌐 Each message must be a JSON object with the given fields (only the to field is required):
| Field | Platform | Type | Description |
|---|---|---|---|
to | Android and iOS | string | string[] | An Expo push token or an array of Expo push tokens specifying the recipient(s) of this message. |
_contentAvailable | iOS Only | boolean | undefined | When this is set to true, the notification will cause the iOS app to start in the background to run a background task. Your app needs to be configured to support this. |
data | Android and iOS | Object | A JSON object delivered to your app. It may be up to about 4KiB; the total notification payload sent to Apple and Google must be at most 4KiB or else you will get a "Message Too Big" error. |
title | Android and iOS | string | The title to display in the notification. Often displayed above the notification body. Maps to AndroidNotification.title and aps.alert.title. |
body | Android and iOS | string | The message to display in the notification. Maps to AndroidNotification.body and aps.alert.body. |
ttl | Android and iOS | number | Time to Live: the number of seconds for which the message may be kept around for redelivery if it hasn't been delivered yet. Defaults to undefined to use the respective defaults of each provider (1 month for Android/FCM as well as iOS/APNs). |
expiration | Android and iOS | number | Timestamp since the Unix epoch specifying when the message expires. Same effect as ttl (ttl takes precedence over expiration). |
priority | Android and iOS | 'default' | 'normal' | 'high' | The delivery priority of the message. Specify default or omit this field to use the default priority on each platform ("normal" on Android and "high" on iOS). |
subtitle | iOS Only | string | The subtitle to display in the notification below the title. Maps to aps.alert.subtitle. |
sound | iOS Only | string | null | Play a sound when the recipient receives this notification. Specify default to play the device's default notification sound, or omit this field to play no sound. Custom sounds need to be configured via the config plugin and then specified including the file extension. Example: bells_sound.wav. |
badge | iOS Only | number | Number to display in the badge on the app icon. Specify zero to clear the badge. |
interruptionLevel | iOS Only | 'active' | 'critical' | 'passive' | 'time-sensitive' | The importance and delivery timing of a notification. The string values correspond to the UNNotificationInterruptionLevel enumeration cases. |
channelId | Android Only | string | ID of the Notification Channel through which to display this notification. If an ID is specified but the corresponding channel does not exist on the device (that has not yet been created by your app), the notification will not be displayed to the user. |
icon | Android Only | string | The notification's icon. Name of an Android drawable resource (example: myicon). Defaults to the icon specified in the config plugin. |
richContent | Android and iOS | Object | Currently supports setting a notification image. Provide an object with key image and value of type string, which is the image URL. Android will show the image out of the box. On iOS, you need to add a Notification Service Extension target to your app. See this example on how to do that. |
categoryId | Android and iOS | string | ID of the notification category that this notification is associated with. Find out more about notification categories here. |
mutableContent | iOS Only | boolean | Specifies whether this notification can be intercepted by the client app. Defaults to false. |
关于 ttl 的说明:在 Android 上,我们会尽最大努力立即传递 TTL 为零的消息,并且不会进行限制。然而,将 TTL 设置为较低的值(例如零)可能会导致处于休眠模式的 Android 设备无法接收到普通优先级的通知。为了确保通知能够送达,TTL 必须足够长,以便设备从休眠模式中唤醒。当同时指定了 ttl 和 expiration 时,本字段的优先级高于 expiration。
关于 priority 的说明:在 Android 上,普通优先级消息不会在设备休眠时打开网络连接,其传递可能会被延迟以节省电量。高优先级消息更可能立即传递,并可能唤醒休眠设备以打开网络连接,从而消耗能源。在 iOS 上,普通优先级消息会在考虑设备电量的时间发送,并可能被分组成批量传递。它们会受到限制,可能无法由 Apple 传递。高优先级消息通常会立即发送。普通优先级对应 APNs 优先级等级 5,高优先级对应等级 10。
关于 channelId 的说明:如果置为空,将使用“默认”通道,并且如果设备上尚不存在,Expo 会创建该通道。但是请注意,“默认”通道是面向用户的,你可能无法完全删除它。
推送票格式
🌐 Push ticket format
{ "data": [ { "status": "error" | "ok", "id": string, // this is the Receipt ID // if status === "error" "message": string, "details": JSON }, ... ], // only populated if there was an error with the entire request "errors": [{ "code": string, "message": string }] }
推送回执请求格式
🌐 Push receipt request format
{ "ids": string[] }
推送回执响应格式
🌐 Push receipt response format
{ "data": { Receipt ID: { "status": "error" | "ok", // if status === "error" "message": string, "details": JSON }, ... }, // only populated if there was an error with the entire request "errors": [{ "code": string, "message": string }] }
交货保证
🌐 Delivery guarantees
Expo 会尽最大努力将通知发送到由 Google 和 Apple 运营的推送通知服务。Expo 的基础设施设计为至少尝试一次向底层推送通知服务发送通知。通知更有可能被多次发送到 Google 或 Apple,而不是完全未发送;然而,这两种情况都不常见。
🌐 Expo makes a best effort to deliver notifications to the push notification services operated by Google and Apple. Expo's infrastructure is designed for at least one attempt at delivery to the underlying push notification services. It is more likely for a notification to be delivered to Google or Apple more than once rather than not at all; however, both these results are uncommon.
在通知已交给底层推送通知服务后,Expo 会创建一个“推送回执”,记录交付是否成功。推送回执表明底层推送通知服务是否收到了该通知。
🌐 After a notification has been handed off to an underlying push notification service, Expo creates a "push receipt" that records whether the handoff was successful. A push receipt denotes whether the underlying push notification service received the notification.
最后,来自 Google 和 Apple 的推送通知服务遵循自己的政策将通知传递给设备。
🌐 Finally, the push notification services from Google and Apple follow their own policies to deliver the notifications to the device.
故障排除
🌐 Troubleshooting
网络连接问题
This section helps you diagnose and resolve common network issues. Your server must have connectivity to Google Cloud Platform services in the United States region, as this is where Expo's push notification service is hosted.
DNS解析
🌐 DNS resolution
测试你的服务器是否能够解析 Expo 的推送服务域名:
🌐 Test if your server can resolve Expo's push service domain name:
dig exp.host # Check with a public DNS server dig @8.8.8.8 exp.host
网络路由与连接
🌐 Network routing and connectivity
验证你的服务器是否可以访问 Expo 的端点:
🌐 Verify your server can reach Expo's endpoints:
# Use traceroute to identify routing issues traceroute exp.host # Test basic connectivity ping exp.host # Test HTTPS connectivity to the push server. # You should receive HTTP response headers with a 200 status code. curl --verbose https://exp.host/
常见问题检查:
🌐 Common issues to check:
- 防火墙规则阻止出站 HTTPS(端口 443)流量
- 可能需要身份验证或特殊配置的企业代理服务器
- 网络 ACL 或安全组(在云环境中)限制出站连接
- 由于MTU大小问题导致的数据包分片
TLS证书验证
🌐 TLS certificate validation
确保你的服务器可以验证服务器的 TLS 证书:
🌐 Ensure your server can validate the server's TLS certificate:
openssl s_client -connect exp.host:443 -servername exp.host
我们使用由主要服务提供商签发的标准 TLS 证书,包括 Cloudflare、Google 和 Let's Encrypt。
🌐 We use standard TLS certificates signed by major service providers including Cloudflare, Google, and Let's Encrypt.