【问题标题】:How to subscribe to topics with web browser using Firebase Cloud Messaging如何使用 Firebase Cloud Messaging 通过网络浏览器订阅主题
【发布时间】:2017-03-16 07:40:16
【问题描述】:

我正在尝试找到一种使用 Firebase 云消息传递向我的所有应用用户发送通知的方法,但我有一个仅限网络的应用。我见过似乎适用于 Android/iOS 的解决方案,它基本上涉及让用户自动订阅一个名为“allDevices”的主题,然后向订阅该主题的所有用户发送通知。我似乎找不到任何关于如何让基于网络的用户订阅主题的文档。有谁知道这是否可能,如果可能,是否有我遗漏的文档可以涵盖这一点?

谢谢!

【问题讨论】:

    标签: javascript firebase firebase-cloud-messaging


    【解决方案1】:

    在 Firebase Cloud Messaging SDK for JavaScript 中,没有直接的 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 是 FCM 服务器密钥,取自您项目的 Firebase 控制台。

    【讨论】:

    • 谢谢,弗兰克!这看起来像我需要走的小巷。为了使用 IID API,我需要对我的 Firebase 项目进行设置吗?我需要在某处/以某种方式预定义一个主题吗?我尝试使用此功能,但在控制台中收到 401 错误:“订阅主题时出错:401 - [object Promise]”。作为参考,我已经验证了令牌是正确的(我可以向特定令牌发送有针对性的通知)并且我的 apiKey 也设置正确。
    • 哦,为了进一步了解,我正在学习 Firebase。我已经启动并成功运行了 Firebase 团队的 JS 消息传递快速入门项目,所以如果它有助于了解代码库的外观,这就是我正在使用的:github.com/firebase/quickstart-js/tree/master/messaging
    • 主题会在设备订阅它们或您向它们发送消息(使用 API)时自动创建。
    • 您应该永远在客户端代码中公开服务器密钥。在我的测试中,代码也适用于客户端 API 密钥。但我听说这是无意的,所以可能是行为改变了。不过有趣的是:我刚刚进行了测试,我仍然能够使用 my 客户端 API 密钥进行订阅,因此这种行为可能是最近更改的,或者仅适用于较新的密钥。
    • GCM 的链接不是“iid.googleapis.com/iid/v1”吗?它也适用于 FCM 吗?
    【解决方案2】:

    任何寻找 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 开发者有所帮助

    【讨论】:

      【解决方案3】:

      Franks 解决方案仍然有效。但是,您需要有一个为您订阅的服务器。

      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);
        })
      }
      

      然后你的服务器有一个像这样的休息调用:

      (java 弹簧)

      @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.. 
        ...
      }
      

      希望这是有道理的。

      【讨论】:

      • 这是一个很棒的解决方案.. 非常感谢.. 你拯救了我的一天.. (甚至可能是一个星期,我打算使用 websocket 进行自定义实现!)
      【解决方案4】:

          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;
              }
      
       

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          猜你喜欢
          • 2017-02-24
          • 2019-10-19
          • 1970-01-01
          • 2020-04-26
          • 1970-01-01
          • 1970-01-01
          • 2018-08-23
          相关资源
          最近更新 更多