Firebase 系列文的第二篇,之前写过【如何在 React 专 案中接收 Notification】,这篇文章有点像,只是不使用 cra-template-pwa,而是完全的只用 Firebase 提供的功能
前置作业
如果还没有建置专案及安装 Firebase SDK 的话,请先参考【Firebase feat React 简易教学 (一):简介&快速 hosting 新网站】
产生 云端通讯 网路推播凭证金钥
到「专案设定」=>「云端通讯」
到「网路设定」=>「网路推播凭证」,点选「Generate key pair」
装会产生一个金钥组
然后同样在 App.js 加入以下 code
import { getMessaging, getToken } from "firebase/messaging";
// messaging
const messaging = getMessaging();
const vapidKey = 'BHweFH.......................'
getToken(messaging, { vapidKey: vapidKey }).then((currentToken) => {
if (currentToken) {
// 取得 currentToken,需传回 server
console.log('get currentToken');
console.log(currentToken);
} else {
console.log('No registration token available. Request permission to generate one.');
}
}).catch((err) => {
console.log('An error occurred while retrieving token. ', err);
});
- vapidKey 就是刚才产生的金钥组
- 这份程式会印出 currentToken 的值,这个值每个使用者每个浏览器都不一样,一般来说要把这个值传回 server 存起来,然后再依照存下来的 token 发送讯息,这里简化起见,仅先印出来
打开专案首页,叫出 Chrome DevTools,就可以看到印出来的 token
建立 Listener
Firebase 有自己的 service worker,档名叫做firebase-messaging-sw.js
,位置在根目录下,这里踩了点雷,需要注意
- 这个档案不会自动产生,需要手动建立,然后根目录以 React 来说,就是放在 public 目录下,而非 src 目录
- 因为不是放在 src 下,所以语法不太一样,不能直接用 import ...etc
将以下程式写入 firebase-messaging-sw.js,这段程式会去监听 BackgroundMessage,也就是只要浏览器开着,即使没上连上网站,也能收到讯息
// Scripts for firebase and firebase messaging
importScripts('https://www.gstatic.com/firebasejs/8.2.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.0/firebase-messaging.js');
const firebaseConfig = {
....
};
firebase.initializeApp(firebaseConfig);
// Retrieve firebase messaging
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function (payload) {
console.log('Received background message ', payload);
});
然后 App.js 也增加以下 code,这段程式会监听在网站开启时,传送讯息会在左下角跳出小视窗
import { onMessage } from "firebase/messaging";
onMessage(messaging, (payload) => {
console.log('Message received. ', payload);
const notificationTitle = 'Message Title';
const notificationOptions = {
body: 'Message body.',
icon: '/logo192.png'
};
new Notification(notificationTitle, notificationOptions)
});
传送讯息
整理两种方法
由 Firebase 界面传送讯息
Firebase 界面本身就提供传送讯息的功能,到左侧 menu 的「Cloud Messaging」,选功能「撰写通知」,填入标题和文字,然后点选「传送测试讯息」
这时会要你输入 FCM 注册凭证,这个凭证就是上面说的要传回给 server 的 token,一般的做法是 server 会将这些 token 存在 DB 中,发送讯息就会依据这些 token 发送, 这里只需要填入刚才的 token ,然后点选发送就可以看到收到讯息了
使用 googleapis 传送讯息
在实务上应该不可能用界面来发送,而且透过程式来传送讯息,我是另外开一个小专案来测试传送,程式不难,直接上程式码
先到「专案设定」=>「服务帐户」
然后产生私密金钥,然后就可以下载个 json 档。注意:如说明,金钥是用来存取专案的权限,请勿储放在公开的存放区
下载回来的 json 档大概长这样
然后在要放送讯息的专案,安装 googleapis 和 google-auth-library
npm install googleapis
npm install google-auth-library
传送讯息程式码如下,这是 firebase 的 sample code,但我写这篇文章的时候又找不到原文在哪里, 还好之前有先复制下来
const https = require('https');
const { google } = require('googleapis');
const key = require('./下载回来的私密金钥档案.json');
const PROJECT_ID = key.project_id;
const HOST = 'fcm.googleapis.com';
const PATH = '/v1/projects/' + PROJECT_ID + '/messages:send';
const MESSAGING_SCOPE = 'https://www.googleapis.com/auth/firebase.messaging';
const SCOPES = [MESSAGING_SCOPE];
const token = "要传送的浏览器 token";
/**
* Get a valid access token.
*/
function getAccessToken() {
return new Promise(function (resolve, reject) {
const jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
SCOPES,
null
);
jwtClient.authorize(function (err, tokens) {
if (err) {
reject(err);
return;
}
console.log(tokens.access_token);
resolve(tokens.access_token);
});
});
}
/**
* Send HTTP request to FCM with given message.
*
* @param {object} fcmMessage will make up the body of the request.
*/
function sendFcmMessage(fcmMessage) {
getAccessToken().then(function (accessToken) {
const options = {
hostname: HOST,
path: PATH,
method: 'POST',
// [START use_access_token]
headers: {
'Authorization': 'Bearer ' + accessToken
}
// [END use_access_token]
};
const request = https.request(options, function (resp) {
resp.setEncoding('utf8');
resp.on('data', function (data) {
console.log('Message sent to Firebase for delivery, response:');
console.log(data);
});
});
request.on('error', function (err) {
console.log('Unable to send message to Firebase');
console.log(err);
});
request.write(JSON.stringify(fcmMessage));
request.end();
});
}
function buildCommonMessage() {
return {
"message": {
"token": token,
"notification": {
"title": "FCM Notification",
"body": "Notification from FCM"
}
}
}
}
sendFcmMessage(buildCommonMessage());
测试成功,就像使用 Firebase 界面传送讯息,可成功接收到传送的讯息