Obsługa błędów pakietu Admin SDK

Błędy pakietu Admin SDK są podzielone na 2 kategorie:

  1. 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ści null, 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.).
  2. 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:

  1. Kod błędu
  2. Komunikat o błędzie
  3. Kod błędu usługi (opcjonalnie)
  4. 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}");
  }
}