将Firebase云信息服务作为一个公共/子服务使用

399 阅读7分钟

Firebase Cloud Messaging(FCM)主要以简化向客户端设备发送通知的过程而闻名。在这篇文章中,我们将学习如何在React应用程序中使用Firebase云信息服务作为推送通知服务和pub/sub服务。

什么是pub/sub系统?

一个发布/订阅系统由两方组成:负责向系统发送消息的发布者和订阅者,后者主动听取来自系统的通知,并可以决定对该消息采取进一步的行动。

发布/订阅系统的一个用例是来自服务器的无状态通信。在验证银行卡支付时,服务器几乎不可能让客户知道它已经验证了支付并授予了用户要求的服务。我们可以使用pub/sub系统轻松做到这一点。

在pub/sub系统中,浏览器监听一个特定的主题,而服务器则向该主题发送一个消息。浏览器立即收到消息,它就可以继续进行其余的客户端处理。

在React中用FCM建立一个pub/sub服务

在本教程中,我们将学习以下内容。

  • 如何在Firebase控制台设置FCM并创建一个新项目
  • 如何设置一个React应用来包含Firebase SDK
  • Firebase云消息的基本概念
  • 如何监听某个特定主题的消息
  • 如何使用HTTP请求向FCM的API发布一条消息

让我们开始吧!

创建一个FCM项目

对于这一部分,需要一个谷歌账户。

首先到console.firebase.google.com/,用你的谷歌账户登录。点击白色的 "创建项目"按钮。

Screenshot of Firebase welcome screen

输入项目的名称,接受条款,然后点击继续。选择你想把项目连接到的账户。

Gif of project creation process in Firebase

创建一个Firebase应用程序

在Firebase控制台,点击白色圆圈中的代码图标**(</>),输入应用程序的名称,选择设置Firebase主机**,然后点击注册应用程序。这将需要一些时间来配置应用程序,然后它才会提示你进行下一步。

添加Firebase SDK安装Firebase CLI的步骤中,扫描一下说明,然后点击继续到控制台 ,最终完成设置。

Gif of adding Firebase to a React app process

获取凭证

让我们来获取Firebase的API密钥,这个密钥让浏览器有能力验证对Firebase API和Firebase JSON文件的请求。

在仪表板上,点击你的新应用的名字,然后点击齿轮图标来访问设置。

Gif detailing how to get API keys from Firebase

接下来,向下滚动到标签的底部;在SDK设置和配置部分,点击配置按钮,揭开网络推送配置。确保复制该配置并保存在安全的地方。

Gif of SDK setup configuration in Firebase

要通过Google Firebase APIs执行授权操作,需要一个服务器密钥。要得到这个,请到项目设置下的云信息标签,向下滚动到项目凭证。复制并保存服务器密钥到安全的地方。

Screenshot of highlighted server key in Firebase project settings

设置一个React应用程序

在这一节中,我们将创建一个React应用程序,并通过它来设置Firebase。

在你的终端输入以下内容。

$ npx create-react-app pub-sub && cd pub-sub && code .

上面的命令将创建一个新的React应用程序到当前目录的pub-sub 。然后,将当前目录改为React应用程序的目录,并在Visual Studio Code中打开该项目进行编辑。

另外,从pub-sub 项目目录下的终端,你可以运行npm start 来打开开发文件夹。

将Firebase SDK安装到React应用程序中

在你的终端,从项目根目录下运行npm i firebase --save 来安装Firebase。

path-to-project/src/utils 创建一个新的文件夹,并在该文件夹中添加一个新的文件,firebaseConfig.json 。这个文件应该有Firebase网页推送设置页面的所有JSON值。

该文件的内容应该是这样的。

{
  apiKey: "***",
  authDomain: "logrocket-pub-sub.firebaseapp.com",
  projectId: "logrocket-pub-sub",
  storageBucket: "logrocket-pub-sub.appspot.com",
  messagingSenderId: "***",
  appId: "1:***:web:***",
  measurementId: "G-G7Q3DJ5GCN"
}

创建一个Firebase辅助工具

/src/utils 文件夹中,创建一个名为firebase.js 的文件,内容如下。

import firebase from "firebase/app";
// eslint-disable-next-line
import _messaging from "firebase/messaging";
import firebaseConfig from "./firebaseConfig";

const app = firebase.initializeApp(firebaseConfig);
export const fMessaging = app.messaging();

第一行是导入Firebase应用。导入Firebase messaging是为了在Firebase应用上添加和初始化Firebase messaging SDK。第四行导入你上面创建的Firebase配置文件。

第六行使用firebaseConfig JSON细节来初始化Firebase应用。最后一行是在Firebase应用程序上初始化云信息,这个应用程序在上面一行被初始化了。

添加firebase-messaging-sw.js

为了完成Firebase的集成,你必须在你的应用程序的一个可公开访问的路径上添加一个firebase-messaging-sw.js 文件,在这个例子中,在path-to-project/public

文件的内容应该是这样的。

// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here. Other Firebase libraries
// are not available in the service worker.
// eslint-disable-next-line
importScripts("https://www.gstatic.com/firebasejs/8.6.7/firebase-app.js");
// eslint-disable-next-line
importScripts("https://www.gstatic.com/firebasejs/8.6.7/firebase-messaging.js");

// Initialize the Firebase app in the service worker by passing in
// your app's Firebase config object.
// https://firebase.google.com/docs/web/setup#config-object
// eslint-disable-next-line
firebase.initializeApp({
  apiKey: "AIzaSyCu7r3TlqiiI_3HTJft_G-SSC8_*******",
  authDomain: "logrocket-pub-sub.firebaseapp.com",
  projectId: "logrocket-pub-sub",
  storageBucket: "logrocket-pub-sub.appspot.com",
  messagingSenderId: "*************",
  appId: "1:709132711133:web:***********************",
  measurementId: "G-*********",
});

// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
// eslint-disable-next-line
const messaging = firebase.messaging();

messaging.onBackgroundMessage((message) => {
  return self.showNotification(
    message.notification.title,
    message.notification
  );
});

前面几行应该很熟悉;Firebase应用程序和消息传递脚本被导入到服务工作者上下文中。接下来,在初始化Firebase消息传递之前,先初始化Firebase应用程序。

Firebase消息传递SDK上的onBackgroundMessage 方法可以捕捉到在浏览器、网页或应用程序未被激活时传递到客户端应用程序(这里是指浏览器)的任何消息。

在这里,通知徽章被触发了,以使用户了解在后台收到的新信息。

Firebase云信息传递的概念

要想在整合中充分地发挥自己的作用,你应该了解这些基本的Firebase云信息传递概念。

前台信息

这些是在浏览器处于活动状态时(例如,用户在页面/浏览器标签上)由客户端接收的消息。这可以通过Firebase messaging SDK上的.onMessage((message) => message) 方法来实现,并且不能在服务工作者上下文中调用。

背景消息

这些消息是在不活动的情况下传递给客户端浏览器的。这可以通过Firebase messaging SDK的.onBackgroundMessage((message) => message) 方法来实现,并且只能在服务工作者的上下文中调用。

一个主题的订阅者

订阅者是一个发送消息的目标群体。移动应用程序可以订阅接收消息,而浏览器不能使用浏览器SDK订阅任何问题。我们将在本文后面学习如何从浏览器中订阅一个主题。

消息/通知数据

默认情况下,客户端收到的所有消息应该是一个看起来像以下的对象。

{
  "notification": {
      "title": "This is the title",
    "body": "This is the body",
    "priority": "high|normal"
  },
  data: {
    anExtra: "Something",
    size: "has a size limit to avoid failure"
  }
}

notification 对象必须至少有titlebody 才能成功发送,而data 可以是一个任意的对象,根据 FCM 文档,不应超过 4000 字节。

notification 对象用于显示基于客户端设备的本地通知,而在我们的案例中,我们并不希望这样。稍后,我们将看到学习如何防止FCM有新消息时弹出通知。

订阅一个已知的主题名称

一个发布/订阅系统主要处理的是主题。一个主题是一组用户或客户,可以获得一组特定的消息。

Firebase网络JavaScript SDK不支持主题订阅,但可以通过HTTP请求到 [https://iid.googleapis.com/iid/v1/](https://iid.googleapis.com/iid/v1/)' + accessToken + '/rel/topics/' + topic.

accessToken 是需要订阅的客户的当前访问令牌。主题是一个持有主题名称的字符串。

为了实现主题订阅,你需要上面指定的accessToken 。在你的React应用程序中,打开Firebase实用程序助手,添加下面的代码。

export const subscribeToTopic = (topicName, handler = () => {}) =>
  fMessaging.getToken().then((currentToken) => {
    if (currentToken) {
      const FIREBASE_API_KEY = `AAAA*******:********************************************************************************************************************************************`;
      // Subscribe to the topic
      const topicURL = `https://iid.googleapis.com/iid/v1/${currentToken}/rel/topics/`;
      return fetch({
        url: topicURL,
        method: "POST",
        headers: {
          Authorization: `key=${FIREBASE_API_KEY}`,
        },
      })
        .then((response) => {
          fMessaging.onMessage(
            (payload) => {
              handler(payload);
            },
            (error) => {
              console.log(error);
            }
          );
        })
        .catch(() => {
          console.error(`Can't subscribe to ${topicName} topic`);
        });
    }
  });

在这里,消息SDK上的getToken 函数返回客户端的当前令牌;有时,如果用户没有给出推送通知所需的权限,则会失败。

接下来,为主题订阅发出HTTP请求;一旦成功,messaging().onMessage ,为客户端监听消息。

要在你的React应用程序中实现subscribeToTopic ,请替换应用程序中的App.js 文件,以包含以下内容。

import React, { useEffect } from "react";
import "./App.css";
import { subscribeToTopic } from "./utils/firebase";

function App() {
  function topicOnMessageHandler(message) {
    console.log(message);
  }

  useEffect(() => {
    subscribeToTopic("LOGROCKET_PUB_SUB_TOPICS", topicOnMessageHandler).then();
  }, []);

  return <div className="App">Firebase Pub / Sub System</div>;
}

export default App;

首先,定义函数topicOnMessageHandler ,以处理任何来到主题的消息,并对其进行处理;它只被记录到控制台。

subscribeToTopic 函数在一个useEffect 钩子中被调用,它接收主题的名字作为LOGROCKET_PUB_SUB_TOPICS ,接收topicOnMessageHandler 作为处理程序。

每当有消息发送到LOGROCKET_PUB_SUB_TOPICS 主题时,你的React应用就会收到它并将其记录到控制台。

处理后台消息

服务工作者文件firebase-messaging-sw.js 已经实现了Firebase消息传送SDK的onBackgroundMessage 方法。在该函数中,消息被记录到控制台,这适用于这个用例。

向React应用发布消息

在一个pub/sub系统中,应该有一个消息的发布者;我们刚刚建立的React应用一直是订阅者。

为了测试这个实现,进入Firebase控制台,展开Engage侧边栏菜单,然后点击Cloud Messaging,进入云消息仪表板。然后点击发送你的第一条消息按钮。

Screenshot of FCM engage tab

撰写通知的工具中,输入通知的标题和正文,然后点击下一步。在目标部分,选择一个主题并输入你在订阅时使用的主题。你可以将信息安排在稍后,或立即发送。点击 "审查"来最终完成 这一过程。

Gif of FCM compose notification tool

一旦通知被发送,你应该看到一个像这样的通知徽章。

Screenshot of test notification

以及收到的消息的控制台日志。

Screenshot of console log for test notification

在控制台之外发送消息

除了仪表板之外,你还可以使用HTTP请求来发送消息。 [https://fcm.googleapis.com/fcm/send](https://fcm.googleapis.com/fcm/send)其中包含通知对象和授权头的主体:key=FIREBASE_API_KEY

请求的主体应该是这样的。

{
    "data": {"Holla": "True"},
    "to": "/topics/LOGROCKET_PUB_SUB_TOPICS",
    "notification": {
        "title": "This is from Postman",
        "body": "hello there"
    }
}

还有一个授权头,描述为Authorization: "key=API_KEY"

Gif of authorization header being added

这有什么用呢?通过这种HTTP方式,服务器上的远程操作就有可能向某些客户订阅的特定主题发送一个通知。

就像它存在于pub/sub系统中一样,客户端浏览器已经作为订阅者;远程服务器可以作为通知的发布者。

防止通知徽章

FCM是以通知著称的。如果它应该作为一个pub/sub服务,通知通常是不必要的。

我们在本文中发布消息的方法总是会引起弹出的通知徽章。你可以通过在发布新消息时从你要发送的有效载荷中省略notification 对象来防止这种情况,像这样。

{
    "data": {"Holla": "True"},
    "to": "/topics/LOGROCKET_PUB_SUB_TOPICS"
}

这样一来,消息就会被送达,通知徽章就不会弹出,而消息处理程序也能有效地处理消息。

从服务工作者向浏览器主线程发送消息

当收到一个后台消息时,onBackgroundMessage ,在服务工作者上下文中被调用。

你可以用self.postMessage({}) 从服务工作者线程向主浏览器线程发送消息,然后用window.addEventListener("onmessage", message => console.log(message)) 在主线程上接收消息。

上述解决方案可以工作,但在这种情况下是不可维护的,因为消息可以在两个地方到达:通过onMessageonBackgroundMessage

更容易管理和维护的方法是将这两个消息推送到一个可以订阅的事件系统,该系统将处理这些消息,无论消息来自哪里。

在这种情况下,BroadcastChannelAPI可能很有用,正如这篇文章所建议的

onBackgroundMessage 函数里面,而不是安慰,你可以把消息发布到一个频道。

messaging.onBackgroundMessage((message) => {
  // if the sent data does not contain notification,
  // no notification would be shown to the user
  const fcmChannel = new BroadcastChannel("fcm-channel");
  fcmChannel.postMessage(message);
});

另外,在subscribeToTopic 的处理程序中,用下面的内容替换控制台日志。

const fcmChannel = new BroadcastChannel("fcm-channel");
fcmChannel.postMessage(message);

要消费这个消息,在测试React应用程序的任何地方,在App.js 文件中创建另一个useEffect 钩子,并像下面这样实现BroadcastChannel API的onmessage 事件。

useEffect(() => {
    const fcmChannel = new BroadCastChannel("fcm-channel");
  fcmChannel.onmessage = (message) => console.log(message);
}, [])

有了这个改变,onmessage 处理程序就能处理来自FCM的所有消息,并将它们记录到控制台。

结论

作为一个有效的推送通知服务,FCM也可以作为一个Pub/Sub系统,并且仍然可以利用现有的基础设施。

这篇文章还分享了如何使用谷歌的API,使FCM的工作更容易,而不是依赖SDK,并使一些边缘案例的使用成为可能。

将BroadcastChannel作为一个事件,对于在不同的FCM消息传递模式中同步数据非常有用。

通过本帖的说明,你可以做到服务器与客户端的无缝通信,而不需要用通知徽章来打断用户。

The postUsing Firebase Cloud Messaging as a pub/sub serviceappeared first onLogRocket Blog.