Fehlerbehandlung im Admin-SDK

Admin-SDK-Fehler sind in zwei Kategorien unterteilt:

  1. Programmierfehler: Hierbei handelt es sich um Programmier- und Konfigurationsfehler in der Benutzeranwendung. Sie treten meist aufgrund der falschen Verwendung des SDK (z. B. Übergabe null an eine Methode, die keine null akzeptiert) und anderen Konfigurationsfehlern auf Firebase-Projekt- oder SDK-Ebene (fehlende Anmeldeinformationen, falsche Projekt-ID-Zeichenfolge usw.) auf An).
  2. API-Fehler: Dazu gehören verschiedene behebbare Fehler, die innerhalb der SDK-Implementierung auftreten, alle Fehler, die von Firebase-Backend-Diensten ausgehen, und andere vorübergehende Fehler (z. B. Zeitüberschreitungen), die bei RPC-Aufrufen auftreten können.

Das Admin SDK signalisiert Programmierfehler , indem es einen Fehler auslöst, der für die betreffende Plattform typisch ist.

  • Java: Löst Instanzen von IllegalArgumentException , NullPointerException oder einem ähnlichen integrierten Laufzeitfehlertyp aus.
  • Python: Löst Instanzen von ValueError , TypeError oder einem anderen integrierten Fehlertyp aus.
  • Go: Gibt einen allgemeinen Fehler zurück.
  • .NET: Löst Instanzen von ArgumentException , ArgumentNullException oder einem ähnlichen integrierten Fehlertyp aus.

In den meisten Situationen sollten Sie Programmierfehler nicht explizit behandeln. Stattdessen sollten Sie Ihren Code und Ihre Konfiguration korrigieren, um Programmierfehler insgesamt zu vermeiden. Betrachten Sie den folgenden Java-Snippet:

String uid = getUserInput();

UserRecord user = FirebaseAuth.getInstance().getUser(uid);

Wenn die getUserInput() -Methode null oder leere Zeichenfolgen zurückgibt, löst die FirebaseAuth.getUser() API eine IllegalArgumentException aus. Anstatt es explizit zu behandeln, können Sie das Problem entschärfen, indem Sie sicherstellen, dass die getUserInput() Methode niemals eine ungültige UID-Zeichenfolge zurückgibt. Wenn dies nicht möglich ist, implementieren Sie die erforderliche Argumentüberprüfung wie folgt in Ihrem eigenen Code:

String uid = getUserInput();
if (Strings.isNullOrEmpty(uid)) {
    log.warn("UID must not be null or empty");
    return;
}

UserRecord user = FirebaseAuth.getInstance().getUser(uid);

Versuchen Sie es grundsätzlich nie erneut bei Programmierfehlern. Die Berücksichtigung einer Fail-Fast-Semantik bei Programmierfehlern ist häufig die beste Vorgehensweise, da dadurch Programmierfehler und Konfigurationsfehler während der Entwicklung aufgedeckt werden, wo sie umgehend behoben werden können. Fail-Fast kann in diesem Zusammenhang bedeuten, dass die Fehler an einen globalen Fehlerhandler in Ihrer Anwendung weitergegeben werden oder sie einfach zu Prüfzwecken protokolliert werden, gefolgt von der Beendigung des aktuellen Ausführungsflusses (die Anwendung sollte nicht abstürzen müssen). Befolgen Sie im Allgemeinen die Best Practices für die Fehlerbehandlung Ihrer Programmiersprache und des Anwendungsframeworks. Dies allein reicht oft aus, um diese Fehlerklasse richtig zu behandeln.

Normalerweise konzentriert sich der Großteil Ihrer Fehlerbehandlungsbemühungen auf die Behandlung von API-Fehlern . Einige dieser Fehler sind behebbar, beispielsweise Fehler, die aus einem vorübergehend nicht verfügbaren Dienst resultieren, und einige werden sogar während des normalen Programmausführungsablaufs erwartet, beispielsweise beim Erkennen ungültiger oder abgelaufener ID-Token. Im Rest dieses Leitfadens wird beschrieben, wie das Admin SDK solche API-Fehler darstellt und welche verschiedenen Optionen zur Behandlung dieser Fehler verfügbar sind.

Struktur eines API-Fehlers

Ein API-Fehler besteht aus folgenden Komponenten:

  1. Fehlercode
  2. Fehlermeldung
  3. Dienstfehlercode (optional)
  4. HTTP-Antwort (optional)

Jeder API-Fehler enthält garantiert einen Fehlercode und eine Fehlermeldung. Bestimmte API-Fehler enthalten auch einen Dienstfehlercode, der spezifisch für die API ist, die den Fehler generiert hat. Beispielsweise enthalten einige von der Firebase Auth-API generierte Fehler einen Dienstfehlercode, der spezifisch für Firebase Auth ist. Wenn der Fehler das Ergebnis einer HTTP-Fehlerantwort eines Backend-Dienstes war, enthält der API-Fehler auch die entsprechende HTTP-Antwort. Dies kann verwendet werden, um die genauen Header und Inhalte der ursprünglichen Antwort zu überprüfen, was zum Debuggen, Protokollieren oder Implementieren einer ausgefeilteren Fehlerbehandlungslogik nützlich ist.

Alle Admin SDK-Implementierungen außer Node.js stellen APIs bereit, die den Zugriff auf die oben genannten Komponenten von API-Fehlern ermöglichen.

Fehlertypen und APIs nach Sprache

Java

In Java erweitern alle API-Fehler die FirebaseException Klasse. Von dieser Basisklasse aus können Sie auf den Fehlercode, die Fehlermeldung und die optionale HTTP-Antwort zugreifen.

public class FirebaseException extends Exception {
    @NonNull
    public ErrorCode getErrorCode() {
        // ...
    }

    @NonNull
    public String getMessage() {
        // ...
    }

    @Nullable
    public IncomingHttpResponse getHttpResponse() {
        // ...
    }
}

APIs, die Dienstfehlercodes offenlegen, stellen API-spezifische Unterklassen von FirebaseException bereit. Beispielsweise sind alle öffentlichen Methoden in der FirebaseAuth API so deklariert, dass sie Instanzen von FirebaseAuthException auslösen. Sie können über diese abgeleitete Klasse auf den Dienstfehlercode zugreifen.

public class FirebaseAuthException extends FirebaseException {
    @Nullable
    public AuthErrorCode getAuthErrorCode() {
        // ...
    }
}

Python

In Python erweitern alle API-Fehler die Klasse exceptions.FirebaseError . Von dieser Basisklasse aus können Sie auf den Fehlercode, die Fehlermeldung und die optionale HTTP-Antwort zugreifen.

class FirebaseError(Exception):
    @property
    def code(self):
          # ...

    @property
    def message(self):
          # ...

    @property
    def http_response(self):
          # ...

Darüber hinaus bietet das Python Admin SDK für jeden Fehlercode separate abgeleitete Klassen. Wir bezeichnen sie als Plattformfehlerklassen .

class InvalidArgumentError(FirebaseError):
    # ...

class NotFoundError(FirebaseError):
    # ...

Sie können entweder FirebaseError in Ihrem Code abfangen und seinen code überprüfen oder eine isinstance() Prüfung anhand einer Plattformfehlerklasse durchführen. Oder Sie können Code schreiben, um bestimmte Plattformfehlertypen direkt abzufangen. Der letztere Ansatz führt wahrscheinlich zu besser lesbarem Fehlerbehandlungscode.

APIs, die Dienstfehlercodes offenlegen, stellen API-spezifische Unterklassen von Plattformfehlerklassen bereit. Beispielsweise können alle öffentlichen Methoden im auth Modul API-spezifische Fehlertypen wie auth.UserNotFoundError und auth.ExpiredIdTokenError auslösen.

class UserNotFoundError(exceptions.NotFoundError):
    # …

class ExpiredIdTokenError(exceptions.InvalidArgumentError):
    # ...

Gehen

Das Go Admin SDK stellt ein errorutils Paket bereit, das eine Reihe von Funktionen enthält, die das Testen auf Fehlercodes ermöglichen.

package errorutils

func IsInvalidArgument(err error) bool {
    // ...
}

func IsNotFound(err error) bool {
    // ...
}

Die Fehlermeldung ist einfach die Zeichenfolge, die von der Error() -Funktion eines Fehlers zurückgegeben wird. Auf die optionale HTTP-Antwort kann durch Aufrufen der Funktion errorutils.HTTPResponse() zugegriffen werden, die ein *http.Response zurückgibt.

Es ist sicher, nil oder einen anderen Fehlerwert an die Fehlerprüffunktionen im Paket errorutils zu übergeben. Sie geben true zurück, wenn das Eingabeargument tatsächlich den betreffenden Fehlercode enthält, und geben für alles andere false zurück. Die Funktion HTTPResponse() verhält sich ähnlich, außer dass sie nil anstelle von false zurückgibt.

APIs, die Dienstfehlercodes offenlegen, stellen API-spezifische Fehlerprüffunktionen in den entsprechenden Paketen bereit. Das auth Paket stellt beispielsweise die Funktionen IsUserNotFound() und IsExpiredIDTokenError() bereit.

.NETZ

In .NET erweitern alle API-Fehler die FirebaseException Klasse. Von dieser Basisklasse aus können Sie auf den Plattformfehlercode, die Fehlermeldung und die optionale HTTP-Antwort zugreifen.

public class FirebaseException : Exception {

    public ErrorCode ErrorCode { get; }

    public String Message { get; }

    public HttpResponseMessage HttpResponse { get; }
}

APIs, die Dienstfehlercodes offenlegen, stellen API-spezifische Unterklassen von FirebaseException bereit. Beispielsweise sind alle öffentlichen Methoden in der FirebaseAuth API so deklariert, dass sie Instanzen von FirebaseAuthException auslösen. Sie können über diese abgeleitete Klasse auf den Dienstfehlercode zugreifen.

public class FirebaseAuthException : FirebaseException {

    public AuthErrorCode AuthErrorCode { get; }
}

Plattformfehlercodes

Fehlercodes sind bei allen Firebase- und Google Cloud Platform-Diensten gleich. In der folgenden Tabelle sind alle möglichen Plattformfehlercodes aufgeführt. Dies ist eine stabile Liste und wird voraussichtlich über einen langen Zeitraum unverändert bleiben.

UNGÜLTIGES ARGUMENT Der Client hat ein ungültiges Argument angegeben.
FAILED_PRECONDITION Die Anforderung kann im aktuellen Systemstatus nicht ausgeführt werden, z. B. beim Löschen eines nicht leeren Verzeichnisses.
AUSSER REICHWEITE Der Client hat einen ungültigen Bereich angegeben.
UNAUTHENTIFIZIERT Die Anfrage wurde aufgrund eines fehlenden, ungültigen oder abgelaufenen OAuth-Tokens nicht authentifiziert.
ZUGRIFF VERWEIGERT Der Kunde verfügt nicht über ausreichende Berechtigungen. Dies kann passieren, weil das OAuth-Token nicht über die richtigen Bereiche verfügt, der Client nicht über die entsprechende Berechtigung verfügt oder die API für das Clientprojekt nicht aktiviert wurde.
NICHT GEFUNDEN Die angegebene Ressource wurde nicht gefunden oder die Anfrage wurde aus unbekannten Gründen wie Whitelisting abgelehnt.
KONFLIKT Parallelitätskonflikt, z. B. Lese-, Änderungs- und Schreibkonflikt. Wird nur von einigen älteren Diensten verwendet. Die meisten Dienste verwenden stattdessen ABORTED oder ALREADY_EXISTS. Sehen Sie in der dienstspezifischen Dokumentation nach, welche davon in Ihrem Code behandelt werden soll.
ABGEBROCHEN Parallelitätskonflikt, z. B. Lese-, Änderungs- und Schreibkonflikt.
IST BEREITS VORHANDEN Die Ressource, die ein Client zu erstellen versuchte, ist bereits vorhanden.
RESSOURCE_ERSCHÖPFT Entweder ist das Ressourcenkontingent erschöpft oder die Ratenbegrenzung ist erreicht.
ABGESAGT Anfrage vom Kunden storniert.
DATENVERLUST Nicht behebbarer Datenverlust oder Datenbeschädigung. Der Client sollte den Fehler dem Benutzer melden.
UNBEKANNT Unbekannter Serverfehler. Typischerweise ein Serverfehler.

Dieser Fehlercode wird auch Fehlern beim Parsen lokaler Antworten (Unmarshalieren) und einer Vielzahl anderer E/A-Fehler auf niedriger Ebene zugewiesen, die nicht leicht diagnostizierbar sind.

INTERN Interner Serverfehler. Typischerweise ein Serverfehler.
NICHT VERFÜGBAR Dienst nicht verfügbar. Normalerweise ist der Server vorübergehend ausgefallen.

Dieser Fehlercode wird auch lokalen Netzwerkfehlern zugeordnet (Verbindung verweigert, keine Route zum Host).

DEADLINE_EXCEEDED Antragsfrist überschritten. Dies geschieht nur, wenn der Aufrufer eine Frist festlegt, die kürzer als die Standardfrist der Ziel-API ist (dh die angeforderte Frist reicht nicht aus, damit der Server die Anforderung verarbeiten kann) und die Anforderung nicht innerhalb der Frist abgeschlossen wurde.

Dieser Fehlercode wird auch lokalen Verbindungs- und Lese-Timeouts zugewiesen.

Die meisten APIs können nur zu einer Teilmenge der oben genannten Fehlercodes führen. In jedem Fall wird von Ihnen nicht erwartet, dass Sie alle diese Fehlercodes bei der Implementierung Ihrer Fehlerhandler explizit behandeln. Die meisten Anwendungen sind nur an ein bis zwei spezifischen Fehlercodes interessiert und behandeln alles andere als allgemeinen, nicht behebbaren Fehler.

Dienstspezifische Fehlercodes

Firebase-Auth

CERTIFICATE_FETCH_FAILED Es konnten keine öffentlichen Schlüsselzertifikate abgerufen werden, die zur Überprüfung eines JWT (ID-Token oder Sitzungscookie) erforderlich sind.
E-MAIL EXISTIERT BEREITS Es existiert bereits ein Benutzer mit der angegebenen E-Mail-Adresse.
EXPIRED_ID_TOKEN Das für verifyIdToken() angegebene ID-Token ist abgelaufen.
EXPIRED_SESSION_COOKIE Das für verifySessionCookie() angegebene Sitzungscookie ist abgelaufen.
INVALID_DYNAMIC_LINK_DOMAIN Die bereitgestellte Dynamic-Link-Domäne ist für das aktuelle Projekt nicht konfiguriert oder autorisiert. Bezogen auf E-Mail-Aktionslink-APIs.
INVALID_ID_TOKEN Das für verifyIdToken() angegebene ID-Token ist ungültig.
INVALID_SESSION_COOKIE Das für verifySessionCookie() angegebene Sitzungscookie ist ungültig.
PHONE_NUMBER_ALREADY_EXISTS Es existiert bereits ein Benutzer mit der angegebenen Telefonnummer.
REVOKED_ID_TOKEN Das für verifyIdToken() angegebene ID-Token wird widerrufen.
REVOKED_SESSION_COOKIE Das für verifySessionCookie() angegebene Sitzungscookie ist abgelaufen.
UNAUTHORIZED_CONTINUE_URL Die Domäne der Fortsetzungs-URL steht nicht auf der Whitelist. Bezogen auf E-Mail-Aktionslink-APIs.
BENUTZER NICHT GEFUNDEN Für die angegebene Kennung wurde kein Benutzerdatensatz gefunden.

Firebase Cloud-Messaging

THIRD_PARTY_AUTH_ERROR Das APNs-Zertifikat oder der Web-Push-Authentifizierungs-API-Schlüssel war ungültig oder fehlte.
UNGÜLTIGES ARGUMENT Ein oder mehrere in der Anfrage angegebene Argumente waren ungültig.
INTERN Interner Serverfehler.
QUOTE ÜBERSCHRITTEN Das Sendelimit für das Nachrichtenziel wurde überschritten.
SENDER_ID_MISMATCH Die authentifizierte Absender-ID unterscheidet sich von der Absender-ID für das Registrierungstoken. Dies bedeutet normalerweise, dass sich der Absender und das Zielregistrierungstoken nicht im selben Firebase-Projekt befinden.
NICHT VERFÜGBAR Der Cloud Messaging-Dienst ist vorübergehend nicht verfügbar.
NICHT REGISTRIERT Die App-Instanz wurde von FCM abgemeldet. Dies bedeutet in der Regel, dass der verwendete Geräteregistrierungstoken nicht mehr gültig ist und ein neuer verwendet werden muss.

Automatische Wiederholungsversuche

Das Admin SDK wiederholt automatisch bestimmte Fehler, bevor diese Fehler den Benutzern angezeigt werden. Im Allgemeinen werden die folgenden Arten von Fehlern transparent wiederholt:

  • Alle API-Fehler, die aus HTTP 503-Antworten (Dienst nicht verfügbar) resultieren.
  • Einige API-Fehler resultieren aus HTTP 500-Antworten (Internal Server Error).
  • Die meisten E/A-Fehler auf niedriger Ebene (Verbindung abgelehnt, Verbindung zurückgesetzt usw.).

Das SDK wiederholt jeden der oben genannten Fehler bis zu fünf Mal (ursprünglicher Versuch + vier Wiederholungsversuche) mit exponentiellem Backoff. Sie können bei Bedarf Ihre eigenen Wiederholungsmechanismen auf Anwendungsebene implementieren, dies ist jedoch normalerweise nicht erforderlich.

Retry-After-Unterstützung

Die Go- und .NET-Implementierungen des Admin SDK unterstützen die Verarbeitung des HTTP- Retry-After Headers. Das heißt, wenn die von den Back-End-Servern gesendete Fehlerantwort den standardmäßigen Retry-After -Header enthält, berücksichtigt das SDK diesen beim erneuten Versuch, solange die angegebene Wartezeit nicht sehr lang ist. Wenn der Retry-After Header eine sehr lange Wartezeit anzeigt, umgeht das SDK Wiederholungsversuche und löst den entsprechenden API-Fehler aus.

Das Python Admin SDK unterstützt derzeit nicht den Retry-After Header und unterstützt nur einfaches exponentielles Backoff.

Beispiele für die API-Fehlerbehandlung

Implementierung eines generischen Fehlerhandlers

In den meisten Fällen benötigen Sie einen generischen Fehlerhandler, der ein breites Spektrum an Fehlern abfängt, um einen unerwarteten Abbruch des Programmablaufs aufgrund eines API-Fehlers zu verhindern. Solche Fehlerhandler protokollieren die Fehler normalerweise nur zu Prüfzwecken oder rufen eine andere Standardroutine zur Fehlerbehandlung für alle aufgetretenen API-Fehler auf. Sie interessieren sich nicht unbedingt für die unterschiedlichen Fehlercodes oder die Gründe, die den Fehler verursacht haben könnten.

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}')

Gehen

token, err := client.VerifyIDToken(ctx, idToken)
if err != nil {
  log.Printf("Failed to verify ID token: %v", err)
  return
}

performPrivilegedOperation(token)

.Netz

try
{
  var token = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(idToken);
  PerformPrivilegedOperation(token.getUid());
}
catch (FirebaseAuthException ex) 
{
  Conole.WriteLine($"Failed to verify ID token: {ex.Message}");
}

Fehlercodes prüfen

In manchen Fällen möchten Sie die genauen Fehlercodes überprüfen und verschiedene kontextbezogene Fehlerbehandlungsroutinen aufrufen. Im folgenden Beispiel haben wir einen Fehlerhandler, der spezifischere Fehlermeldungen basierend auf dem Dienstfehlercode protokolliert.

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}')

Gehen

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)

.Netz

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}");
  }
}

Hier ist ein weiteres Beispiel, bei dem wir sowohl nach Fehlercodes der obersten Ebene als auch nach Dienstfehlercodes suchen:

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}')

Gehen

_, 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
}

.Netz

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}");
  }
}

Zugriff auf die HTTP-Antwort

In einigen seltenen Fällen möchten Sie möglicherweise die von einem Back-End-Dienst zurückgegebene HTTP-Fehlerantwort überprüfen und eine Fehlerbehandlungsaktion daran durchführen. Das Admin SDK stellt sowohl die Header als auch den Inhalt dieser Fehlerantworten bereit. Der Antwortinhalt wird normalerweise als Zeichenfolge oder rohe Bytesequenz zurückgegeben und kann in jedes erforderliche Zielformat geparst werden.

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}')

Gehen

_, 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
}

.Netz

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}");
  }
}