Flutter/Dart中FCM HTTP v1发送通知失败,请求排查协助
FCM HTTP v1通知未送达问题排查与修复
首要问题:你用错了Bearer令牌
你当前通过getAccessToken()获取的是Firebase Auth用户的ID Token,但FCM v1 API要求的是服务账号的OAuth2访问令牌——用户ID Token只能用来验证用户身份,根本无权调用FCM的服务端API,这是导致通知发不出去的核心原因。
正确获取FCM令牌的方法
需要用Firebase服务账号生成专用的OAuth2令牌,步骤如下:
- 去Firebase控制台下载服务账号JSON文件(路径:项目设置 > 服务账号 > 生成新的私钥)
- 用Google Auth库生成令牌,Dart示例代码:
import 'package:googleapis_auth/auth_io.dart'; import 'package:http/http.dart' as http; Future<String> getFcmAccessToken() async { // 注意:不要把服务账号JSON硬编码到代码里,建议从环境变量或安全配置文件读取 final serviceAccountJson = { \"type\": \"service_account\", \"project_id\": \"你的项目ID\", \"private_key_id\": \"你的私钥ID\", \"private_key\": \"你的私钥内容\", \"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); // FCM所需的权限范围 final scopes = ['https://www.googleapis.com/auth/firebase.messaging']; final client = await clientViaServiceAccount(credentials, scopes); final accessToken = client.credentials.accessToken.data; client.close(); return accessToken; }
其他排查方向
1. 验证Device Token是否有效
- 先去FCM控制台的「发送测试消息」功能,直接用这个Device Token发测试通知,如果也收不到,说明Token无效(可能过期、应用已卸载,或者设备注册信息失效)
- 确认Token格式正确:应该是一串约150字符的字符串
2. 检查API请求细节
- 确认请求URL里的项目ID正确:把
academy换成你实际的Firebase项目ID,格式是https://fcm.googleapis.com/v1/projects/[你的项目ID]/messages:send - 一定要打印API的响应内容,这是排查错误的关键!修改你的
http.post代码,加上响应日志:
final response = await http.post(...); print('FCM响应状态码: ${response.statusCode}'); print('FCM响应内容: ${response.body}'); // 不要只靠toast提示,日志能帮你看到具体错误(比如认证失败、Token无效等)
3. 权限与配置检查
- 确保你的服务账号拥有「Firebase Messaging Admin」角色(去Google Cloud控制台 > IAM > 找到对应的服务账号 > 添加角色)
- 确认客户端应用已经正确配置FCM:AndroidManifest.xml/Info.plist设置无误,google-services.json/GoogleService-Info.plist已添加到项目中
修复后的发送方法示例
static Future<void> sendNotificationForTesting(String deviceToken) async { try { String token = await getFcmAccessToken(); // 替换成正确的令牌获取方法 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 $token' }, body: jsonEncode(<String, dynamic>{ \"message\": { \"token\": deviceToken, \"notification\": { \"title\": \"突发新闻\", \"body\": \"有新的新闻报道可用。\" }, \"data\": {\"id\": \"1\"}, \"android\": { \"notification\": { \"click_action\": \"FLUTTER_NOTIFICATION_CLICK\", }, }, } }), ); if (response.statusCode == 200) { Fluttertoast.showToast( backgroundColor: Colors.green, msg: \"通知发送成功\", textColor: Colors.white, ); } else { print('FCM错误详情: ${response.body}'); Fluttertoast.showToast( backgroundColor: Colors.red, msg: \"发送失败: ${response.body}\", textColor: Colors.white, ); } } catch (e) { print(\"发送测试通知失败: $e\"); Fluttertoast.showToast( backgroundColor: Colors.red, msg: \"发送失败: ${e.toString()}\", textColor: Colors.white, ); } }
内容的提问来源于stack exchange,提问作者Templeton Meshack
相关产品推荐
相关产品推荐

