Les erreurs du SDK Admin sont divisées en deux catégories :
- Erreurs de programmation : Il s'agit d'erreurs de programmation et de configuration dans l'application utilisateur. Ils se produisent principalement en raison d'une utilisation incorrecte du SDK (comme le passage
null
à une méthode qui n'accepte pas les valeursnull
) et d'autres erreurs de configuration au niveau du projet Firebase ou du SDK (informations d'identification manquantes, chaîne d'ID de projet incorrecte, etc. sur). - Erreurs d'API : celles-ci incluent diverses erreurs récupérables qui se produisent dans l'implémentation du SDK, toutes les erreurs provenant des services backend Firebase et d'autres erreurs transitoires (telles que des délais d'attente) qui peuvent survenir lors des appels RPC.
Le SDK Admin signale les erreurs de programmation en lançant une erreur native de la plate-forme en question.
- Java : génère des instances de
IllegalArgumentException
,NullPointerException
ou un type d'erreur d'exécution intégré similaire. - Python : génère des instances de
ValueError
,TypeError
ou un autre type d'erreur intégré. - Go : renvoie une erreur générique.
- .NET : génère des instances de
ArgumentException
,ArgumentNullException
ou un type d'erreur intégré similaire.
Dans la plupart des situations, vous ne devez pas gérer explicitement les erreurs de programmation. Au lieu de cela, vous devez corriger votre code et votre configuration pour éviter complètement les erreurs de programmation. Considérez l'extrait Java suivant :
String uid = getUserInput();
UserRecord user = FirebaseAuth.getInstance().getUser(uid);
Si la méthode getUserInput()
renvoie des chaînes null
ou vides, l'API FirebaseAuth.getUser()
renvoie une IllegalArgumentException
. Au lieu de le gérer explicitement, vous pouvez atténuer le problème en vous assurant que la méthode getUserInput()
ne renvoie jamais une chaîne UID non valide. Si ce n'est pas possible, implémentez la vérification des arguments nécessaire dans votre propre code comme suit :
String uid = getUserInput();
if (Strings.isNullOrEmpty(uid)) {
log.warn("UID must not be null or empty");
return;
}
UserRecord user = FirebaseAuth.getInstance().getUser(uid);
Par principe, ne réessayez jamais sur des erreurs de programmation. Autoriser une sémantique rapide sur les erreurs de programmation est souvent la meilleure solution car elle expose les bogues de programmation et les erreurs de configuration pendant le développement, où ils peuvent être rapidement corrigés. Dans ce contexte, l'échec rapide peut signifier laisser les erreurs se propager à un gestionnaire d'erreurs global dans votre application, ou simplement les enregistrer à des fins d'audit, suivi de la fin du flux d'exécution en cours (l'application ne devrait pas avoir à planter). En général, suivez les meilleures pratiques de gestion des erreurs de votre langage de programmation et du framework d'application. Cela suffit souvent à traiter correctement cette classe d’erreurs.
En règle générale, l'essentiel de vos efforts de gestion des erreurs se concentrera sur la gestion des erreurs d'API . Certaines de ces erreurs sont récupérables, comme les erreurs résultant d'un service temporairement indisponible, et certaines sont même anticipées pendant le flux normal d'exécution du programme, comme la détection de jetons d'identification invalides ou expirés. Le reste de ce guide décrit comment le SDK Admin représente ces erreurs d'API et les différentes options disponibles pour les gérer.
Structure d'une erreur API
Une erreur API se compose des éléments suivants :
- Code d'erreur
- Message d'erreur
- Code d'erreur de service (facultatif)
- Réponse HTTP (facultatif)
Chaque erreur API est garantie de contenir un code d'erreur et un message d'erreur. Certaines erreurs d'API contiennent également un code d'erreur de service spécifique à l'API qui a généré l'erreur. Par exemple, certaines erreurs générées par l'API Firebase Auth contiennent un code d'erreur de service spécifique à Firebase Auth. Si l'erreur était le résultat d'une réponse d'erreur HTTP provenant d'un service backend, l'erreur API contient également la réponse HTTP correspondante. Cela peut être utilisé pour inspecter les en-têtes et le contenu exacts de la réponse d'origine, ce qui est utile pour le débogage, la journalisation ou la mise en œuvre d'une logique de gestion des erreurs plus sophistiquée.
Toutes les implémentations du SDK Admin, à l'exception de Node.js, fournissent des API qui permettent d'accéder aux composants ci-dessus des erreurs d'API.
Types d'erreurs et API par langue
Java
En Java, toutes les erreurs d'API étendent la classe FirebaseException
. Vous pouvez accéder au code d'erreur, au message d'erreur et à la réponse HTTP facultative à partir de cette classe de base.
public class FirebaseException extends Exception {
@NonNull
public ErrorCode getErrorCode() {
// ...
}
@NonNull
public String getMessage() {
// ...
}
@Nullable
public IncomingHttpResponse getHttpResponse() {
// ...
}
}
Les API qui exposent les codes d'erreur de service fournissent des sous-classes de FirebaseException
spécifiques aux API. Par exemple, toutes les méthodes publiques de l'API FirebaseAuth
sont déclarées pour lancer des instances de FirebaseAuthException
. Vous pouvez accéder au code d'erreur du service à partir de cette classe dérivée.
public class FirebaseAuthException extends FirebaseException {
@Nullable
public AuthErrorCode getAuthErrorCode() {
// ...
}
}
Python
En Python, toutes les erreurs d'API étendent la classe exceptions.FirebaseError
. Vous pouvez accéder au code d'erreur, au message d'erreur et à la réponse HTTP facultative à partir de cette classe de base.
class FirebaseError(Exception):
@property
def code(self):
# ...
@property
def message(self):
# ...
@property
def http_response(self):
# ...
De plus, Python Admin SDK propose des classes dérivées distinctes pour chaque code d'erreur. Nous les appelons classes d'erreurs de plate-forme .
class InvalidArgumentError(FirebaseError):
# ...
class NotFoundError(FirebaseError):
# ...
Vous pouvez soit intercepter FirebaseError
dans votre code et vérifier son code
, soit effectuer une vérification isinstance()
par rapport à une classe d'erreur de plate-forme. Vous pouvez également écrire du code pour détecter directement des types d’erreurs spécifiques à la plate-forme. Cette dernière approche est susceptible d'aboutir à un code de gestion des erreurs plus lisible.
Les API qui exposent les codes d'erreur de service fournissent des sous-classes spécifiques aux API des classes d'erreur de plateforme. Par exemple, toutes les méthodes publiques du module auth
peuvent générer des types d'erreur spécifiques à l'API tels que auth.UserNotFoundError
et auth.ExpiredIdTokenError
.
class UserNotFoundError(exceptions.NotFoundError):
# …
class ExpiredIdTokenError(exceptions.InvalidArgumentError):
# ...
Aller
Le SDK Go Admin fournit un package errorutils
qui contient une série de fonctions permettant de tester les codes d'erreur.
package errorutils
func IsInvalidArgument(err error) bool {
// ...
}
func IsNotFound(err error) bool {
// ...
}
Le message d'erreur est simplement la chaîne renvoyée par la fonction Error()
d'une erreur. La réponse HTTP facultative est accessible en appelant la fonction errorutils.HTTPResponse()
, qui renvoie un *http.Response
.
Il est prudent de transmettre nil
ou toute autre valeur d’erreur aux fonctions de vérification des erreurs du package errorutils
. Ils renvoient true
si l'argument d'entrée contient réellement le code d'erreur en question et renvoient false
pour tout le reste. La fonction HTTPResponse()
a un comportement similaire, sauf qu'elle renvoie nil
au lieu de false
.
Les API qui exposent les codes d'erreur de service fournissent des fonctions de vérification des erreurs spécifiques à l'API dans les packages correspondants. Par exemple, le package auth
fournit les fonctions IsUserNotFound()
et IsExpiredIDTokenError()
.
.FILET
Dans .NET, toutes les erreurs d'API étendent la classe FirebaseException
. Vous pouvez accéder au code d'erreur de la plateforme, au message d'erreur et à la réponse HTTP facultative à partir de cette classe de base.
public class FirebaseException : Exception {
public ErrorCode ErrorCode { get; }
public String Message { get; }
public HttpResponseMessage HttpResponse { get; }
}
Les API qui exposent les codes d'erreur de service fournissent des sous-classes de FirebaseException
spécifiques aux API. Par exemple, toutes les méthodes publiques de l'API FirebaseAuth
sont déclarées pour lancer des instances de FirebaseAuthException
. Vous pouvez accéder au code d'erreur du service à partir de cette classe dérivée.
public class FirebaseAuthException : FirebaseException {
public AuthErrorCode AuthErrorCode { get; }
}
Codes d'erreur de la plateforme
Les codes d'erreur sont communs à tous les services Firebase et Google Cloud Platform. Le tableau suivant présente tous les codes d'erreur possibles de la plate-forme. Il s’agit d’une liste stable et qui devrait rester inchangée pendant une longue période.
ARGUMENT INVALIDE | Le client a spécifié un argument non valide. |
FAILED_PRECONDITION | La requête ne peut pas être exécutée dans l'état actuel du système, comme la suppression d'un répertoire non vide. |
OUT_OF_RANGE | Le client a spécifié une plage non valide. |
NON AUTHENTIFIÉ | Demande non authentifiée en raison d'un jeton OAuth manquant, invalide ou expiré. |
PERMISSION REFUSÉE | Le client ne dispose pas d'une autorisation suffisante. Cela peut se produire parce que le jeton OAuth n'a pas les bonnes étendues, que le client n'a pas l'autorisation ou que l'API n'a pas été activée pour le projet client. |
PAS TROUVÉ | La ressource spécifiée est introuvable ou la demande est rejetée pour des raisons non divulguées telles que la mise sur liste blanche. |
CONFLIT | Conflit de concurrence, tel qu'un conflit de lecture-modification-écriture. Utilisé uniquement par quelques services existants. La plupart des services utilisent ABORTED ou ALREADY_EXISTS à la place. Reportez-vous à la documentation spécifique au service pour voir lequel gérer dans votre code. |
AVORTÉ | Conflit de concurrence, tel qu'un conflit de lecture-modification-écriture. |
EXISTE DÉJÀ | La ressource qu'un client a tenté de créer existe déjà. |
RESOURCE_EXHAUSTED | Soit en dehors du quota de ressources, soit en atteignant la limite de débit. |
ANNULÉ | Demande annulée par le client. |
PERTE DE DONNÉES | Perte de données irrécupérable ou corruption de données. Le client doit signaler l'erreur à l'utilisateur. |
INCONNU | Erreur de serveur inconnue. Généralement un bug du serveur. Ce code d'erreur est également attribué aux erreurs d'analyse de réponse locale (unmarshal) et à un large éventail d'autres erreurs d'E/S de bas niveau qui ne sont pas facilement diagnosticables. |
INTERNE | Erreur interne du serveur. Généralement un bug du serveur. |
INDISPONIBLE | Service non disponible. Généralement, le serveur est temporairement hors service. Ce code d'erreur est également attribué aux erreurs du réseau local (connexion refusée, pas de route vers l'hôte). |
DEADLINE_EXCEEDED | Délai de demande dépassé. Cela ne se produira que si l'appelant définit un délai plus court que le délai par défaut de l'API cible (c'est-à-dire que le délai demandé n'est pas suffisant pour que le serveur traite la demande) et que la demande ne s'est pas terminée dans le délai imparti. Ce code d'erreur est également attribué à la connexion locale et aux délais de lecture. |
La plupart des API ne peuvent générer qu'un sous-ensemble des codes d'erreur ci-dessus. Dans tous les cas, vous n'êtes pas censé gérer explicitement tous ces codes d'erreur lors de l'implémentation de vos gestionnaires d'erreurs. La plupart des applications ne s'intéresseraient qu'à 1 à 2 codes d'erreur spécifiques et traiteraient tout le reste comme une panne générique irrécupérable.
Codes d'erreur spécifiques au service
Authentification Firebase
CERTIFICAT_FETCH_FAILED | Échec de la récupération des certificats de clé publique requis pour vérifier un JWT (jeton d'identification ou cookie de session). |
L'EMAIL EXISTE DÉJÀ | Un utilisateur existe déjà avec l'e-mail fourni. |
EXPIRED_ID_TOKEN | Le jeton d'ID spécifié pour verifyIdToken() a expiré. |
EXPIRED_SESSION_COOKIE | Le cookie de session spécifié pour verifySessionCookie() a expiré. |
INVALID_DYNAMIC_LINK_DOMAIN | Le domaine de lien dynamique fourni n'est pas configuré ou autorisé pour le projet en cours. Lié aux API de liens d’action par courrier électronique. |
INVALID_ID_TOKEN | Le jeton d'ID spécifié pour verifyIdToken() n'est pas valide. |
INVALID_SESSION_COOKIE | Le cookie de session spécifié pour verifySessionCookie() n'est pas valide. |
PHONE_NUMBER_ALREADY_EXISTS | Un utilisateur existe déjà avec le numéro de téléphone fourni. |
REVOKED_ID_TOKEN | Le jeton d'ID spécifié pour verifyIdToken() est révoqué. |
REVOKED_SESSION_COOKIE | Le cookie de session spécifié pour verifySessionCookie() a expiré. |
UNAUTHORIZED_CONTINUE_URL | Le domaine de l'URL de suite n'est pas sur liste blanche. Lié aux API de liens d’action par courrier électronique. |
UTILISATEUR NON TROUVÉ | Aucun enregistrement d'utilisateur trouvé pour l'identifiant donné. |
Messagerie cloud Firebase
THIRD_PARTY_AUTH_ERROR | Le certificat APN ou la clé API d'authentification Web Push était invalide ou manquant. |
ARGUMENT INVALIDE | Un ou plusieurs arguments spécifiés dans la demande n'étaient pas valides. |
INTERNE | Erreur interne du serveur. |
QUOTA DÉPASSÉ | Limite d'envoi dépassée pour la cible du message. |
SENDER_ID_MISMATCH | L'ID de l'expéditeur authentifié est différent de l'ID de l'expéditeur du jeton d'enregistrement. Cela signifie généralement que l'expéditeur et le jeton d'enregistrement cible ne se trouvent pas dans le même projet Firebase. |
INDISPONIBLE | Le service Cloud Messaging est temporairement indisponible. |
NON ENREGISTRÉ | L'instance d'application n'a pas été enregistrée auprès de FCM. Cela signifie généralement que le jeton d'enregistrement de l'appareil utilisé n'est plus valide et qu'un nouveau doit être utilisé. |
Nouvelles tentatives automatiques
Le SDK Admin réessaye automatiquement certaines erreurs avant de les exposer aux utilisateurs. En général, les types d’erreurs suivants font l’objet de nouvelles tentatives transparentes :
- Toutes les erreurs API résultant de réponses HTTP 503 (Service non disponible).
- Certaines erreurs API résultant de réponses HTTP 500 (Internal Server Error).
- La plupart des erreurs d'E/S de bas niveau (connexion refusée, réinitialisation de la connexion, etc.).
Le SDK réessayera chacune des erreurs ci-dessus jusqu'à 5 fois (la tentative d'origine + 4 tentatives) avec un intervalle exponentiel. Vous pouvez implémenter vos propres mécanismes de nouvelle tentative au niveau de l'application si vous le souhaitez, mais cela n'est généralement pas obligatoire.
Prise en charge de la nouvelle tentative après
Les implémentations Go et .NET du SDK Admin prennent en charge la gestion de l'en-tête HTTP Retry-After
. Autrement dit, si la réponse d'erreur envoyée par les serveurs backend contient l'en-tête Retry-After
standard, le SDK le respectera lors d'une nouvelle tentative tant que la durée d'attente spécifiée n'est pas très longue. Si l’en-tête Retry-After
indique une durée d’attente très longue, le SDK ignorera les tentatives et générera l’erreur API appropriée.
Le SDK Python Admin ne prend actuellement pas en charge l’en-tête Retry-After
et ne prend en charge qu’un simple intervalle exponentiel.
Exemples de gestion des erreurs API
Implémentation d'un gestionnaire d'erreurs générique
Dans la plupart des cas, vous souhaitez un gestionnaire d'erreurs générique qui détecte un large éventail d'erreurs afin d'éviter une interruption inattendue du flux du programme en raison d'une erreur d'API. Ces gestionnaires d'erreurs se contentent généralement d'enregistrer les erreurs à des fins d'audit ou d'appeler une autre routine de gestion des erreurs par défaut pour toutes les erreurs d'API rencontrées. Ils ne sont pas forcément intéressés par les différents codes d’erreur ni par les raisons qui ont pu provoquer l’erreur.
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}')
Aller
token, err := client.VerifyIDToken(ctx, idToken)
if err != nil {
log.Printf("Failed to verify ID token: %v", err)
return
}
performPrivilegedOperation(token)
.Filet
try
{
var token = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(idToken);
PerformPrivilegedOperation(token.getUid());
}
catch (FirebaseAuthException ex)
{
Conole.WriteLine($"Failed to verify ID token: {ex.Message}");
}
Vérification des codes d'erreur
Dans certains cas, vous souhaiterez inspecter les codes d'erreur exacts et invoquer différentes routines de gestion des erreurs contextuelles. Dans l'exemple suivant, nous avons un gestionnaire d'erreurs qui enregistre des messages d'erreur plus spécifiques en fonction du code d'erreur du service.
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}')
Aller
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)
.Filet
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}");
}
}
Voici un autre exemple dans lequel nous vérifions les codes d'erreur de niveau supérieur et de service :
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}')
Aller
_, 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
}
.Filet
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}");
}
}
Accéder à la réponse HTTP
Dans de rares cas, vous souhaiterez peut-être inspecter la réponse d'erreur HTTP renvoyée par un service backend et effectuer une action de gestion des erreurs sur celle-ci. Le SDK Admin expose à la fois les en-têtes et le contenu de ces réponses d’erreur. Le contenu de la réponse est généralement renvoyé sous forme de chaîne ou de séquence d'octets brute et peut être analysé dans n'importe quel format cible nécessaire.
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}')
Aller
_, 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
}
.Filet
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}");
}
}