實做FCM Web推播

2021/12/03

Firebase專案設定

首先建立firebase專案後先建立個網頁應用程式

1.png

 

接著前往專案設定

2.png

 

建立或取得網路推播憑證(vapidKey)

後續Web前端串接需要使用

3.png

 

後端發送

 

發送請求授權方式有以下兩種

  1. server key(伺服器金鑰)
  2. service account + project ID(sender ID)

 

server key(伺服器金鑰)

在專案設定>雲端通訊頁面可建立server key

32.png

 

service account key

在專案設定>服務帳戶頁面可建立service account key

4.png

 

發送請求(service account + proejct ID)

可透過此API來發送推播

以下透過php google/apiclient來發送推播

直接使用notification參數來設定推播通知(可參考此連結)

 

<?php

namespace Modules\Base\Services;

class FcmService
{
    protected $client;
    protected $credentialFilePath, $projectId;
    public function __construct()
    {
        $this->credentialFilePath = env('FCM_SERVICE_ACCOUNT_KEY');
        $this->projectId = env('FCM_PROJECT_ID');
    }

    public function send(string $token) : ?string
    {
        $this->client = new \Google_Client();
        $this->client->setApplicationName('FirebaseCloudMessage');
        $this->client->setAuthConfig($this->credentialFilePath);
        $this->client->addScope(['https://www.googleapis.com/auth/firebase.messaging']);
        $httpClient = $this->client->authorize();
        $url = "https://fcm.googleapis.com/v1/projects/{$this->projectId}/messages:send";
        $params = [
            'message' => [
                'token' => $token,
                'notification' => [
                    'title' => 'title',
                    'body' => 'content...'
                ],
            ],
        ];

        $httpClient->post($url, [
            'json' => $params,
        ]);
    }
}

 

發送請求(server key)

<?php

namespace Modules\Base\Services;
use GuzzleHttp\Client;
class FcmService
{
    protected $client, $messageId, $serverKey;
    protected $success, $error;
    public function __construct()
    {
        $this->serverKey = env('FCM_SERVER_KEY');
        $this->client = new Client();
    }

    public function send(string $deviceToken)
    {

        $this->client->request('post', 'https://fcm.googleapis.com/fcm/send', [
            'headers' => [
                'Authorization' => "key={$this->serverKey}",
            ],
            'json' => [
                'to' => $deviceToken,
                'notification' => [
                    'title' => 'title',
                    'body' => 'content...'
                ],
                'data' => ['foo' => 'bar'],
            ],
        ]);
    }
}

 

前端接收訊息

在前端可分為兩中情況來接收訊息

一種是前景一種是背景下列將詳細敘述

參考文件:https://firebase.google.com/docs/cloud-messaging/js/receive

 

通知權限

在接收通知之前

我們要先確認user有允許網站通知權限

若user已經拒絕通知權限

可在Web提醒使用者前往瀏覽器通知設定開放通知權限

 

請求通知權限方式

await window.Notification.requestPermission()

 

取得通知權限方式

window.Notification.permission
// default: 使用者尚未給予任何權限 (無法顯示通知)
// granted: 允許通知權限
// denied: 拒絕通知權限

 

發送通知

// sw為service worker實體
sw.showNotification('title', {
  body: 'content...',
  icon: 'https://foobar.com/foobar.png',
})

 

前景通知

import { initializeApp } from 'firebase/app'
import { getMessaging, getToken, onMessage } from 'firebase/messaging'
import { Messaging } from '@firebase/messaging'

const app = initializeApp({
  apiKey: 'api-key',
  authDomain: 'project-id.firebaseapp.com',
  databaseURL: 'https://project-id.firebaseio.com',
  projectId: 'project-id',
  storageBucket: 'project-id.appspot.com',
  messagingSenderId: 'sender-id',
  appId: 'app-id',
});

messaging = getMessaging();
deviceToken = await getToken(messaging, {
  vapidKey: 'vapidKey', // 網路推播憑證
})

onMessage(messaging, (payload) => {
  // TODO
})

 

背景通知

若後端送出的推播請求meesage欄位中有notification屬性

下方的code在執行const messaging = firebase.messaging();

將會背景收到推播後自動產生通知

若不希望自動產生通知

或是希望調整預設的通知icon等客製化設定

可參考下方的"不使用預設icon"段落

 

importScripts('https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js');

firebase.initializeApp({
  apiKey: 'api-key',
  authDomain: 'project-id.firebaseapp.com',
  databaseURL: 'https://project-id.firebaseio.com',
  projectId: 'project-id',
  storageBucket: 'project-id.appspot.com',
  messagingSenderId: 'sender-id',
  appId: 'app-id',
});

const messaging = firebase.messaging();

messaging.onBackgroundMessage((payload) => {
  // TODO
});

 

不使用預設的icon

上述有提到

若後端送出的推播請求meesage欄位中有notification屬性

將會自動在背景模式跳出提醒

且無法由後端設定icon(Web Push請求不可自訂icon)

因此若有額外需求

可以在後端發送推播請求的時候不要使用notification屬性

透過data屬性自訂要帶入的標題、內文、icon等資料

前端在透過onMessage或是onBackgroundMessage的callback

自行建立notification

 

<?php

namespace Modules\Base\Services;
use GuzzleHttp\Client;
class FcmService
{
    protected $client, $messageId, $serverKey;
    protected $success, $error;
    public function __construct()
    {
        $this->serverKey = env('FCM_SERVER_KEY');
        $this->client = new Client();
    }

    public function send(string $deviceToken)
    {

        $this->client->request('post', 'https://fcm.googleapis.com/fcm/send', [
            'headers' => [
                'Authorization' => "key={$this->serverKey}",
            ],
            'json' => [
                'to' => $deviceToken,
                'data' => [
                    'title' => 'title',
                    'body' => 'content...',
                    'icon' => 'https://foobar/foobar.jpg',
                ],
            ],
        ]);
    }
}