AndroidスマホにPUSH通知を送るというだけのことに、てこずりました。
参考にできそうな記事はたくさんあり、それを真似ているのに、なぜかPUSH通知されないのです。
とりあえず(自分は)こうやってできた、というメモ書きとして残します。
環境(状況)
C# .NETCore3.1
AndroidにPUSH通知を受信するサンプルアプリを作成・インストール済み
FCM(Firebase)にPUSH通知のためのプロジェクトを作成済み
ゴール
FCMにPUSH通知のリクエストをするC#ソースコードを得る。Azure内で動くC# .NETCore3のソースがほしいが、まずはコンソールアプリでよい。
(追記)
Azureで実行する際に、key.p12というファイルの取得を行うコードを変更しました。
やってみて分かったこと
- PUSH通知のリクエスト(WebAPI)を送るエンドポイント(URL)が変更されている。
- Google Api HTTP v1というAPIを使う。
- Googleサービスアカウントに対しPrivate keyを作成し、それをOAuth2.0のアクセストークン取得に使う
紹介記事のほとんどは旧のエンドポイントが使われています。FCMに移行された2019/5以降の記事さえ、旧のエンドポイントが使われています。
紛らわしいのは、いまだに旧のエンドポイントにFCM HTTP APIで送ることができ、そのリクエストのレスポンスは"success": 1を返すことです。でも、AndroidにはPUSH通知がきません。もしかすると、FCMに移行された2019/5/29以降に作られたFirebaseプロジェクトでは、このようにリクエストは成功するけどPUSH通知は発生しない、という挙動になるのかもしれません。 (編集) 誤解の可能性がありました。PUSHデータ内の通知ペイロード(notification)の有無でAndroid側の挙動が変わること、Androidアプリ側が正しくない可能性もあったこと、等々・・。以下、旧プロトコルが使えないという記載を削除しました。
旧
POST https://fcm.googleapis.com/fcm/send
新
POST https://fcm.googleapis.com/v1/projects/{projectID}/messages:send
v1では新のほうのエンドポイントとなります。
旧のエンドポイントを使っている記事(例えばQiitaにもある)によれば、PostmanでPOSTコマンド一発でPUSH通知ができるようです。それに倣ってやってみると、確かにPOSTは成功し、レスポンスも"success": 1 が返ってきます。
一方、新のエンドポイントでは、APIがv1に変わっているので、上記のような記事の内容そのままとはいきません。さらに、v1ではOAuth2.0が使われておりアクセストークンを使うためやりとりが増えるはずなので、PostmanでPOSTコマンド一発、というわけにはいかなそうです。なのでPostmanで試してみることはやめ、いきなりC#コードを書いてみることにしました。
Googleドキュメントを読むと、一応、v1 APIでOAuthを使うにはどうしたらいいのかが書かれています。・・・が、いろんなことが沢山書かれているので、迷子になってしまいました。
結局こうした、というのを書き出してみると、
作成済みのFirebaseプロジェクトで、サービスアカウントに対し、Private keyを作成。



Jsonファイルを作る手順もあるようですが、今回のコードではP12を選択します。

作成された(ダウンロードされた) P12ファイルを、key.p12にリネームします。
.NETCore3.1で、コンソールアプリを作成します。この手順の説明は割愛します。
NuGetでGoogle.Api.AuthおよびGoogle.Apis.FirebaseCloudMessaging.v1をインストールします。また、key.p12をプロジェクトに追加し、出力ディレクトリにコピーで「新しい場合はコピーする」とします。

(追記) Androidへの通知のときにchannelを追加できるよう修正。
using System;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.FirebaseCloudMessaging.v1;
namespace SendToFcm
{
class Program
{
static void Main(string[] args)
{
String serviceAccountEmail = "hoge_webservices@appspot.gserviceaccount.com";
// fine on local. but it doesn't work on Azure. not sure why.
// var certificate = new X509Certificate2(@"key.p12", "notasecret", X509KeyStorageFlags.Exportable);
// this works on Azure.
var certificate = new X509Certificate2(file, "notasecret", X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { Google.Apis.FirebaseCloudMessaging.v1.FirebaseCloudMessagingService.Scope.CloudPlatform }
}.FromCertificate(certificate));
// Create the service.
var service = new FirebaseCloudMessagingService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "PUSH API Sample",
});
var requestBody = new Google.Apis.FirebaseCloudMessaging.v1.Data.SendMessageRequest();
requestBody.Message = new Google.Apis.FirebaseCloudMessaging.v1.Data.Message();
// token is a target which you want to send a PUSH to, and was made by an android-terminal with an android-app.
requestBody.Message.Token = "eGxxxxx0VxI:APA91xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxNG-f";
// Notification is to be seen on the android display.
requestBody.Message.Android = new Google.Apis.FirebaseCloudMessaging.v1.Data.AndroidConfig();
requestBody.Message.Android.Priority = "high";
requestBody.Message.Android.Notification = new Google.Apis.FirebaseCloudMessaging.v1.Data.AndroidNotification();
requestBody.Message.Android.Notification.Body = "body of your notif";
requestBody.Message.Android.Notification.Title = "title of your notif";
requestBody.Message.Android.Notification.ChannelId = "channelId";
// Data is data used by android app.
requestBody.Message.Data = new System.Collections.Generic.Dictionary<string, string>();
requestBody.Message.Data.Add("key1", "123");
requestBody.Message.Data.Add("key2", "456");
// projectId is from Firebase projcect
var projectId = "hoge_webservices";
// "Projects" is an instance of ProjectsResource. Projects.Messages is a REST resource.
var send = service.Projects.Messages.Send(requestBody, $"projects/{projectId}");
try
{
// Send HttpRequest
// Execute() returns a supposed-to-be "response" but no information in it, either success or error.
// Errors will be thrown as exceptions.
var response = send.Execute();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
}
参考
結局、Google公式ドキュメントを拾い読みしました。
エンドポイントが変わっていますよ、というドキュメント
以前の HTTP から HTTP v1 に移行する
HTTP v1のPUSH通知に載せるデータが比較的簡単に説明されているドキュメント。
FCM メッセージについて
C#(.NET)コード実装例は、Google API クライアントライブラリのドキュメントにあります。残念ながらずばり自分の欲しいものがある可能性は低そう。
API Client Libraries .NET ガイド Get Started
そこからさらにOAuth周りの実装は下記のページ。ここから、"サービスアカウント"を使えばよさそうだとわかり、"Service Account"のところのサンプルコードにより、ServiceAccountCredentialやxxxService(⇒FirebaseCloudMessagingService)というクラスを使えばよいことが分かります。
API Client Libraries .NET ガイド OAuth 2.0
ここら辺までわかったら、GitHubのソースを見ながら、FirebaseCloudMessagingServiceを使ったサンプルコードを作ってみます。
GitHub googleapis/google-api-dotnet-client
そこからAPIごとのクラス
そこからFCM
上記のGitHubのソースから、さらにProjectsResourceおよびProjectsResource.MessagesResourceへ進めばよいと見当がつきます。ここで下記のAPIドキュメントが参考になることがわかりました。
APIリファレンス REST Resource: projects.messages
このAPIリファレンスではメソッドを試す機能があるようです。ここでの例なら、Methodsのsendをクリックすると下記ページに移り、そこにTry this APIとあります。私は残念ながらOAuthをどうすればよいか分からず使えませんでした。
