Ionic 3的PWA和Firebase Cloud Messaging 注册

6

我正在阅读这篇文章(不幸的是它并不完整),尝试学习如何将Ionic 3基于PWA的应用程序与Firebase云消息传递进行集成:使用FCM推送通知

我所做的:

  1. 按照文章中的建议,在service-worker.js中添加了FCM库:

'use strict';
importScripts('./build/sw-toolbox.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-messaging');

firebase.initializeApp({
  // get this from Firebase console, Cloud messaging section
  'messagingSenderId': '47286327412'
});

const messaging = firebase.messaging();

messaging.setBackgroundMessageHandler((payload) => {
  console.log('Received background message ', payload);
  // here you can override some options describing what's in the message; 
  // however, the actual content will come from the service sending messages
  const notificationOptions = {
    icon: '/assets/img/appicon.png'
  };
  return self.registration.showNotification(notificationTitle, notificationOptions);
});

self.toolbox.options.cache = {
  name: 'ionic-cache'
};

// pre-cache our key assets
self.toolbox.precache(
  [
    './build/main.js',
    './build/vendor.js',
    './build/main.css',
    './build/polyfills.js',
    'index.html',
    'manifest.json'
  ]
);

// dynamically cache any other local assets
self.toolbox.router.any('/*', self.toolbox.cacheFirst);

// for any other requests go to the network, cache,
// and then only use that cached resource if your user goes offline
self.toolbox.router.default = self.toolbox.networkFirst;

然后在此创建了基于Firebase Messaging的提供程序:

import { Injectable } from "@angular/core";
import * as firebase from 'firebase';
import { Storage } from '@ionic/storage';

@Injectable()
export class FirebaseMessagingProvider {
  private messaging: firebase.messaging.Messaging;
  private unsubscribeOnTokenRefresh = () => {};

  constructor(
    private storage: Storage
  ) {
    this.messaging = firebase.messaging();
  }

  public enableNotifications() {
    console.log('Requesting permission...');
    return this.messaging.requestPermission().then(() => {
        console.log('Permission granted');
        // token might change - we need to listen for changes to it and update it
        this.setupOnTokenRefresh();
        return this.updateToken();
      });
  }

  public disableNotifications() {
    this.unsubscribeOnTokenRefresh();
    this.unsubscribeOnTokenRefresh = () => {};
    return this.storage.set('fcmToken','').then();
  }

  private updateToken() {
    return this.messaging.getToken().then((currentToken) => {
      if (currentToken) {
        // we've got the token from Firebase, now let's store it in the database
        return this.storage.set('fcmToken', currentToken);
      } else {
        console.log('No Instance ID token available. Request permission to generate one.');
      }
    });
  }

  private setupOnTokenRefresh(): void {
    this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
      console.log("Token refreshed");
      this.storage.set('fcmToken','').then(() => { this.updateToken(); });
    });
  }
    
}

现在,在应用程序初始化期间,我调用enableNotifications(),但是出现了错误,提示默认服务工作者未找到(404):

在获取脚本时收到了不良的HTTP响应代码(404)。 :8100/firebase-messaging-sw.js Failed to load resource: net::ERR_INVALID_RESPONSE

如果我将service-worker.js中的Firebase相关内容移动到WWW文件夹中的默认服务工作者中,则会从Firebase收到一般性错误(错误,无法注册服务工作者)。

问题: - 是否有关于Ionic 3的PWA和FCM的最新指南? - 在高层次上,Ionic 3与Angular中注册服务工作者的区别是什么?我看过有关Angular的教程,但不知道如何在Ionic 3中进行相同操作。


这个小组论坛中可以看到一些参考资料,例如这份文档这篇文章,它们都适用于Android和iOS平台。 - MαπμQμαπkγVπ.0
很遗憾,这只适用于“本地”(cordova)平台。我正在尝试为基于ionic的PWA - 渐进式Web应用程序解决此问题。 - Sergey Rudenko
好的,我进一步进行了研究并更改了实例化Firebase应用程序的方式,但问题仍然存在 - 尽管默认的service-worker.js具有所有代码,但Firebase仍然会给出错误请求404; - Sergey Rudenko
请阅读以下内容:https://serviceworke.rs/push-simple_index_doc.html 但那里的上下文有些不同... - Sergey Rudenko
2个回答

10

更新:以下内容截至今天(2018年2月12日)仍然有效,但一旦AngularFire2支持消息模块,它很可能变得不那么相关。所以请在这个前提下看待以下内容...

好的,我进行了研究,并最终让它在我的Ionic 3 PWA上运行,所以我在这里发布解决方案:

  1. 先决条件:
    • 我创建了一个空白的ionic应用程序(只有一个主页)
    • 使用npm install安装angularfire2和firebase(“angularfire2”:“5.0.0-rc.4”,“firebase”:“4.9.1”),我特别使用了5.0.0-rc.4,因为我在最新版本中遇到了稳定性问题;
    • 创建了一个配置文件(src文件夹中的environment.ts):

export const firebaseConfig = {
    apiKey: "Your Stuff Here from FB",
    authDomain: "YOURAPPNAME.firebaseapp.com",
    databaseURL: "https://YOURAPPNAME.firebaseio.com",
    projectId: "YOURAPPNAME",
    storageBucket: "YOURAPPNAME.appspot.com",
    messagingSenderId: "FROMFIREBASECONEOLE"
};

  1. 我修改了 app.module.ts 文件,按照以下方式添加了 firebase 和 angularfire2:

...
import { AngularFireModule } from 'angularfire2';
import 'firebase/messaging'; // only import firebase messaging or as needed;
import { firebaseConfig } from '../environment';
import { FirebaseMessagingProvider } from '../providers/firebase-messaging';
...

@NgModule({
  declarations: [
    MyApp,
    HomePage
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    AngularFireModule.initializeApp(firebaseConfig),
    IonicStorageModule.forRoot()
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
    HomePage
  ],
  providers: [
    FirebaseMessagingProvider,
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})
export class AppModule {}

这里我们还导入了我们的提供程序,其代码如下:

  1. 在提供程序文件夹中,我创建了一个名为firebase-messaging.ts的文件,内容如下:

import { Injectable } from "@angular/core";
import { FirebaseApp } from 'angularfire2';
// I am importing simple ionic storage (local one), in prod this should be remote storage of some sort.
import { Storage } from '@ionic/storage';

@Injectable()
export class FirebaseMessagingProvider {
  private messaging;
  private unsubscribeOnTokenRefresh = () => {};

  constructor(
    private storage: Storage,
    private app: FirebaseApp
  ) {
    this.messaging = app.messaging();
    navigator.serviceWorker.register('service-worker.js').then((registration) => {
    this.messaging.useServiceWorker(registration);
    //this.disableNotifications()
    this.enableNotifications();
});
  }

  public enableNotifications() {
    console.log('Requesting permission...');
    return this.messaging.requestPermission().then(() => {
        console.log('Permission granted');
        // token might change - we need to listen for changes to it and update it
        this.setupOnTokenRefresh();
        return this.updateToken();
      });
  }

  public disableNotifications() {
    this.unsubscribeOnTokenRefresh();
    this.unsubscribeOnTokenRefresh = () => {};
    return this.storage.set('fcmToken','').then();
  }

  private updateToken() {
    return this.messaging.getToken().then((currentToken) => {
      if (currentToken) {
        // we've got the token from Firebase, now let's store it in the database
        console.log(currentToken)
        return this.storage.set('fcmToken', currentToken);
      } else {
        console.log('No Instance ID token available. Request permission to generate one.');
      }
    });
  }

  private setupOnTokenRefresh(): void {
    this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
      console.log("Token refreshed");
      this.storage.set('fcmToken','').then(() => { this.updateToken(); });
    });
  }
    
}

请注意,我初始化了 Firebase 应用并在构造函数中注册了 ionic 的默认服务工作者(service-worker.js),它包含以下内容,紧接着默认值之后:

  1. service-worker.js:

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

firebase.initializeApp({
  // get this from Firebase console, Cloud messaging section
  'messagingSenderId': 'YOURIDFROMYOURFIREBASECONSOLE' 
});

const messaging = firebase.messaging();

messaging.setBackgroundMessageHandler(function(payload) {
  console.log('Received background message ', payload);
  // here you can override some options describing what's in the message; 
  // however, the actual content will come from the Webtask
  const notificationOptions = {
    icon: '/assets/images/logo-128.png'
  };
  return self.registration.showNotification(notificationTitle, notificationOptions);
});

在这一点上,您还需要确保将应用程序配置为PWA。Josh Morony提供了一份很好的指南,今天在YouTube上也有一个视频流介绍了该主题。简而言之,在index.html中取消注释:

  1. src目录下的index.html取消注释:

 <!-- un-comment this code to enable service worker -->
  <script>
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('service-worker.js')
        .then(() => console.log('service worker installed'))
        .catch(err => console.error('Error', err));
    }
  </script>

  1. 好的,几乎是最后一步 - 你的manifest.json文件(在src目录下)应该确切包含以下代码:
    "gcm_sender_id": "103953800507"

这里结束了客户端的初步设置。请注意,我还没有实现任何处理用户在应用程序内时收到通知的功能,现阶段只处理当您的标签页处于非活动状态(即我测试过的情况)时从服务器发送消息。

  1. 现在,您需要进入Firebase控制台并获取服务器密钥(点击设置齿轮图标,然后查看云消息部分)。复制服务器密钥。同时运行客户端(ionic serve)并捕获本地令牌(我只是使用console.log记录了它)。然后尝试使用POST方法给自己发送消息。(我用Postman完成了此操作)

// method: "POST",
//url: "https://fcm.googleapis.com/fcm/send",
    // get the key from Firebase console
    headers: { Authorization: `key=${fcmServerKey}` }, 
    json: {
        "notification": { 
            "title": "Message title",
            "body": "Message body",
            "click_action": "URL to your app?"
        },
        // userData is where your client stored the FCM token for the given user
        // it should be read from the database
        "to": userData.fcmRegistrationKey
    }

通过这样做,我能够在应用程序处于后台时可靠地发送自己的消息。我还没有处理前台,但是这个问题是关于如何初始化默认服务工作者并将其与FCM配合使用的。

我希望这能帮助未来的一些学习者。


我尝试了你的方法,在谷歌中成功注册了服务工作者,但在SAFARI和FIREFOX中出现错误“navigator.serviceWorker未定义”,有时在Chrome中也会出现此错误。 此外,messaging.getToken()现在也没有被调用。 不知道我错过了什么。 - Tabish Sohail
你正在iOS设备上测试PWA吗?如果是,请记住,iOS浏览器(无论“品牌”如何)尚不支持服务工作者。它们很快就会支持,但现在还没有;/ - Sergey Rudenko
我明白了,仍需检查服务工作者是否支持,直到最近苹果才开始支持。 - Sergey Rudenko
请问能帮忙吗?我在使用 getToken() 函数时遇到了问题,它没有返回任何值或错误。服务工作者已经成功安装并获得了权限,但是没有令牌。 - Tabish Sohail
你好,我已经成功地实现了流程并在 API 调用上获得了成功的响应。但是,我的浏览器上没有任何通知弹出。有任何想法吗?API:https://fcm.googleapis.com/fcm/send 响应结果:{"multicast_id":6904414188195222649,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1545375125056264%e609af1cf9fd7ecd"}]} - Bluerain
显示剩余4条评论

0

我已经成功地实现了该过程,并在API调用上获得了成功响应。但是我的浏览器上没有弹出任何通知弹窗。有什么想法吗?

API: https://fcm.googleapis.com/fcm/send

收到的响应:

{"multicast_id":6904414188195222649,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1545375125056264%e609af1cf9fd7ecd"}]}

请检查我控制台中附加的URL: enter image description here


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