PAYATHON 2026

.NET MAUI Android App 注册 Push notifications 时返回 422

排障赵师傅

结论

这个 422 应先从后端的 CreateOrUpdateInstallationAsync 查起。现有日志只能确认请求进入了 NotificationsController.UpdateInstallation,还无法判断具体是哪项配置出了问题。

在官方示例中,只要后端服务返回 false,控制器就会返回 422 Unprocessable Entity。所以,安装信息不符合要求和调用 Azure Notification Hubs 时发生异常,都可能让客户端收到这个状态码。官方控制器示例

Android Emulator 可以接收 FCM 推送。应检查模拟器是否具备 Google Play services,以及应用是否成功取得 FCM registration token。仅凭使用了模拟器,无法解释这个 422。

为什么不应把问题直接归到 GetDeviceInstallation

这行代码用于取得设备安装信息:

var deviceInstallation =
    DeviceInstallationService?.GetDeviceInstallation(tags);

官方 Android 实现会先检查 Google Play services 和 token,再构造包含 InstallationId、Platform、PushChannel 和 Tags 的对象。这个方法不发送 HTTP 请求;如果 Google Play services 不可用或 token 为空,它会先抛出本地异常。Android 实现源码

HTTP 请求在下一步发送:

await SendAsync<DeviceInstallation>(
    HttpMethod.Put, RequestUrl, deviceInstallation);

示例中的 SendAsync 会调用 response.EnsureSuccessStatusCode(),在收到非成功响应时抛出异常。Response status code does not indicate success: 422 就是在这里抛出的。注册服务源码

安装信息仍需检查,但断点停在某一行,不能证明 HTTP 422 是该行产生的。后面的 SecureStorage.SetAsync 要等请求成功后才会执行。

先记录后端的失败原因

官方 NotificationHubService.CreateOrUpdateInstallationAsync 会在以下情况下返回 false:

  • InstallationId、Platform 或 PushChannel 为空。
  • Platform 不在后端支持的平台映射中。
  • _hub.CreateOrUpdateInstallationAsync 抛出异常,被 catch 捕获。

最后一种情况没有记录异常,因此客户端只能看到 422。后端服务源码

先修改该方法中的 catch,把异常写入日志:

try
{
    await _hub.CreateOrUpdateInstallationAsync(installation, token);
}
catch (Exception ex)
{
    _logger.LogError(
        ex,
        "Notification Hubs installation update failed. Platform={Platform}",
        deviceInstallation.Platform);

    return false;
}

字段校验失败和平台映射失败的位置也应分别增加日志,记录进入了哪个失败分支、哪些字段为空。只在控制器中记录“注册失败”,仍然无法定位问题。

部署后再次点击 Register,查看异常类型、消息、内部异常,以及 SDK 提供的服务端错误信息,再据此排查权限、Hub 名称、请求内容或网络问题。

日志里的 IIS HTML 页面只显示了 422 和托管模块信息,没有具体的失败原因。凭这些信息,既无法认定 IIS 配置有误,也无法确认 422 是 Azure Notification Hubs 返回的原始状态码。

检查实际提交的安装信息

调用 SendAsync 前,查看 deviceInstallation 中的这些字段:

字段应检查的内容
InstallationId非空,并能稳定标识当前安装
Platform与后端平台映射完全一致;此示例 Android 使用 "fcmv1"
PushChannel当前应用取得的 FCM registration token
Tags先使用空数组排除自定义标签的影响

PushChannel 应来自设备端 Firebase Messaging SDK,不能填写 Firebase API key、Project ID 或 Azure 连接字符串。

可以在原有 RegisterDeviceAsync 中增加检查,在依赖项缺失或字段为空时直接报错:

public async Task RegisterDeviceAsync(params string[] tags)
{
    var service = DeviceInstallationService
        ?? throw new InvalidOperationException(
            "IDeviceInstallationService is not registered.");

    var deviceInstallation = service.GetDeviceInstallation(tags)
        ?? throw new InvalidOperationException(
            "Device installation is null.");

    if (string.IsNullOrWhiteSpace(deviceInstallation.InstallationId) ||
        string.IsNullOrWhiteSpace(deviceInstallation.Platform) ||
        string.IsNullOrWhiteSpace(deviceInstallation.PushChannel))
    {
        throw new InvalidOperationException(
            "InstallationId, Platform and PushChannel are required.");
    }

    await SendAsync<DeviceInstallation>(
        HttpMethod.Put, RequestUrl, deviceInstallation)
        .ConfigureAwait(false);

    await SecureStorage.SetAsync(
        CachedDeviceTokenKey, deviceInstallation.PushChannel)
        .ConfigureAwait(false);

    await SecureStorage.SetAsync(
        CachedTagsKey, JsonSerializer.Serialize(tags));
}

这些检查能尽早发现客户端数据问题,但不保证能解决后端异常。使用官方 Android 实现时,空 token 通常在构造安装信息时就会被拦截。

排查时,可以在本地调试器中对照客户端对象、发送的 JSON 和后端收到的对象,确认序列化没有丢失字段。公开日志中只记录 token 是否存在或其长度,不要输出完整 token。

检查 Azure 与 Firebase 配置

如果安装信息正常,再结合后端异常检查配置。

App Service 是否配置了正确的 Hub

本地通过 dotnet user-secrets 设置的值不会自动成为 Azure App Service 的配置。请确认实际部署的应用或部署槽中存在以下配置:

NotificationHub:Name
NotificationHub:ConnectionString
Authentication:ApiKey

核对 Hub 名称和连接字符串是否指向同一目标,并确认连接字符串具有创建、更新 installation 的权限。教程使用 DefaultFullSharedAccessSignature 对应的连接字符串配置后端。官方配置说明

客户端访问 API 使用的 Authentication:ApiKey 和后端访问 Notification Hubs 使用的连接字符串,是两项独立配置。

FCM v1 配置是否一致

检查 Notification Hub 的 Google FCM v1 配置,包括服务账号的 Project ID、Client Email 和 Private Key。这些值来自服务账号 JSON,不能用 Android 客户端的 google-services.json 代替。Notification Hubs 平台配置说明

客户端还需检查:

  • google-services.json 是否对应当前使用的 Firebase 项目和 Android 包名。
  • .csproj 中的 ApplicationId 是否匹配 Firebase 注册的包名。
  • 文件是否以 GoogleServicesJson 构建项参与当前 Android 目标的构建。

如果复制的构建配置带有特定的 TargetFramework 条件,要确认它与项目实际目标一致。文件放进了项目目录,并不代表构建时已经使用了它。

这些配置需要逐项核对,但当前的 422 不足以证明 FCM 凭据有误。部分问题可能要到后续发送通知时才会出现。

Android Emulator 需要检查什么

Firebase 支持在带 Google APIs 的 Android Emulator 上测试 FCM;该 MAUI 示例还要求 Android API 26+ 和 Google Play services。Firebase Android 接入说明、MAUI 示例运行要求

排查时,确认以下几点:

  1. 使用带 Google APIs 或 Google Play 的系统镜像,且 Google Play services 可用。
  2. 模拟器能够访问 Firebase 服务。
  3. 获取 token 的异步操作已成功完成,且 token 已写入注册流程使用的同一个 IDeviceInstallationService 实例。
  4. 取得 token 后才允许点击 Register,同时记录获取 token 失败时的异常。

如果模拟器缺少 Google Play services,官方实现通常会先抛出本地检查异常。若请求已经进入后端并返回 422,就应继续追踪后端的失败分支。换成真机也不能省略这一步。

Android 13+ 的 POST_NOTIFICATIONS 权限影响通知展示,应在测试接收通知时检查,不能直接解释后端 installation 注册接口的 422。Firebase 通知权限说明

备注:内容仅供参考。