טיפול בשגיאות SDK של Admin

שגיאות Admin SDK מחולקות לשתי קטגוריות:

  1. שגיאות תכנות: אלו הן שגיאות תכנות ותצורה באפליקציית המשתמש. הם מתרחשים בעיקר עקב שימוש שגוי ב-SDK (כגון העברת null לשיטה שאינה מקבלת ערכי null ), ושגיאות תצורה אחרות ברמת פרויקט Firebase או SDK (חסרים אישורים, מחרוזת מזהה פרויקט שגויה וכדומה עַל).
  2. שגיאות API: אלה כוללות שגיאות שונות הניתנות לשחזור המתרחשות ביישום SDK, כל השגיאות שמקורן בשירותי Firebase backend ושגיאות חולפות אחרות (כגון פסקי זמן) שעלולות להתרחש בעת ביצוע קריאות RPC.

ה-Admin SDK מאותת על שגיאות תכנות על ידי זריקת שגיאה מקורית לפלטפורמה המדוברת.

  • Java: זורק מופעים של IllegalArgumentException , NullPointerException או סוג שגיאת זמן ריצה מובנה דומה.
  • Python: מעלה מופעים של ValueError , TypeError או סוג שגיאה מובנה אחר.
  • Go: מחזירה שגיאה כללית.
  • .NET: זורק מופעים של ArgumentException , ArgumentNullException או סוג שגיאה מובנה דומה.

ברוב המצבים אסור לטפל במפורש בשגיאות תכנות. במקום זאת, עליך לתקן את הקוד והתצורה שלך כדי למנוע שגיאות תכנות לחלוטין. שקול את קטע ה-Java הבא:

String uid = getUserInput();

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

אם השיטה getUserInput() מחזירה מחרוזות null או ריקות, ה-API FirebaseAuth.getUser() זורק קובץ IllegalArgumentException . במקום לטפל בה באופן מפורש, אתה יכול להקל על הבעיה על ידי הבטחת השיטה getUserInput() לעולם לא תחזיר מחרוזת UID לא חוקית. אם זה לא אפשרי, יישם את בדיקת הארגומנטים הדרושים בקוד משלך באופן הבא:

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

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

כעיקרון, לעולם אל תנסה שוב על שגיאות תכנות. מתן אפשרות לסמנטיקה מהירה לכישלון על שגיאות תכנות היא לרוב דרך הפעולה הטובה ביותר מכיוון שהיא חושפת באגים בתכנות ושגיאות תצורה במהלך הפיתוח, שם ניתן לתקן אותן באופן מיידי. כשל מהיר בהקשר זה יכול להיות מתן אפשרות לשגיאות להתפשט למטפל שגיאות גלובלי באפליקציה שלך, או פשוט רישום אותן למטרות ביקורת ולאחר מכן סיום זרימת הביצוע הנוכחית (האפליקציה לא צריכה לקרוס). באופן כללי, עקוב אחר השיטות המומלצות לטיפול בשגיאות של שפת התכנות שלך ושל מסגרת היישום. לעתים קרובות זה לבד מספיק כדי להתמודד בצורה נכונה עם סוג זה של שגיאות.

בדרך כלל, עיקר מאמצי הטיפול בשגיאות יתמקדו בטיפול בשגיאות API . חלק מהשגיאות הללו ניתנות לשחזור, כגון שגיאות שנבעו משירות בלתי זמין באופן זמני, וחלקן אף צפויות במהלך זרימת ההפעלה הרגילה של התוכנית, כגון זיהוי אסימוני מזהה לא חוקיים או שפג תוקפם. שאר המדריך מתאר כיצד ה-Admin SDK מייצג שגיאות API כאלה, ואת האפשרויות השונות הזמינות לטיפול בהן.

מבנה של שגיאת API

שגיאת API מורכבת מהרכיבים הבאים:

  1. קוד שגיאה
  2. הודעת שגיאה
  3. קוד שגיאה בשירות (אופציונלי)
  4. תגובת HTTP (אופציונלי)

מובטח שכל שגיאת API תכיל קוד שגיאה והודעת שגיאה. שגיאות API מסוימות מכילות גם קוד שגיאת שירות הספציפי ל-API שיצר את השגיאה. לדוגמה, שגיאות מסוימות שנוצרו על ידי Firebase Auth API מכילות קוד שגיאת שירות הספציפי ל-Firebase Auth. אם השגיאה הייתה תוצאה של תגובת שגיאת HTTP משירות אחורי, שגיאת ה-API מכילה גם את תגובת ה-HTTP המתאימה. זה יכול לשמש כדי לבדוק את הכותרות והתוכן המדויקים של התגובה המקורית, וזה שימושי עבור ניפוי באגים, רישום, או יישום לוגיקה מתוחכמת יותר לטיפול בשגיאות.

כל ההטמעות של Admin SDK מלבד Node.js מספקות ממשקי API המאפשרים גישה לרכיבים שלעיל של שגיאות API.

סוגי שגיאות וממשקי API לפי שפה

Java

ב-Java כל שגיאות ה-API מרחיבות את המחלקה FirebaseException . אתה יכול לגשת לקוד השגיאה, להודעת השגיאה ולתגובת ה-HTTP האופציונלית ממחלקת הבסיס הזו.

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

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

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

ממשקי API שחושפים קודי שגיאות שירות מספקים תת-מחלקות ספציפיות ל-API של FirebaseException . לדוגמה, כל השיטות הציבוריות בממשק ה-API FirebaseAuth מוכרזות כמזרקות מופעים של FirebaseAuthException . אתה יכול לגשת לקוד שגיאת השירות ממחלקה נגזרת זו.

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

פִּיתוֹן

ב-Python כל שגיאות ה-API מרחיבות את המחלקה exceptions.FirebaseError . אתה יכול לגשת לקוד השגיאה, להודעת השגיאה ולתגובת HTTP האופציונלית ממחלקת הבסיס הזו.

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

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

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

יתר על כן, Python Admin SDK מציע מחלקות נגזרות נפרדות עבור כל קוד שגיאה. אנו מתייחסים אליהם כאל מחלקות שגיאות פלטפורמה .

class InvalidArgumentError(FirebaseError):
    # ...

class NotFoundError(FirebaseError):
    # ...

אתה יכול לתפוס FirebaseError בקוד שלך ולבדוק code שלו, או לבצע בדיקת isinstance() מול מחלקת שגיאות של פלטפורמה. או שאתה יכול לכתוב קוד כדי לתפוס ישירות סוגי שגיאות ספציפיים בפלטפורמה. הגישה האחרונה צפויה לגרום לקוד טיפול בשגיאות קריא יותר.

ממשקי API החושפים קודי שגיאה של שירות מספקים תת-מחלקות ספציפיות ל-API של מחלקות שגיאות בפלטפורמה. לדוגמה, כל השיטות הציבוריות במודול auth עשויות לזרוק סוגי שגיאות ספציפיות ל-API כגון auth.UserNotFoundError ו- auth.ExpiredIdTokenError .

class UserNotFoundError(exceptions.NotFoundError):
    # …

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

ללכת

ה- Go Admin SDK מספק חבילת errorutils המכילה סדרה של פונקציות המאפשרות בדיקת קודי שגיאה.

package errorutils

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

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

הודעת השגיאה היא פשוט המחרוזת המוחזרת על ידי הפונקציה Error() של שגיאה. ניתן לגשת לתגובת HTTP האופציונלית על ידי קריאה לפונקציה errorutils.HTTPResponse() , המחזירה *http.Response .

זה בטוח להעביר nil או כל ערך שגיאה אחר לפונקציות בדיקת השגיאות בחבילת errorutils . הם מחזירים true אם ארגומנט הקלט מכיל את קוד השגיאה המדובר, ומחזירים false עבור כל השאר. לפונקציה HTTPResponse() יש התנהגות דומה, אלא שהיא מחזירה nil במקום false .

ממשקי API החושפים קודי שגיאות שירות מספקים פונקציות בדיקת שגיאות ספציפיות ל-API בחבילות המתאימות. לדוגמה, חבילת ה- auth מספקת את הפונקציות IsUserNotFound() ו- IsExpiredIDTokenError() .

.נֶטוֹ

ב-.NET כל שגיאות ה-API מרחיבות את המחלקה FirebaseException . אתה יכול לגשת לקוד השגיאה של הפלטפורמה, להודעת השגיאה ולתגובת ה-HTTP האופציונלית ממחלקת הבסיס הזו.

public class FirebaseException : Exception {

    public ErrorCode ErrorCode { get; }

    public String Message { get; }

    public HttpResponseMessage HttpResponse { get; }
}

ממשקי API שחושפים קודי שגיאות שירות מספקים תת-מחלקות ספציפיות ל-API של FirebaseException . לדוגמה, כל השיטות הציבוריות בממשק ה-API FirebaseAuth מוכרזות כמזרקות מופעים של FirebaseAuthException . אתה יכול לגשת לקוד שגיאת השירות ממחלקה נגזרת זו.

public class FirebaseAuthException : FirebaseException {

    public AuthErrorCode AuthErrorCode { get; }
}

קודי שגיאה של פלטפורמה

קודי שגיאה נפוצים בכל שירותי Firebase ו-Google Cloud Platform. הטבלה הבאה מתארת ​​את כל קודי השגיאה האפשריים של הפלטפורמה. זוהי רשימה יציבה, וצפויה להישאר ללא שינוי לתקופה ארוכה.

INVALID_ARGUMENT הלקוח ציין ארגומנט לא חוקי.
FAILED_PRECONDITION לא ניתן לבצע בקשה במצב המערכת הנוכחי, כגון מחיקת ספרייה לא ריקה.
מחוץ לטווח הלקוח ציין טווח לא חוקי.
לא מאומת הבקשה לא אומתה עקב אסימון OAuth חסר, לא חוקי או שפג תוקפו.
ההרשאה נדחתה ללקוח אין הרשאה מספקת. זה יכול לקרות מכיוון שלאסימון OAuth אין את ההיקפים הנכונים, ללקוח אין הרשאה או שה-API לא הופעל עבור פרויקט הלקוח.
לא נמצא המשאב שצוין לא נמצא, או שהבקשה נדחתה מסיבות לא ידועות כמו רישום הלבן.
סְתִירָה התנגשות במקביל, כגון התנגשות קריאה-שינוי-כתיבה. בשימוש רק על ידי כמה שירותים מדור קודם. רוב השירותים משתמשים ב-ABORTED או ALREADY_EXISTS במקום זה. עיין בתיעוד הספציפי לשירות כדי לראות באיזה מהם לטפל בקוד שלך.
הופסק התנגשות במקביל, כגון התנגשות קריאה-שינוי-כתיבה.
כבר קיים המשאב שלקוח ניסה ליצור כבר קיים.
RESOURCE_EXHAUSTED או מחוץ למכסת המשאבים או הגעה להגבלת שיעור.
מבוטל הבקשה בוטלה על ידי הלקוח.
DATA_LOSS אובדן נתונים בלתי ניתן לשחזור או השחתת נתונים. על הלקוח לדווח למשתמש על השגיאה.
לא ידוע שגיאת שרת לא ידועה. בדרך כלל באג בשרת.

קוד שגיאה זה מוקצה גם לשגיאות ניתוח (unmarshal) של תגובה מקומית, ולמגוון רחב של שגיאות I/O ברמה נמוכה אחרות שאינן ניתנות לאבחון בקלות.

פְּנִימִי שגיאת שרת פנימית. בדרך כלל באג בשרת.
אינו זמין שירותים לא זמינים. בדרך כלל השרת מושבת זמנית.

קוד שגיאה זה מוקצה גם לשגיאות רשת מקומית (החיבור נדחה, אין נתיב למארח).

DEADLINE_EXCEEDED חרג המועד האחרון לבקשה. זה יקרה רק אם המתקשר יגדיר דד-ליין קצר יותר ממועד ברירת המחדל של יעד API (כלומר דד-ליין מבוקש אינו מספיק כדי שהשרת יעבד את הבקשה), והבקשה לא הסתיימה בתוך המועד.

קוד שגיאה זה מוקצה גם לזמן קצוב לחיבור מקומי ולקריאה.

רוב ממשקי ה-API יכולים לגרום רק לקבוצת משנה של קודי השגיאה שלעיל. בכל מקרה, לא מצפים ממך לטפל במפורש בכל קודי השגיאה הללו בעת יישום מטפלי השגיאות שלך. רוב היישומים יתעניינו רק ב-1-2 קודי שגיאה ספציפיים ויתייחסו לכל השאר כאל כישלון גנרי שלא ניתן לשחזר.

קודי שגיאה ספציפיים לשירות

אישור Firebase

CERTIFICATE_FETCH_FAILED נכשל באחזור אישורי מפתח ציבורי הנדרשים לאימות JWT (אסימון מזהה או קובץ cookie של הפעלה).
האימייל כבר קיים משתמש כבר קיים עם האימייל שסופק.
EXPIRED_ID_TOKEN פג תוקפו של אסימון המזהה שצוין ל- verifyIdToken() .
EXPIRED_SESSION_COOKIE פג תוקף קובץ ה-cookie של ההפעלה שצוין ל- verifySessionCookie() i.
INVALID_DYNAMIC_LINK_DOMAIN תחום הקישור הדינמי שסופק אינו מוגדר או מורשה עבור הפרויקט הנוכחי. קשור לממשקי API של קישור פעולות בדוא"ל.
INVALID_ID_TOKEN אסימון המזהה שצוין ל- verifyIdToken() אינו חוקי.
INVALID_SESSION_COOKIE קובץ ה-cookie של ההפעלה שצוין ל- verifySessionCookie() אינו חוקי.
PHONE_NUMBER_ALREADY_EXISTS משתמש כבר קיים עם מספר הטלפון שסופק.
REVOKED_ID_TOKEN אסימון המזהה שצוין ל- verifyIdToken() בוטל.
REVOKED_SESSION_COOKIE פג תוקף קובץ ה-cookie של ההפעלה שצוין ל- verifySessionCookie() .
UNAUTHORIZED_CONTINUE_URL הדומיין של המשך כתובת האתר אינו ברשימת ההיתרים. קשור לממשקי API של קישור פעולות בדוא"ל.
המשתמש לא נמצא לא נמצאה רשומת משתמש עבור המזהה הנתון.

Firebase Cloud Messaging

THIRD_PARTY_AUTH_ERROR אישור ה-APN או מפתח ה-API של דחיפה באינטרנט לא היה חוקי או חסר.
INVALID_ARGUMENT ארגומנט אחד או יותר שצוין בבקשה לא היה חוקי.
פְּנִימִי שגיאת שרת פנימית.
חריגה מהמכסה חרגת ממגבלת השליחה עבור יעד ההודעה.
SENDER_ID_MISMATCH מזהה השולח המאומת שונה מזהה השולח עבור אסימון הרישום. זה בדרך כלל אומר שהשולח ואסימון הרישום היעד אינם נמצאים באותו פרויקט Firebase.
אינו זמין שירות הודעות ענן אינו זמין באופן זמני.
לא רשום מופע האפליקציה לא נרשם מ-FCM. בדרך כלל זה אומר שאסימון הרישום של המכשיר שבו נעשה שימוש אינו תקף יותר ויש להשתמש באחד חדש.

ניסיונות חוזרים אוטומטיים

ה-Admin SDK מנסה אוטומטית שגיאות מסוימות לפני חשיפת השגיאות הללו למשתמשים. באופן כללי, סוגי השגיאות הבאים מנוסים שוב בשקיפות:

  • כל שגיאות ה-API הנובעות מתגובות HTTP 503 (שירות לא זמין).
  • כמה שגיאות API הנובעות מתגובות HTTP 500 (שגיאת שרת פנימית).
  • רוב שגיאות ה-I/O ברמה נמוכה (החיבור נדחה, איפוס החיבור וכו').

ה-SDK ינסה שוב כל אחת מהשגיאות הנ"ל עד 5 פעמים (הניסיון המקורי + 4 ניסיונות חוזרים) עם השבתה מעריכית. אתה יכול ליישם מנגנוני ניסיון חוזר משלך ברמת היישום אם תרצה, אבל זה בדרך כלל לא נדרש.

נסה שוב-לאחר תמיכה

ההטמעות Go ו-.NET של ה-Admin SDK מגיעות עם תמיכה בטיפול בכותרת HTTP Retry-After . כלומר, אם תגובת השגיאה שנשלחה על ידי שרתי הקצה מכילה את הכותרת Retry-After הסטנדרטית, ה-SDK יכבד זאת בעת ניסיון חוזר כל עוד משך ההמתנה שצוין אינו ארוך במיוחד. אם הכותרת Retry-After מציינת משך המתנה ארוך מאוד, ה-SDK יעקוף נסיונות חוזרים ויזרוק את שגיאת ה-API המתאימה.

Python Admin SDK אינו תומך כרגע בכותרת Retry-After , ותומך רק ב-backoff אקספוננציאלי פשוט.

דוגמאות לטיפול בשגיאות API

הטמעת מטפל שגיאות גנרי

ברוב המקרים מה שאתה רוצה הוא מטפל שגיאות גנרי שתופס מגוון רחב של שגיאות כדי למנוע סיום בלתי צפוי של זרימת התוכנית עקב שגיאת API. מטפלי שגיאות כאלה בדרך כלל פשוט רושמים את השגיאות למטרות ביקורת, או מפעילים שגרת ברירת מחדל אחרת לטיפול בשגיאות עבור כל שגיאות ה-API שנתקלו. הם לא בהכרח מתעניינים בקודי השגיאה השונים או בסיבות שאולי גרמו לשגיאה.

Java

try {
  FirebaseToken token = FirebaseAuth.getInstance().verifyIdToken(idToken);
  performPrivilegedOperation(token.getUid());
} catch (FirebaseAuthException ex) {
  System.err.println("Failed to verify ID token: " + ex.getMessage());
}

פִּיתוֹן

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

ללכת

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

performPrivilegedOperation(token)

.נֶטוֹ

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

בדיקת קודי שגיאה

במקרים מסוימים, תרצה לבדוק את קודי השגיאה המדויקים, ולהפעיל שגרות שונות לטיפול בשגיאות מודעות להקשר. בדוגמה הבאה, יש לנו מטפל בשגיאות שמתעד הודעות שגיאה ספציפיות יותר על סמך קוד השגיאה של השירות.

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

פִּיתוֹן

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

ללכת

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)

.נֶטוֹ

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

הנה דוגמה נוספת שבה אנו בודקים את קודי השגיאה ברמה העליונה וגם את קודי השירות:

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

פִּיתוֹן

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

ללכת

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

.נֶטוֹ

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

גישה לתגובת HTTP

במקרים נדירים מסוימים, ייתכן שתרצה לבדוק את תגובת שגיאת ה-HTTP המוחזרת על ידי שירות אחורי ולבצע עליה פעולה כלשהי לטיפול בשגיאות. ה-Admin SDK חושף הן את הכותרות והן את התוכן של תגובות השגיאה הללו. תוכן התגובה מוחזר בדרך כלל כמחרוזת או כרצף בתים גולמי, וניתן לנתח אותו לכל פורמט יעד הכרחי.

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

פִּיתוֹן

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

ללכת

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

.נֶטוֹ

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