You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用新FCM API推送通知失败求助:报错及配置问题排查

FlutterFire FCM推送通知异常问题解决

核心问题分析

你的代码存在多个关键错误,直接导致推送失败:

  1. 请求URL错误:原URL末尾多余 HTTP/后缀,且项目ID用了无效占位符idd,无法正确定位FCM资源
  2. 认证方式混淆:FCM v1 API不使用服务器密钥,需要基于服务账号的OAuth2令牌;你错误地将私钥格式内容作为key=值传入,完全不符合v1 API的认证要求
  3. 请求体结构不匹配:v1 API的推送内容必须包裹在message字段内,目标设备标识需用token而非Legacy API的to字段
  4. VAPID Key未配置:getToken方法中vapidKey为空,Web平台无法生成有效的FCM Token

分步修复方案

方案1:改用FCM Legacy API(快速解决)

如果不想处理v1 API的复杂认证,直接切换到更简单的Legacy API:

  • 修正请求URL:改为https://fcm.googleapis.com/fcm/send
  • 修正认证头:Authorization值设为key=你的服务器密钥(从Firebase控制台>云消息传递>服务器密钥获取)
  • 补全设备Token:to字段填入getToken方法获取到的真实设备Token

修复后的sendNotification代码:

Future<void> sendNotification() async {
  try {
    final response = await http.post(
      Uri.parse('https://fcm.googleapis.com/fcm/send'),
      headers: <String, String>{
        'Content-Type': 'application/json',
        'Authorization': 'key=你的服务器密钥',
      },
      body: jsonEncode(<String, dynamic>{
        'notification': <String, dynamic>{
          'body': 'This is a test notification',
          'title': 'Test Notification'
        },
        'priority': 'high',
        'data': <String, dynamic>{
          'click_action': 'FLUTTER_NOTIFICATION_CLICK',
          'id': '1',
          'status': 'done'
        },
        'to': '真实设备FCM Token', // 替换为getToken获取到的token
      }),
    );

    if (response.statusCode == 200) {
      print('Notification sent successfully');
      print('Response body: ${response.body}');
    } else {
      print('Failed to send notification');
      print('Response status: ${response.statusCode}');
      print('Response body: ${response.body}');
    }
  } catch (e) {
    print('Exception occurred while sending notification: $e');
  }
}

方案2:正确配置FCM v1 API(长期维护)

如果坚持使用v1 API,需完成以下配置:

  1. 获取服务账号密钥:Firebase控制台>项目设置>服务账号>生成新私钥,下载JSON配置文件
  2. 添加依赖:在pubspec.yaml中添加googleapis_auth: ^1.4.1和http: ^0.13.5
  3. 生成OAuth2令牌:通过服务账号密钥生成FCM API的访问令牌
  4. 修正请求URL:替换idd为你的实际项目ID,移除末尾 HTTP/,即https://fcm.googleapis.com/v1/projects/你的项目ID/messages:send
  5. 调整请求体结构:将推送内容包裹在message字段内,用token指定目标设备

修复后的完整示例代码:

import 'package:googleapis_auth/googleapis_auth.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:firebase_messaging/firebase_messaging.dart';

final FirebaseMessaging messaging = FirebaseMessaging.instance;

void requestPermission() async {
  try {
    NotificationSettings settings = await messaging.requestPermission(
      alert: true,
      announcement: false,
      badge: true,
      carPlay: false,
      criticalAlert: false,
      provisional: false,
      sound: true,
    );

    print('User granted permission: ${settings.authorizationStatus}');
  } catch (e) {
    print('Error requesting permission: $e');
  }
}

void getToken() async {
  try {
    String? token = await messaging.getToken(
      vapidKey: '你的Web VAPID密钥', // 从Firebase控制台>云消息传递>Web配置获取
    );
    print("FCM Token: $token");
  } catch (e) {
    print('Error getting FCM token: $e');
  }
}

Future<String> getOAuthToken() async {
  // 加载服务账号JSON内容(生产环境请勿硬编码,建议从安全配置读取)
  final serviceAccountJson = jsonDecode('''
  {
    "type": "service_account",
    "project_id": "你的项目ID",
    "private_key_id": "你的私钥ID",
    "private_key": "-----BEGIN PRIVATE KEY-----\n你的私钥内容\n-----END PRIVATE KEY-----\n",
    "client_email": "你的服务账号邮箱",
    "client_id": "你的客户端ID",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_x509_cert_url": "你的客户端证书URL"
  }
  ''');

  final credentials = ServiceAccountCredentials.fromJson(serviceAccountJson);
  final scopes = ['https://www.googleapis.com/auth/firebase.messaging'];
  final client = await clientViaServiceAccount(credentials, scopes);
  
  final token = client.credentials.accessToken.data;
  client.close();
  return token;
}

Future<void> sendNotification() async {
  try {
    final oauthToken = await getOAuthToken();
    final response = await http.post(
      Uri.parse('https://fcm.googleapis.com/v1/projects/你的项目ID/messages:send'),
      headers: <String, String>{
        'Content-Type': 'application/json',
        'Authorization': 'Bearer $oauthToken',
      },
      body: jsonEncode(<String, dynamic>{
        'message': {
          'token': '真实设备FCM Token',
          'notification': <String, dynamic>{
            'body': 'This is a test notification',
            'title': 'Test Notification'
          },
          'data': <String, dynamic>{
            'click_action': 'FLUTTER_NOTIFICATION_CLICK',
            'id': '1',
            'status': 'done'
          },
          'android': {
            'priority': 'high'
          },
          'apns': {
            'headers': {
              'apns-priority': '10'
            }
          }
        }
      }),
    );

    if (response.statusCode == 200) {
      print('Notification sent successfully');
      print('Response body: ${response.body}');
    } else {
      print('Failed to send notification');
      print('Response status: ${response.statusCode}');
      print('Response body: ${response.body}');
    }
  } catch (e) {
    print('Exception occurred while sending notification: $e');
  }
}

额外注意事项

  • 确保getToken方法填入正确的VAPID密钥,否则Web平台无法生成FCM Token
  • 测试时必须使用真实设备Token,不可留空
  • 生产环境禁止硬编码服务账号JSON,建议通过环境变量或安全配置中心读取

内容的提问来源于stack exchange,提问作者John Smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 19:24:51