C# FCM与解析服务器推送通知不匹配SenderId和客户端应用程序错误

C# FCM与解析服务器推送通知不匹配SenderId和客户端应用程序错误,c#,android,firebase,parse-platform,push-notification,C#,Android,Firebase,Parse Platform,Push Notification,我曾经尝试使用GCM,但我无法让它与解析服务器一起工作。。所以我采纳了stackoverflow用户的建议,并尝试使用FCM 我的设备从FCM获取注册id,如下所示: 04-15 17:01:29.773 I/parse.GcmRegistrar(30144): GCM registration successful. Registration Id: APA91bFoNUPYdsjN6O_CkPje-O0hXjNz9kvURZMex72xClyBr_5o6D0vYtI-F0iyAGgSYjpI

我曾经尝试使用GCM,但我无法让它与解析服务器一起工作。。所以我采纳了stackoverflow用户的建议,并尝试使用FCM

我的设备从FCM获取注册id,如下所示:

04-15 17:01:29.773 I/parse.GcmRegistrar(30144): GCM registration successful. Registration Id: APA91bFoNUPYdsjN6O_CkPje-O0hXjNz9kvURZMex72xClyBr_5o6D0vYtI-F0iyAGgSYjpIEaJt2QQ2CXk2qpI11gPFUSUdzH-NxQRXSK3hPkuaiC_lciVV3E0fp6A_VZUoYJ8VxOIh
    // Example express application adding the parse-server module to expose Parse
// compatible API routes.

var express = require('express');
var ParseServer = require('parse-server').ParseServer;
var path = require('path');

var databaseUri = process.env.DATABASE_URI || process.env.MONGODB_URI;

if (!databaseUri) {
  console.log('DATABASE_URI not specified, falling back to localhost.');
}
var pushConfig = {};
if (process.env.GCM_SENDER_ID && process.env.GCM_API_KEY) {
   pushConfig['android'] = { 
   senderId: process.env.GCM_SENDER_ID || '',
   apiKey: process.env.GCM_API_KEY || ''};
}



var api = new ParseServer({
  databaseURI: databaseUri || 'mongodb://localhost:27017/dev',
  cloud: process.env.CLOUD_CODE_MAIN || __dirname + '/cloud/main.js',
  appId: process.env.APP_ID || 'myAppId',
  masterKey: process.env.MASTER_KEY || '', //Add your master key here. Keep it secret!
  serverURL: process.env.SERVER_URL || 'http://localhost:1337/parse',  // Don't forget to change to https if needed
    push: pushConfig,
  liveQuery: {
    classNames: ["Posts", "Comments"] // List of classes to support for query subscriptions
  }

});
// Client-keys like the javascript key or the .NET key are not necessary with parse-server
// If you wish you require them, you can set them as options in the initialization above:
// javascriptKey, restAPIKey, dotNetKey, clientKey

var app = express();

// Serve static assets from the /public folder
app.use('/public', express.static(path.join(__dirname, '/public')));

// Serve the Parse API on the /parse URL prefix
var mountPath = process.env.PARSE_MOUNT || '/parse';
app.use(mountPath, api);

// Parse Server plays nicely with the rest of your web routes
app.get('/', function(req, res) {
  res.status(200).send('I dream of being a website.  Please star the parse-server repo on GitHub!');
});

// There will be a test page available on the /test path of your server url
// Remove this before launching your app
app.get('/test', function(req, res) {
  res.sendFile(path.join(__dirname, '/public/test.html'));
});

var port = process.env.PORT || 1337;
var httpServer = require('http').createServer(app);
httpServer.listen(port, function() {
    console.log('parse-server-example running on port ' + port + '.');
});

// This will enable the Live Query real-time server
ParseServer.createLiveQueryServer(httpServer);
我试图用这个ID从firebase控制台发送一个通知,它正在工作。我的事件被触发,一切正常

当我想使用ParseCloud函数向我的用户发送通知时,问题就出现了。在搜索设备输出日志以查找错误时,我发现了以下错误:

04-15 17:01:25.490 E/parse.GcmRegistrar(30144): Found com.parse.push.gcm_sender_id <meta-data> element with value "id:767075137222", but the value is missing the expected "id:" prefix
我已经为Heroku宿主解析的配置变量定义了GCM_SENDER_ID和GCM_API_键

在我从客户端应用程序调用ParseCloud函数后,我在heroku日志中看到:

    Apr 15 08:03:02 fuseparse app/web.1: } method=POST, url=/parse/push, host=fuseparse.herokuapp.com, connection=close, user-agent=node-XMLHttpRequest, Parse/js1.11.1 (NodeJS 9.11.1), accept=*/*, content-type=text/plain, x-request-id=ea046fd0-5fb7-46b7-9ceb-e6a0fd2ebad1, x-forwarded-for=54.81.77.161, x-forwarded-proto=https, x-forwarded-port=443, via=1.1 vegur, connect-time=0, x-request-start=1523804582292, total-route-time=0, content-length=270, installationId=e2dc9f85-3c2f-464e-beca-c8b9d2cba528, alert=The Giants scored! 
Apr 15 08:03:02 fuseparse app/web.1: verbose: RESPONSE from [POST] /parse/push: { 
Apr 15 08:03:02 fuseparse app/web.1:   "headers": { 
Apr 15 08:03:02 fuseparse app/web.1:     "X-Parse-Push-Status-Id": "upnMh1652U" 
Apr 15 08:03:02 fuseparse app/web.1:   }, 
Apr 15 08:03:02 fuseparse app/web.1:   "response": { 
Apr 15 08:03:02 fuseparse app/web.1:     "result": true 
Apr 15 08:03:02 fuseparse app/web.1:   } 
Apr 15 08:03:02 fuseparse app/web.1: } X-Parse-Push-Status-Id=upnMh1652U, result=true 
Apr 15 08:03:02 fuseparse app/web.1: #### PUSH OK 
Apr 15 08:03:02 fuseparse app/web.1: verbose: _PushStatus upnMh1652U: sending push to installations with 1 batches 
Apr 15 08:03:02 fuseparse app/web.1: verbose: Sending push to 1 
Apr 15 08:03:02 fuseparse app/web.1: node-pre-gyp verb parse-server-push-adapter GCM sending to 1 device 
Apr 15 08:03:02 fuseparse app/web.1: node-pre-gyp verb parse-server-push-adapter GCM GCM Response: { 
Apr 15 08:03:02 fuseparse app/web.1: node-pre-gyp verb parse-server-push-adapter GCM     "multicast_id": 5516369214301735000, 
Apr 15 08:03:02 fuseparse app/web.1: node-pre-gyp verb parse-server-push-adapter GCM     "success": 0, 
Apr 15 08:03:02 fuseparse app/web.1: node-pre-gyp verb parse-server-push-adapter GCM     "failure": 1, 
Apr 15 08:03:02 fuseparse app/web.1: node-pre-gyp verb parse-server-push-adapter GCM     "canonical_ids": 0, 
Apr 15 08:03:02 fuseparse app/web.1: node-pre-gyp verb parse-server-push-adapter GCM     "results": [ 
Apr 15 08:03:02 fuseparse app/web.1: node-pre-gyp verb parse-server-push-adapter GCM         { 
Apr 15 08:03:02 fuseparse app/web.1: node-pre-gyp verb parse-server-push-adapter GCM             "error": "MismatchSenderId" 
Apr 15 08:03:02 fuseparse app/web.1: node-pre-gyp verb parse-server-push-adapter GCM         } 
Apr 15 08:03:02 fuseparse app/web.1: node-pre-gyp verb parse-server-push-adapter GCM     ] 
Apr 15 08:03:02 fuseparse app/web.1: node-pre-gyp verb parse-server-push-adapter GCM } 
Apr 15 08:03:02 fuseparse app/web.1: verbose: _PushStatus upnMh1652U: sent push! 0 success, 1 failures 
Apr 15 08:03:02 fuseparse app/web.1: verbose: _PushStatus upnMh1652U: needs cleanup devicesToRemove=[] 

我已经做了好几天了。。有人能告诉我我想做的是可能的吗?如果可能的话,我哪里做错了

如果您使用fcm通知,您需要将其添加到您的清单中

<!-- Firebase Notifications -->
        <service android:name=".HERE_YOUR_CLASS_WHICH_EXTENDS_FirebaseMessagingService">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>

        <service android:name=".HERE_YOUR_CLASS_WHICH_EXTENDS_FirebaseInstanceIDService">
            <intent-filter>
                <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
            </intent-filter>
        </service>
        <!-- ./Firebase Notifications -->

任何对此错误有问题的人:

04-15 17:01:25.490 E/parse.GcmRegistrar(30144): Found com.parse.push.gcm_sender_id <meta-data> element with value "id:767075137222", but the value is missing the expected "id:" prefix
04-15 17:01:25.490 E/parse.gcmregistar(30144):找到值为“id:767075137222”的com.parse.push.gcm_sender_id元素,但该值缺少预期的“id:”前缀
但是在androidmanifest中定义了gcm_sender_id,很可能安装了错误的sdk或parse.dll。我所做的是下载了parseplatform github中提供的开源SDK,并修改了gcmregistar.cs类,使发送方id不返回null:

这花了我很长时间,但是对于任何有相同问题的人,请下载最新的.dll,或者如果问题仍然存在,这意味着dll没有更新,您必须手动执行此操作。在我的例子中,我使用的是.NET Sdk,它没有更新

下载开放源代码sdk并将Internal/Push/gcmregistar.cs替换为

using System;
using Android.App;
using Android.Content;
using Android.OS;
using System.Threading.Tasks;

namespace Parse {
  internal class GcmRegistrar {
    private const string LogTag = "parse.GcmRegistrar";

    private const string ExtraRegistrationId = "registration_id";

    private const string ExtraSenderId = "com.parse.push.gcm_sender_id";
    private const string ParseGcmSenderId = "1076345567071";

    public const string IntentRegisterAction = "com.google.android.c2dm.intent.REGISTER";

    private readonly Object mutex = new Object();
    private Request request;
    private Context context;

    public static GcmRegistrar GetInstance() {
      return Singleton.Instance;
    }

    private static class Singleton {
      public static readonly GcmRegistrar Instance = new GcmRegistrar(Application.Context);
    }

    private GcmRegistrar(Context context) {
      this.context = context;
    }

    private string getActualSenderIdFromExtra(Object senderIdExtra) {
            if (senderIdExtra == null ) {
        return null;
      }
      string senderId = senderIdExtra.ToString();
      if (!senderId.StartsWith("id:")) {
        return null;
      }

      return senderId.Substring(3);
    }

    public void Register() {
      ParseInstallation installation = ParseInstallation.CurrentInstallation;

      lock (mutex) {
        if (installation.DeviceToken == null && request == null) {
          var metadata = ManifestInfo.GetApplicationMetaData();
          object senderIdExtra = null;
          if (metadata != null) {
            senderIdExtra = metadata.Get(ExtraSenderId);
          }

          string senderIds = ParseGcmSenderId;
          if (senderIdExtra != null) {
            string senderId = getActualSenderIdFromExtra(senderIdExtra);

            if (senderId != null) {
              senderIds += "," + senderId;
            } else {
              Android.Util.Log.Error("parse.GcmRegistrar", "Found " + ExtraSenderId + " <meta-data> element with value \""
                + senderIdExtra.ToString() + "\", but the value is missing the expected \"id:\" prefix");
            }
          }
          request = Request.CreateAndSend(this.context, senderIds);
        }
      }
    }

    /// <summary>
    /// Handles GCM registration intent from <see cref="ParsePushBroadcastReceiver"/> and saves the GCM registration
    /// id as <see cref="ParseInstallation.CurrentInstallation"/> device token.
    /// </summary>
    /// <remarks>
    /// Should be called by a broadcast receiver or service to handle GCM registration response
    /// intent (com.google.android.c2dm.intent.REGISTRATION).
    /// </remarks>
    /// <param name="intent"></param>
    public Task HandleRegistrationIntentAsync(Intent intent) {
      if (intent.Action == ParsePushBroadcastReceiver.ActionGcmRegisterResponse) {
        string registrationId = intent.GetStringExtra(ExtraRegistrationId);

        if (registrationId != null && registrationId.Length > 0) {
          Android.Util.Log.Info(LogTag, "GCM registration successful. Registration Id: " + registrationId);
          ParseInstallation installation = ParseInstallation.CurrentInstallation;

          // Set `pushType` via internal `Set` method since we want to skip mutability check.
          installation.Set("pushType", "gcm");
          installation.DeviceToken = registrationId;

          return installation.SaveAsync();
        }
      }
      return Task.FromResult(0);
    }

    /// <summary>
    /// Encapsulates the GCM registration request-response, potentially using <c>AlarmManager</c> to
    /// schedule retries if the GCM service is not available.
    /// </summary>
    private class Request {
      private Context context;
      private string senderId;
      private PendingIntent appIntent;

      public static Request CreateAndSend(Context context, string senderId) {
        Request request = new Request(context, senderId);
        request.Send();

        return request;
      }

      private Request(Context context, string senderId) {
        this.context = context;
        this.senderId = senderId;
        appIntent = PendingIntent.GetBroadcast(context, 0, new Intent(), 0);
      }

      private void Send() {
        Intent intent = new Intent(IntentRegisterAction);
        intent.SetPackage("com.google.android.gsf");
        intent.PutExtra("sender", senderId);
        intent.PutExtra("app", appIntent);

        ComponentName name = null;
        try {
          name = context.StartService(intent);
        } catch (Exception) {
          // Do nothing.
        }
      }
    }
  }
}
使用系统;
使用Android.App;
使用Android.Content;
使用Android.OS;
使用System.Threading.Tasks;
名称空间解析{
内部类GCMRegistar{
private const string LogTag=“parse.gcmregistar”;
private const string extralregistrationid=“registration\u id”;
private const string ExtraSenderId=“com.parse.push.gcm\u sender\u id”;
私有常量字符串ParseGcmSenderId=“1076345567071”;
public const string intentregistAction=“com.google.android.c2dm.intent.REGISTER”;
私有只读对象互斥体=新对象();
私人请求;
私人语境;
公共静态gcmregistar GetInstance(){
返回Singleton.Instance;
}
私有静态类单例{
public static readonly gcmregistar Instance=new gcmregistar(Application.Context);
}
专用GCMRegistar(上下文){
this.context=上下文;
}
私有字符串getActualSenderIdFromExtra(对象senderIdExtra){
if(senderIdExtra==null){
返回null;
}
字符串senderId=senderIdExtra.ToString();
如果(!senderId.StartsWith(“id:)){
返回null;
}
返回senderId.Substring(3);
}
公开作废登记册(){
ParseInstallation=ParseInstallation.CurrentInstallation;
锁(互斥){
if(installation.DeviceToken==null&&request==null){
var metadata=ManifestInfo.GetApplicationMetaData();
对象senderIdExtra=null;
if(元数据!=null){
senderIdExtra=metadata.Get(ExtraSenderId);
}
字符串senderID=ParseGcmSenderId;
if(senderIdExtra!=null){
字符串senderId=getActualSenderIdFromExtra(senderIdExtra);
if(senderId!=null){
senderId+=“,”+senderId;
}否则{
Android.Util.Log.Error(“parse.gcmregistar”,“Found”+ExtraSenderId+”元素的值为“”
+senderIdExtra.ToString()+“\”,但该值缺少预期的\“id:\“前缀”);
}
}
request=request.CreateAndSend(this.context,senderID);
}
}
}
/// 
///处理GCM注册意图,并保存GCM注册
///id作为设备令牌。
/// 
/// 
///应由广播接收器或服务调用,以处理GCM注册响应
///intent(com.google.android.c2dm.intent.REGISTRATION)。
/// 
/// 
公共任务HandleRegistrationIntentAsync(意图){
if(intent.Action==ParsePushBroadcastReceiver.ActionGcmRegisterResponse){
string registrationId=intent.GetStringExtra(ExtraRegistrationId);
if(registrationId!=null&®istrationId.Length>0){
Android.Util.Log.Info(LogTag,“GCM注册成功。注册Id:”+registrationId);
ParseInstallation=ParseInstallation.CurrentInstallation;
//通过内部“Set”方法设置“pushType”,因为我们想跳过可变性检查。
安装组件(“推式”、“gcm”);
installation.DeviceToken=注册ID;
返回installation.SaveAsync();
}
}
返回Task.FromResult(0);
}
/// 
///封装GCM注册请求响应,可能使用AlarmManager
///如果GCM服务不可用,则计划重试。
/// 
私有类请求{
私人语境;
私有字符串senderId;
私人悬而未决的意图;
公共静态请求CreateAndSend(上下文上下文,字符串senderId){
请求=新请求(上下文,senderId);
request.Send();
返回请求;
}
私有请求(上下文,字符串senderId){
this.context=上下文;
this.senderId=senderId;
appIntent
04-15 17:01:25.490 E/parse.GcmRegistrar(30144): Found com.parse.push.gcm_sender_id <meta-data> element with value "id:767075137222", but the value is missing the expected "id:" prefix
using System;
using Android.App;
using Android.Content;
using Android.OS;
using System.Threading.Tasks;

namespace Parse {
  internal class GcmRegistrar {
    private const string LogTag = "parse.GcmRegistrar";

    private const string ExtraRegistrationId = "registration_id";

    private const string ExtraSenderId = "com.parse.push.gcm_sender_id";
    private const string ParseGcmSenderId = "1076345567071";

    public const string IntentRegisterAction = "com.google.android.c2dm.intent.REGISTER";

    private readonly Object mutex = new Object();
    private Request request;
    private Context context;

    public static GcmRegistrar GetInstance() {
      return Singleton.Instance;
    }

    private static class Singleton {
      public static readonly GcmRegistrar Instance = new GcmRegistrar(Application.Context);
    }

    private GcmRegistrar(Context context) {
      this.context = context;
    }

    private string getActualSenderIdFromExtra(Object senderIdExtra) {
            if (senderIdExtra == null ) {
        return null;
      }
      string senderId = senderIdExtra.ToString();
      if (!senderId.StartsWith("id:")) {
        return null;
      }

      return senderId.Substring(3);
    }

    public void Register() {
      ParseInstallation installation = ParseInstallation.CurrentInstallation;

      lock (mutex) {
        if (installation.DeviceToken == null && request == null) {
          var metadata = ManifestInfo.GetApplicationMetaData();
          object senderIdExtra = null;
          if (metadata != null) {
            senderIdExtra = metadata.Get(ExtraSenderId);
          }

          string senderIds = ParseGcmSenderId;
          if (senderIdExtra != null) {
            string senderId = getActualSenderIdFromExtra(senderIdExtra);

            if (senderId != null) {
              senderIds += "," + senderId;
            } else {
              Android.Util.Log.Error("parse.GcmRegistrar", "Found " + ExtraSenderId + " <meta-data> element with value \""
                + senderIdExtra.ToString() + "\", but the value is missing the expected \"id:\" prefix");
            }
          }
          request = Request.CreateAndSend(this.context, senderIds);
        }
      }
    }

    /// <summary>
    /// Handles GCM registration intent from <see cref="ParsePushBroadcastReceiver"/> and saves the GCM registration
    /// id as <see cref="ParseInstallation.CurrentInstallation"/> device token.
    /// </summary>
    /// <remarks>
    /// Should be called by a broadcast receiver or service to handle GCM registration response
    /// intent (com.google.android.c2dm.intent.REGISTRATION).
    /// </remarks>
    /// <param name="intent"></param>
    public Task HandleRegistrationIntentAsync(Intent intent) {
      if (intent.Action == ParsePushBroadcastReceiver.ActionGcmRegisterResponse) {
        string registrationId = intent.GetStringExtra(ExtraRegistrationId);

        if (registrationId != null && registrationId.Length > 0) {
          Android.Util.Log.Info(LogTag, "GCM registration successful. Registration Id: " + registrationId);
          ParseInstallation installation = ParseInstallation.CurrentInstallation;

          // Set `pushType` via internal `Set` method since we want to skip mutability check.
          installation.Set("pushType", "gcm");
          installation.DeviceToken = registrationId;

          return installation.SaveAsync();
        }
      }
      return Task.FromResult(0);
    }

    /// <summary>
    /// Encapsulates the GCM registration request-response, potentially using <c>AlarmManager</c> to
    /// schedule retries if the GCM service is not available.
    /// </summary>
    private class Request {
      private Context context;
      private string senderId;
      private PendingIntent appIntent;

      public static Request CreateAndSend(Context context, string senderId) {
        Request request = new Request(context, senderId);
        request.Send();

        return request;
      }

      private Request(Context context, string senderId) {
        this.context = context;
        this.senderId = senderId;
        appIntent = PendingIntent.GetBroadcast(context, 0, new Intent(), 0);
      }

      private void Send() {
        Intent intent = new Intent(IntentRegisterAction);
        intent.SetPackage("com.google.android.gsf");
        intent.PutExtra("sender", senderId);
        intent.PutExtra("app", appIntent);

        ComponentName name = null;
        try {
          name = context.StartService(intent);
        } catch (Exception) {
          // Do nothing.
        }
      }
    }
  }
}