如何使用 Web 浏览器通过 Firebase Cloud Messaging 订阅主题

30
我正在寻找一种方法,通过Firebase Cloud Messaging向我的应用程序的所有用户发送通知,但我有一个仅限Web的应用程序。我看到了一些解决方案,似乎是针对Android/iOS的,基本上涉及让用户自动订阅名为"allDevices"的主题,然后向所有订阅该主题的用户发送通知。我只是找不到有关如何使基于Web的用户订阅主题的任何文档。是否有人知道是否有可能,并且如果可以,有没有我错过的覆盖此内容的文档?
谢谢!
5个回答

34

在 JavaScript 的 Firebase Cloud Messaging SDK 中,没有直接订阅客户端到主题的 API。但可以通过 REST API 将令牌订阅到主题。调用此 API 需要指定 FCM 服务器密钥,这意味着您只应在受信任的环境中执行此操作,例如开发计算机、您控制的服务器或 Cloud Functions。由于拥有 FCM 服务器密钥允许代表您的应用向所有应用程序用户发送消息,因此这是必要的。

经过我的测试发现,我使用的是旧项目,在该项目中,客户端 API 密钥可以用于订阅主题。出于安全原因,新项目已移除此功能。

例如,在 Node.js 中,您可以调用 REST API 来为应用实例创建关系映射,方法如下:create a relation mapping for an app instance

function subscribeTokenToTopic(token, topic) {
  fetch('https://iid.googleapis.com/iid/v1/'+token+'/rel/topics/'+topic, {
    method: 'POST',
    headers: new Headers({
      'Authorization': 'key='+fcm_server_key
    })
  }).then(response => {
    if (response.status < 200 || response.status >= 400) {
      throw 'Error subscribing to topic: '+response.status + ' - ' + response.text();
    }
    console.log('Subscribed to "'+topic+'"');
  }).catch(error => {
    console.error(error);
  })
}

其中fcm_server_key是从您项目的Firebase控制台中获取的FCM服务器密钥。


更新:现在大多数Firebase Admin SDK也包含了将FCM令牌订阅到主题的选项,这使得在支持的环境中更容易完成此操作。有关更多信息,请参阅将客户端应用程序订阅到主题的文档。


谢谢,弗兰克!这看起来就像我需要走下去的小巷。我需要在我的Firebase项目中设置什么才能使用IID API吗?我需要在某个地方/某种方式预定义一个主题吗?我尝试使用此函数,但是控制台中出现了401错误:“订阅主题时出错:401-[object Promise]”。供参考,我已经验证了令牌的正确性(我可以向特定令牌发送有针对性的通知),并且我的apiKey也已正确设置。 - Derek
哦,为了更好的理解,我正在学习Firebase。我已经成功地运行了Firebase团队的JS消息快速入门项目,如果需要了解代码库的样子,这就是我使用的:https://github.com/firebase/quickstart-js/tree/master/messaging。 - Derek
谢谢你的回复,Frank,我很感激。由于某种原因,问题归结为我的Firebase项目的API密钥。我现在不确定为什么,但昨天我的项目上有一个与今天在Firebase控制台中看到的不同的API密钥。自动生成的代码用于将Firebase添加到我的Web应用程序,现在显示与我昨天写下的不同的API密钥。因此,基本上,该应用程序可以使用生成的代码工作,但是为了使您的功能正常工作,我必须硬编码昨天不同的密钥。我不知道为什么或如何更改...奇怪。 - Derek
2
你绝不能在客户端代码中公开服务器密钥。在我的测试中,该代码也可以使用客户端API密钥。但我听说这是无意的,所以行为可能已经改变了。有趣的是:我刚刚进行了测试,仍然能够使用我的客户端API密钥进行订阅,因此可能是最近更改了此行为或仅适用于较新的密钥。 - Frank van Puffelen
2
“https://iid.googleapis.com/iid/v1” 这个链接不是专门用于 GCM 吗?它也适用于 FCM 吗? - J. Doe
显示剩余11条评论

10

如需 PHP 解决方案,请参考以下内容。由于您将使用服务器的 API 密钥,因此请勿在客户端执行此操作。

客户端 Firebase JS 代码

// Initialize Firebase
var config = {
    apiKey: "xxxx",
    authDomain: "yyy",
    databaseURL: "zzzz",
    projectId: "aaaa",
    storageBucket: "bbbbb",
    messagingSenderId: "ccc"
  };
firebase.initializeApp(config);

const messaging = firebase.messaging();

messaging.requestPermission()
.then(function() {
  console.log('Notification permission granted.');
  return messaging.getToken();
})
.then(function(token) {

//send this token to server
  console.log(token); // Display user token
})
.catch(function(err) { // Happen if user deney permission

  console.log('Unable to get permission to notify.', err);
});

messaging.onMessage(function(payload){
    console.log('onMessage',payload);
})

使用 PHP Curl 的服务器端代码

$headers = array
    ('Authorization: key=' . API_ACCESS_KEY,
    'Content-Type: application/json');

$ch = curl_init();
// browser token you can get it via ajax from client side
$token = 'drVdtCt82qY:APA91bEZb99GvoS9knv-cp5ThVBiYGBjUwl_Ewj2tKaRFwp7HoG347utaNKbgLWmkxpGadABtIg-DspPUh5sC_bc2JrBKVw10Ejg72nYxZgD2wBU-adYJo0yi03lX22s5K2UEp6gwnMv';
curl_setopt($ch, CURLOPT_URL, "https://iid.googleapis.com/iid/v1/$token/rel/topics/testIshakTopic");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, array());
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
echo "The Result : " . $result;

希望它能对PHP开发者有所帮助


8
弗兰克的解决方案仍然有效。但是,您需要拥有一个服务器来为您订阅。
function subscribeTokenToTopic(token, topic) {
  fetch('https://myserver.com/'+token+'/rel/topics/'+topic, {
    method: 'POST',
    headers: new Headers({
      'Authorization': 'Bearer '+ oauthToken
    })
  }).then(response => {
    if (response.status < 200 || response.status >= 400) {
      throw 'Error subscribing to topic: '+response.status + ' - ' + response.text();
    }
    console.log('Subscribed to "'+topic+'"');
  }).catch(error => {
    console.error(error);
  })
}

如果您的服务器有一个类似于以下内容的REST调用:
(Java Spring)
@RequestMapping(value = "/{token}/rel/topics/{topic}", method = RequestMethod.POST)
public ResponseEntity<?> subscribeTokenToTopic(@PathVariable("token") String token, @PathVariable("topic") String topic)  throws ServletException {
  URL url = new URL("https://iid.googleapis.com/iid/v1/"+token+"/rel/topics/"+topic);
  // make https post call here.. 
  ...
}

希望这有意义。

5

    import firebase from 'firebase/app';
    import 'firebase/messaging';

    const config = {
        apiKey: "xxxx",
        authDomain: "xxx",
        databaseURL: "xxx",
        projectId: "xxx",
        storageBucket: "xxxx",
        messagingSenderId: 'xxxxx',
        appId: 'xxxxx',
        measurementId: 'xxxxxx'
      };
      firebase.initializeApp(config);
        try {
              if (firebase.messaging.isSupported()) {
                const messaging = firebase.messaging();
                messaging
                  .getToken({
                    vapidKey: VAPID_KEY
                  })
                  .then((currentToken) => {
                    if (currentToken) {
                                           subscribeTokenToTopic(currentToken,FirebaseAdminTopic);
                    }
                  })
                  .catch((err) => {
                    console.log('Error to get token', err);
                  });

                messaging.onMessage((payload) => {
                  console.log(payload.notification)
                });

                // Otherwise, we need to ask the user for permission
                if (Notification.permission !== 'granted') {
                  Notification.requestPermission();
                }
              } else {
                console.log('firebase messaging not supported');
              }
            } catch (err) {
              console.log(err);
            }
            
            
            
        function subscribeTokenToTopic(token, topic) {
          fetch(`https://iid.googleapis.com/iid/v1/${token}/rel/topics/${topic}`, {
            method: 'POST',
            headers: new Headers({
              Authorization: `key=${FCM_SERVER_KEY}`
            })
          })
            .then((response) => {
              if (response.status < 200 || response.status >= 400) {
                console.log(response.status, response);
              }
              console.log(`"${topic}" is subscribed`);
            })
            .catch((error) => {
              console.error(error.result);
            });
          return true;
        }

 


0

使用服务器密钥而不是config.apiKey可以正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接