Błędy pakietu Admin SDK są podzielone na 2 kategorie:
- Błędy programowania: są to błędy programowania i konfiguracji w
do aplikacji użytkownika. Dzieje się tak głównie z powodu nieprawidłowego użycia pakietu SDK
np. przekazanie atrybutu
null
do metody, która nie akceptuje wartościnull
, oraz innych błędów konfiguracji na poziomie projektu Firebase lub pakietu SDK (brak dane logowania, nieprawidłowy ciąg znaków identyfikatora projektu itd.). - Błędy interfejsu API: zawierać różne błędy możliwe do naprawienia występujące w implementacji SDK, wszystkie błędy pochodzące z usług backendu Firebase i inne błędów (np. przekroczenia limitu czasu), które mogą wystąpić podczas wywołań RPC.
Pakiet Admin SDK sygnalizuje błędy programowania, wysyłając błąd natywny w z konkretnej platformy.
- Java: zwraca instancje
IllegalArgumentException
,NullPointerException
lub podobny typ błędu w czasie działania. - Python: podnosi instancje
ValueError
,TypeError
lub innego wbudowanego typu błędu. - Go: zwraca błąd ogólny.
- .NET: zwraca instancje
ArgumentException
,ArgumentNullException
lub podobnego wbudowanego błędu.
W większości przypadków nie należy obsługiwać błędów programowania bezpośrednio. Zamiast tego: musisz poprawić kod i konfigurację, by uniknąć błędów programowania. Przyjrzyjmy się temu fragmentowi kodu Java:
String uid = getUserInput();
UserRecord user = FirebaseAuth.getInstance().getUser(uid);
Jeśli metoda getUserInput()
zwraca null
lub puste ciągi, funkcja
Interfejs API FirebaseAuth.getUser()
zgłasza IllegalArgumentException
. Zamiast
w sposób jednoznaczny, można zminimalizować problem, upewniając się,
Metoda getUserInput()
nigdy nie zwraca nieprawidłowego ciągu znaków identyfikatora UID. Jeśli nie
zaimplementuj w swoim kodzie niezbędne argumenty do sprawdzania argumentów w następujący sposób:
String uid = getUserInput();
if (Strings.isNullOrEmpty(uid)) {
log.warn("UID must not be null or empty");
return;
}
UserRecord user = FirebaseAuth.getInstance().getUser(uid);
Zasadniczo nie próbuj ponownie w przypadku błędów programowania. Umożliwianie awarii semantyka błędów programistycznych jest często najlepszym rozwiązaniem, ponieważ ujawnia błędy programowania i konfiguracji w fazie programowania, można szybko naprawić. Szybkie błędy w tym kontekście mogą oznaczać, że błędy do globalnego modułu obsługi błędów w aplikacji lub tylko logując je w celach kontroli, a następnie zakończenia bieżącego przepływu wykonywania (aplikacja nie powinna ulegać awarii). Ogólnie rzecz biorąc, postępuj zgodnie z instrukcjami obsługi sprawdzonych metod języka programowania i aplikacji, platformy. To często wystarcza, aby właściwie radzić sobie z tą klasą .
Zazwyczaj większość działań związanych z obsługą błędów skupia się na obsłudze interfejsów API . Niektóre z tych błędów można naprawić, na przykład błędy spowodowane mogą być tymczasowo niedostępne, a niektóre są oczekiwane normalny proces wykonywania programu, na przykład wykrywanie nieprawidłowych lub nieważnych tokenów tożsamości. W dalszej części tego przewodnika opisujemy, jak pakiet Admin SDK reprezentuje takie błędy w interfejsie API. i różnych opcjach obsługi tych reklam.
Struktura błędu interfejsu API
Błąd interfejsu API składa się z tych komponentów:
- Kod błędu
- Komunikat o błędzie
- Kod błędu usługi (opcjonalnie)
- Odpowiedź HTTP (opcjonalnie)
Każdy błąd interfejsu API musi zawierać kod błędu i komunikat o błędzie. Niektóre błędy interfejsu API zawierają też kod błędu usługi związany z danym interfejsem API który je wygenerował. Na przykład niektóre błędy wygenerowane przez Uwierzytelnienie Firebase Interfejs API zawiera kod błędu usługi charakterystyczny dla Uwierzytelniania Firebase. Jeśli błąd w efekcie odpowiedzi błędu HTTP otrzymanej od usługi backendu, błąd interfejsu API zawiera też odpowiednią odpowiedź HTTP. Może to służyć do sprawdzania dokładne nagłówki i treść oryginalnej odpowiedzi, co jest przydatne debugowania, logowania lub implementacji bardziej złożonej logiki obsługi błędów.
Wszystkie implementacje pakietu Admin SDK (oprócz Node.js) udostępniają interfejsy API, które umożliwiają dostęp powyższych składowych błędów interfejsu API.
Typy błędów i interfejsy API według języka
Java
W Javie wszystkie błędy interfejsu API rozszerzają klasę FirebaseException
. Aby uzyskać dostęp do
kodu błędu, komunikatu o błędzie i opcjonalnej odpowiedzi HTTP z tej klasy podstawowej.
public class FirebaseException extends Exception {
@NonNull
public ErrorCode getErrorCode() {
// ...
}
@NonNull
public String getMessage() {
// ...
}
@Nullable
public IncomingHttpResponse getHttpResponse() {
// ...
}
}
Interfejsy API, które udostępniają kody błędów usługi, zawierają specyficzne dla API podklasy
FirebaseException
Na przykład wszystkie metody publiczne w interfejsie API FirebaseAuth
są zadeklarowane do zgłaszania instancji FirebaseAuthException
. Aby uzyskać dostęp do
kodu błędu usługi z tej klasy derywowanej.
public class FirebaseAuthException extends FirebaseException {
@Nullable
public AuthErrorCode getAuthErrorCode() {
// ...
}
}
Python
W Pythonie wszystkie błędy interfejsu API rozszerzają zakres exceptions.FirebaseError
zajęcia. Możesz wyświetlić kod błędu, komunikat o błędzie i opcjonalny kod HTTP
odpowiedź z tej klasy bazowej.
class FirebaseError(Exception):
@property
def code(self):
# ...
@property
def message(self):
# ...
@property
def http_response(self):
# ...
Co więcej, pakiet Python Admin SDK oferuje oddzielne klasy derywowane dla każdego kodu błędu. Określamy je jako klasy błędów platformy.
class InvalidArgumentError(FirebaseError):
# ...
class NotFoundError(FirebaseError):
# ...
Możesz znaleźć element FirebaseError
w kodzie i sprawdzić jego code
lub
wykonaj kontrolę isinstance()
z klasą błędu platformy. Możesz też napisać
w celu bezpośredniego wychwytywania konkretnych typów błędów platformy. Drugim podejściem
co może skutkować bardziej czytelnym kodem obsługi błędów.
Interfejsy API, które udostępniają kody błędów usługi, udostępniają podklasy platformy specyficzne dla interfejsu API
klas błędów. Na przykład wszystkie metody publiczne w module auth
mogą zgłaszać
Typy błędów typowe dla interfejsu API, takie jak auth.UserNotFoundError
czy
auth.ExpiredIdTokenError
class UserNotFoundError(exceptions.NotFoundError):
# …
class ExpiredIdTokenError(exceptions.InvalidArgumentError):
# ...
Go
Pakiet SDK Go Admin udostępnia pakiet errorutils
, który zawiera serię
które umożliwiają testowanie kodów błędów.
package errorutils
func IsInvalidArgument(err error) bool {
// ...
}
func IsNotFound(err error) bool {
// ...
}
Komunikat o błędzie to po prostu ciąg znaków zwrócony przez funkcję Error()
. Dostęp do opcjonalnej odpowiedzi HTTP można uzyskać, wywołując metodę
funkcji errorutils.HTTPResponse()
, która zwraca *http.Response
.
Można bezpiecznie przekazać nil
lub jakąkolwiek inną wartość błędu do funkcji sprawdzania błędu
w pakiecie errorutils
. Zwracają wartość true
, jeśli argument wejściowy
faktycznie zawiera kwestionowany kod błędu i zwraca wartość false
dla wszystkich elementów.
. Funkcja HTTPResponse()
działa podobnie, z tą różnicą, że zwraca
nil
zamiast false
.
Interfejsy API, które udostępniają kody błędów usług, umożliwiają sprawdzanie błędów interfejsu API
w odpowiednich pakietach. Na przykład pakiet auth
udostępnia funkcje IsUserNotFound()
i IsExpiredIDTokenError()
.
.NET
W .NET wszystkie błędy interfejsu API rozszerzają zakres FirebaseException
zajęcia. Aby uzyskać dostęp do
kod błędu platformy, komunikat o błędzie i opcjonalną odpowiedź HTTP z tego
zajęcia.
public class FirebaseException : Exception {
public ErrorCode ErrorCode { get; }
public String Message { get; }
public HttpResponseMessage HttpResponse { get; }
}
Interfejsy API, które udostępniają kody błędów usługi, zawierają specyficzne dla API podklasy
FirebaseException
Na przykład wszystkie metody publiczne w interfejsie API FirebaseAuth
są zadeklarowane do zgłaszania instancji FirebaseAuthException
.
Aby uzyskać dostęp do
kodu błędu usługi z tej klasy derywowanej.
public class FirebaseAuthException : FirebaseException {
public AuthErrorCode AuthErrorCode { get; }
}
Kody błędów platformy
Kody błędów są wspólne dla wszystkich usług Firebase i Google Cloud Platform. W tabeli poniżej znajdziesz wszystkie możliwe kody błędów platformy. To jest jest stabilna i pozostanie niezmieniona przez dłuższy czas.
NIEPRAWIDŁOWY_ARG | Klient podał nieprawidłowy argument. |
FAILED_PRECONDITION | Nie można wykonać żądania w bieżącym stanie systemu, na przykład usunąć katalogu, który nie jest pusty. |
OUT_OF_RANGE | Klient podał nieprawidłowy zakres. |
BEZ UWIERZYTELNIANIA | Żądanie nie zostało uwierzytelnione z powodu braku, nieprawidłowego lub wygasłego tokena OAuth. |
PERMISSION_DENIED | Klient nie ma wystarczających uprawnień. Może się tak zdarzyć, gdy token OAuth nie ma odpowiednich zakresów, klient nie ma uprawnień lub interfejs API nie został włączony w projekcie klienta. |
NOT_FOUND | Nie znaleziono określonego zasobu lub żądanie zostało odrzucone z nieujawnionych powodów, takich jak dodanie do białej listy. |
ROZWÓJ AKCJI | Konflikt równoczesności, na przykład konflikt odczytu-modyfikacji-zapisu. Używany tylko przez kilka starszych usług. Większość usług używa zamiast tego właściwości ABORTED lub ALREADY_EXISTS. Aby sprawdzić, który z nich należy obsłużyć w kodzie, zapoznaj się z dokumentacją konkretnej usługi. |
ANULOWANO | Konflikt równoczesności, na przykład konflikt odczytu-modyfikacji-zapisu. |
JUŻ ISTNIEJE | Zasób, który próbował utworzyć klient, już istnieje. |
RESOURCE_EXHAUSTED | Limit zasobu został wyczerpany lub usługa aktywuje ograniczanie liczby żądań. |
ANULOWANO | Żądanie anulowane przez klienta. |
UTRATA_DANYCH | Nieodwracalna utrata danych lub ich uszkodzenie. Klient powinien zgłosić błąd użytkownikowi. |
BRAK INFORMACJI | Nieznany błąd serwera. Zwykle jest to błąd serwera.
Ten kod błędu jest również przypisany do lokalnych błędów analizy (unmarshal) i wielu innych niskopoziomowych błędów wejścia-wyjścia, których nie da się łatwo zdiagnozować. |
DO UŻYTKU WEWNĘTRZNEGO | Wewnętrzny błąd serwera. Zwykle jest to błąd serwera. |
DANE NIEDOSTĘPNE | Usługa niedostępna Zwykle serwer jest tymczasowo niedostępny.
Ten kod błędu jest też przypisany do błędów sieci lokalnej (odrzucenie połączenia, brak trasy do hosta). |
DEADLINE_EXCEEDED | Upłynął termin realizacji żądania. Dzieje się tak tylko wtedy, gdy osoba wywołująca ustawi termin krótszy niż domyślny termin docelowego interfejsu API (tzn. żądany termin jest niewystarczający na przetworzenie żądania przez serwer), a żądanie nie zostało ukończone w wyznaczonym terminie.
Ten kod błędu jest też przypisany do połączenia lokalnego i limitów czasu odczytu. |
Większość interfejsów API może skutkować tylko podzbiorem powyższych kodów błędów. W każdym przypadku nie powinny obsługiwać wszystkich tych kodów błędów podczas modułów obsługi błędów. Większość aplikacji byłaby zainteresowana tylko 1–2 określone kody błędów i traktuj wszystkie pozostałe kody jako ogólne, niemożliwe do naprawienia. niepowodzenie.
Kody błędów dotyczące konkretnej usługi
Uwierzytelnianie Firebase
CERTIFICATE_FETCH_FAILED | Nie udało się pobrać certyfikatów klucza publicznego wymaganych do weryfikacji tokena JWT (tokena identyfikatora lub pliku cookie sesji). |
EMAIL_ALREADY_EXISTS | Istnieje już użytkownik z podanym adresem e-mail. |
WYGAŚNIĘCIE_ID_TOKEN | Token identyfikatora określony dla verifyIdToken() stracił ważność.
|
WYGASŁ_SESJA_COOKIE | Plik cookie sesji określony na potrzeby funkcji verifySessionCookie() utracił ważność.
|
NOT_DYNAMIC_LINK_DOMAIN | Podana domena linku dynamicznego nie została skonfigurowana ani autoryzowana w bieżącym projekcie. Powiązane z interfejsami API linków do działań w e-mailach. |
NIEPRAWIDŁOWY_TOKEN | Token identyfikatora podany jako verifyIdToken() jest nieprawidłowy.
|
NIEPRAWIDŁOWA_SESJA_COOKIE | Plik cookie sesji określony jako verifySessionCookie() jest nieprawidłowy.
|
PHONE_NUMBER_ALREADY_EXISTS | Użytkownik o podanym numerze telefonu już istnieje. |
REVOKED_ID_TOKEN (REVOKED_ID_TOKEN) | Token identyfikatora określony dla verifyIdToken() został unieważniony.
|
ANULOWANO_SESJĘ_COOKIE | Plik cookie sesji określony dla verifySessionCookie() utracił ważność.
|
UNAUTHORIZED_KONTYNUUJ_URL | Domena adresu URL kontynuacji nie znajduje się na białej liście. Powiązane z interfejsami API linków do działań w e-mailach. |
NIE ZNALEZIONO UŻYTKOWNIKA | Nie znaleziono rekordu użytkownika dla podanego identyfikatora. |
Firebase Cloud Messaging
THIRD_PARTY_AUTH_ERROR | Certyfikat APNs lub klucz interfejsu Web push auth API jest nieprawidłowy lub nie ma go wcale. |
NIEPRAWIDŁOWY_ARG | Co najmniej jeden argument określony w żądaniu był nieprawidłowy. |
DO UŻYTKU WEWNĘTRZNEGO | Wewnętrzny błąd serwera. |
QUOTA_EXCEEDED (PRZEKROCZONO) | Przekroczono limit wysyłania dla celu wiadomości. |
IDENTYFIKATOR_NADAWCY_MISMATCH | Identyfikator uwierzytelnionego nadawcy różni się od identyfikatora nadawcy dla tokena rejestracji. Zwykle oznacza to, że nadawca i docelowy token rejestracji nie są w tym samym projekcie Firebase. |
DANE NIEDOSTĘPNE | Usługa Cloud Messaging jest tymczasowo niedostępna. |
NIEZAREJESTROWANE | Instancja aplikacji została wyrejestrowana z FCM. Zwykle oznacza to, że użyty token rejestracji urządzenia stracił ważność i trzeba użyć nowego. |
Automatyczne ponowne próby
Pakiet Admin SDK automatycznie ponawia określone błędy przed ich ujawnieniem użytkownikom. Zwykle w przejrzysty sposób powtarzamy te typy błędów:
- Wszystkie błędy interfejsu API wynikające z odpowiedzi HTTP 503 (usługa niedostępna).
- Niektóre błędy interfejsu API wynikające z odpowiedzi HTTP 500 (wewnętrzny błąd serwera).
- Większość błędów wejścia-wyjścia niskiego poziomu (odrzucenie połączenia, zresetowanie połączenia itp.).
Pakiet SDK ponów próbę każdego z powyższych błędów maksymalnie 5 razy (pierwotna próba + 4 ponowne próby) ze wzrastającym czasem do ponowienia. Możesz wdrożyć własną ponowienie próby na poziomie aplikacji, ale zwykle nie jest to możliwe.
Pomoc „Ponów próbę po”
Implementacje w języku Go i .NET pakietu Admin SDK obsługują
z nagłówkiem HTTP Retry-After
. Oznacza to, że jeśli odpowiedź o błędzie wysłana przez
że serwery backendu zawierają standardowy nagłówek Retry-After
,
przy ponawianiu próby, o ile podany czas oczekiwania nie jest zbyt długi
. Jeśli nagłówek Retry-After
wskazuje bardzo długi czas oczekiwania, SDK
pominie ponowne próby i zgłosi odpowiedni błąd interfejsu API.
pakiet Python Admin SDK nie obsługuje obecnie nagłówka Retry-After
,
obsługuje tylko prosty wykładniczy czas do ponowienia.
Przykłady obsługi błędów interfejsu API
Wdrażanie ogólnego modułu obsługi błędów
Zwykle potrzebny jest ogólny moduł obsługi błędów, który śledzi błędów, aby zapobiec nieoczekiwanemu przerwaniu przepływu programu na skutek Błąd interfejsu API. Tego typu moduły obsługi błędów zwykle rejestrują błędy na potrzeby kontroli, lub wywołać inną domyślną procedurę obsługi błędów dla wszystkich napotkanych interfejsów API . Nie muszą być zainteresowani różnymi kodami błędów ani które mogą być jego przyczyną.
Java
try {
FirebaseToken token = FirebaseAuth.getInstance().verifyIdToken(idToken);
performPrivilegedOperation(token.getUid());
} catch (FirebaseAuthException ex) {
System.err.println("Failed to verify ID token: " + ex.getMessage());
}
Python
try:
token = auth.verify_id_token(idToken)
perform_privileged_pperation(token.uid)
except exceptions.FirebaseError as ex:
print(f'Failed to verify ID token: {ex}')
Go
token, err := client.VerifyIDToken(ctx, idToken)
if err != nil {
log.Printf("Failed to verify ID token: %v", err)
return
}
performPrivilegedOperation(token)
.Net
try
{
var token = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(idToken);
PerformPrivilegedOperation(token.getUid());
}
catch (FirebaseAuthException ex)
{
Conole.WriteLine($"Failed to verify ID token: {ex.Message}");
}
Sprawdzanie kodów błędów
W niektórych przypadkach warto sprawdzić dokładne kody błędów i wywołać i różnych procedur obsługi błędów zależnych od kontekstu. W poniższym przykładzie ma moduł obsługi błędów, który rejestruje bardziej szczegółowe komunikaty o błędach przez kod błędu usługi.
Java
try {
FirebaseToken token = FirebaseAuth.getInstance().verifyIdToken(idToken);
performPrivilegedOperation(token.getUid());
} catch (FirebaseAuthException ex) {
if (ex.getAuthErrorCode() == AuthErrorCode.ID_TOKEN_EXPIRED) {
System.err.println("ID token has expired");
} else if (ex.getAuthErrorCode() == AuthErrorCode.ID_TOKEN_INVALID) {
System.err.println("ID token is malformed or invalid");
} else {
System.err.println("Failed to verify ID token: " + ex.getMessage());
}
}
Python
try:
token = auth.verify_id_token(idToken)
perform_privileged_operation(token.uid)
except auth.ExpiredIdTokenError:
print('ID token has expired')
except auth.InvalidIdTokenError:
print('ID token is malformed or invalid')
except exceptions.FirebaseError as ex:
print(f'Failed to verify ID token: {ex}')
Go
token, err := client.VerifyIDToken(ctx, idToken)
if auth.IsIDTokenExpired(err) {
log.Print("ID token has expired")
return
}
if auth.IsIDTokenInvalid(err) {
log.Print("ID token is malformed or invalid")
return
}
if err != nil {
log.Printf("Failed to verify ID token: %v", err)
return
}
performPrivilegedOperation(token)
.Net
try
{
var token = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(idToken);
PerformPrivilegedOperation(token.getUid());
}
catch (FirebaseAuthException ex)
{
if (ex.AuthErrorCode == AuthErrorCode.ExpiredIdToken)
{
Console.WriteLine("ID token has expired");
}
else if (ex.AuthErrorCode == AuthErrorCode.InvalidIdToken)
{
Console.WriteLine("ID token is malformed or invalid");
}
else
{
Conole.WriteLine($"Failed to verify ID token: {ex.Message}");
}
}
Oto inny przykład, w którym sprawdzamy kody błędów zarówno najwyższego poziomu, jak i usługi:
Java
try {
FirebaseMessaging.getInstance().send(createMyMessage());
} catch (FirebaseMessagingException ex){
if (ex.getMessagingErrorCode() == MessagingErrorCode.UNREGISTERED) {
System.err.println("App instance has been unregistered");
removeTokenFromDatabase();
} else if (ex.getErrorCode() == ErrorCode.Unavailable) {
System.err.println("FCM service is temporarily unavailable");
scheduleForRetryInAnHour();
} else {
System.err.println("Failed to send notification: " + ex.getMessage());
}
}
Python
try:
messaging.send(create_my_message())
except messaging.UnregisteredError:
print('App instance has been unregistered')
remove_token_from_database()
except exceptions.UnavailableError:
print('FCM service is temporarily unavailable')
schedule_for_retry_in_an_hour()
except exceptions.FirebaseError as ex:
print(f'Failed to send notification: {ex}')
Go
_, err := client.Send(ctx, createMyMessage())
if messaging.IsUnregistered(err) {
log.Print("App instance has been unregistered")
removeTokenFromDatabase()
return
}
if errorutils.IsUnavailable(err) {
log.Print("FCM service is temporarily unavailable")
scheduleForRetryInAnHour()
return
}
if err != nil {
log.Printf("Failed to send notification: %v", err)
return
}
.Net
try
{
await FirebaseMessaging.DefaultInstance.SendAsync(createMyMessage());
}
catch (FirebaseMessagingException ex)
{
if (ex.MessagingErrorCode == MessagingErrorCode.UNREGISTERED)
{
Console.WriteLine("App instance has been unregistered");
removeTokenFromDatabase();
}
else if (ex.ErrorCode == ErrorCode.Unavailable)
{
Console.WriteLine("FCM service is temporarily unavailable");
scheduleForRetryInAnHour();
}
else
{
Console.WriteLine($"Failed to send notification: {ex.Message}");
}
}
Dostęp do odpowiedzi HTTP
W rzadkich przypadkach może być wskazane sprawdzenie odpowiedzi błędu HTTP zwróconej przez i wykonać na niej działanie z obsługą błędów. Pakiet Admin SDK ujawnia zarówno nagłówki, jak i zawartość tych odpowiedzi na błędy. Odpowiedź zawartość jest zwykle zwracana w postaci ciągu lub nieprzetworzonej sekwencji bajtowej i może być analizowana na dowolny wymagany format docelowy.
Java
try {
FirebaseMessaging.getInstance().send(createMyMessage());
} catch (FirebaseMessagingException ex){
IncomingHttpResponse response = ex.getHttpResponse();
if (response != null) {
System.err.println("FCM service responded with HTTP " + response.getStatusCode());
Map<String, Object> headers = response.getHeaders();
for (Map.Entry<String, Object> entry : headers.entrySet()) {
System.err.println(">>> " + entry.getKey() + ": " + entry.getValue());
}
System.err.println(">>>");
System.err.println(">>> " + response.getContent());
}
}
Python
try:
messaging.send(create_my_message())
except exceptions.FirebaseError as ex:
response = ex.http_response
if response is not None:
print(f'FCM service responded with HTTP {response.status_code}')
for key, value in response.headers.items():
print(f'>>> {key}: {value}')
print('>>>')
print(f'>>> {response.content}')
Go
_, err := client.Send(ctx, createMyMessage())
if resp := errorutils.HTTPResponse(err); resp != nil {
log.Printf("FCM service responded with HTTP %d", resp.StatusCode)
for key, value := range resp.Header {
log.Printf(">>> %s: %v", key, value)
}
defer resp.Body.Close()
b, _ := ioutil.ReadAll(resp.Body)
log.Print(">>>")
log.Printf(">>> %s", string(b))
return
}
.Net
try
{
await FirebaseMessaging.DefaultInstance.SendAsync(createMyMessage());
}
catch (FirebaseMessagingException ex)
{
var response = ex.HttpResponse
if response != null
{
Console.WriteLine($"FCM service responded with HTTP { response.StatusCode}");
var headers = response.Headers;
for (var entry in response.Headers)
{
Console.WriteLine($">>> {entry.Key}: {entry.Value}");
}
var body = await response.Content.ReadAsString();
Console.WriteLine(">>>");
Console.WriteLine($">>> {body}");
}
}