Laravel10中使用FCM API V1批量推送通知遇400错误求助
解决FCM API V1批量推送的400 Bad Request错误
问题背景
此前使用Cloud Messaging API (Legacy),单次CURL请求可向1000台设备推送通知。切换至Firebase Cloud Messaging API (V1)后,循环单推耗时久且增加PHP 8.1.0应用服务器性能开销,已在Firebase控制台删除旧服务器密钥。编写代码尝试批量推送时触发400错误,需解决该问题并实现高效批量推送。
现有代码
FcmService服务类
namespace App\Services; use GuzzleHttp\Client; use GuzzleHttp\Exception\RequestException; use Firebase\JWT\JWT; class FcmService { protected $client; protected $url; protected $credentialsFilePath; public function __construct() { $this->client = new Client(); $this->credentialsFilePath = public_path('xxxxxx.json'); $this->url = 'https://fcm.googleapis.com/v1/projects/xxxx/messages:send'; } public function sendMultiNotification(array $tokens, string $title, string $body) { $responses = []; $batchSize = 500; // Maximum allowed tokens per request // Split tokens into batches foreach (array_chunk($tokens, $batchSize) as $tokenBatch) { $message = [ 'message' => [ 'tokens' => $tokenBatch, // Use 'tokens' for multiple device tokens 'notification' => [ 'title' => $title, 'body' => $body, ], ], ]; try { $response = $this->client->post($this->url, [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->generateAccessToken(), 'Content-Type' => 'application/json', ], 'json' => $message, ]); // Log the response status and body \Log::info('FCM Response Status: ' . $response->getStatusCode()); \Log::info('FCM Response Body: ' . $response->getBody()); $responses[] = json_decode($response->getBody(), true); } catch (RequestException $e) { \Log::error('FCM Error: ' . $e->getMessage()); $responses[] = ['error' => $e->getMessage()]; } } return $responses; // Return all responses for each batch } protected function generateAccessToken() { $credentials = json_decode(file_get_contents($this->credentialsFilePath), true); $now = time(); $payload = [ 'iss' => $credentials['client_email'], 'sub' => $credentials['client_email'], 'iat' => $now, 'exp' => $now + 3600, 'aud' => 'https://fcm.googleapis.com/', ]; return JWT::encode($payload, $credentials['private_key'], 'RS256'); } }
控制器代码
namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use App\Services\FcmService; use Illuminate\Http\Request; class FcmController1 extends Controller { protected $fcmService; public function __construct(FcmService $fcmService) { $this->fcmService = $fcmService; // Assign the injected service to the class property } public function send(Request $request) { $tokens =['xxxxx',xxx2']; $title = 'Your Notification Title'; $body = 'Your Notification Body'; $responses = $this->fcmService->sendMultiNotification($tokens, $title, $body); return response()->json($response); } }
错误信息
"error": "Client error:
POST https://fcm.googleapis.com/v1/projects/newbonuscard/messages:sendresulted in a400 Bad Requestresponse:
{
"error": {
"code": 400,
"message": "Invalid JSON payload received. Unknown name "tokens" at 'message': Prot (truncated...)
"
解决方法
1. 修正API端点与请求体结构
FCM V1 API的messages:send接口仅支持单设备推送,批量推送需使用messages:sendAll接口,且请求体需构造为包含多个单设备消息的数组(无tokens字段,每个消息用token指定设备)。
修改FcmService的关键代码:
public function __construct() { $this->client = new Client(); $this->credentialsFilePath = public_path('xxxxxx.json'); // 替换为批量推送接口 $this->url = 'https://fcm.googleapis.com/v1/projects/xxxx/messages:sendAll'; } public function sendMultiNotification(array $tokens, string $title, string $body) { $responses = []; $batchSize = 500; // FCM sendAll接口最多支持500条消息/请求 foreach (array_chunk($tokens, $batchSize) as $tokenBatch) { // 构造批量消息数组 $messages = []; foreach ($tokenBatch as $token) { $messages[] = [ 'notification' => [ 'title' => $title, 'body' => $body, ], 'token' => $token ]; } $requestBody = [ 'messages' => $messages ]; try { $response = $this->client->post($this->url, [ 'headers' => [ 'Authorization' => 'Bearer ' . $this->generateAccessToken(), 'Content-Type' => 'application/json', ], 'json' => $requestBody, ]); \Log::info('FCM Response Status: ' . $response->getStatusCode()); \Log::info('FCM Response Body: ' . $response->getBody()); $responses[] = json_decode($response->getBody(), true); } catch (RequestException $e) { \Log::error('FCM Error: ' . $e->getMessage()); $responses[] = ['error' => $e->getMessage()]; } } return $responses; }
2. 修正控制器中的语法错误
- 令牌数组中字符串需加引号:
$tokens = ['xxxxx', 'xxx2']; - 返回变量名需匹配:
return response()->json($responses);
修改后的控制器代码片段:
public function send(Request $request) { $tokens = ['xxxxx', 'xxx2']; $title = 'Your Notification Title'; $body = 'Your Notification Body'; $responses = $this->fcmService->sendMultiNotification($tokens, $title, $body); return response()->json($responses); }
3. 验证权限与密钥
确保服务账号JSON文件拥有cloudmessaging.messages.create权限,且服务器能正常读取该文件。
内容的提问来源于stack exchange,提问作者Qasim Boqaai
相关产品推荐
相关产品推荐

