Xử lý lỗi SDK quản trị

Lỗi SDK quản trị được chia thành hai loại:

  1. Lỗi lập trình: Đây là các lỗi lập trình và cấu hình trong ứng dụng của người dùng. Chúng chủ yếu xảy ra do cách sử dụng SDK không chính xác (chẳng hạn như chuyển null sang một phương thức không chấp nhận giá trị null ) và các lỗi cấu hình khác ở cấp độ SDK hoặc dự án Firebase (thiếu thông tin xác thực, chuỗi ID dự án không chính xác, v.v. TRÊN).
  2. Lỗi API: Chúng bao gồm nhiều lỗi có thể phục hồi khác nhau xảy ra trong quá trình triển khai SDK, tất cả các lỗi bắt nguồn từ dịch vụ phụ trợ của Firebase và các lỗi tạm thời khác (chẳng hạn như hết thời gian chờ) có thể xảy ra khi thực hiện lệnh gọi RPC.

SDK quản trị báo hiệu lỗi lập trình bằng cách đưa ra lỗi xuất phát từ nền tảng được đề cập.

  • Java: Ném ra các phiên bản của IllegalArgumentException , NullPointerException hoặc loại lỗi thời gian chạy tích hợp tương tự.
  • Python: Tăng các phiên bản của ValueError , TypeError hoặc loại lỗi tích hợp khác.
  • Go: Trả về lỗi chung.
  • .NET: Ném ra các phiên bản của ArgumentException , ArgumentNullException hoặc loại lỗi tích hợp tương tự.

Trong hầu hết các tình huống, bạn không nên xử lý rõ ràng các lỗi lập trình. Thay vào đó, bạn nên sửa mã và cấu hình của mình để tránh hoàn toàn các lỗi lập trình. Hãy xem xét đoạn mã Java sau:

String uid = getUserInput();

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

Nếu phương thức getUserInput() trả về null hoặc chuỗi rỗng thì API FirebaseAuth.getUser() sẽ ném IllegalArgumentException . Thay vì xử lý nó một cách rõ ràng, bạn có thể giảm thiểu vấn đề bằng cách đảm bảo phương thức getUserInput() không bao giờ trả về chuỗi UID không hợp lệ. Nếu điều đó là không thể, hãy triển khai việc kiểm tra đối số cần thiết trong mã của riêng bạn như sau:

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

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

Theo nguyên tắc, không bao giờ thử lại các lỗi lập trình. Cho phép sử dụng ngữ nghĩa không nhanh đối với các lỗi lập trình thường là cách hành động tốt nhất vì nó làm lộ ra các lỗi lập trình và lỗi cấu hình trong quá trình phát triển, nơi chúng có thể được sửa chữa kịp thời. Không nhanh trong ngữ cảnh này có thể có nghĩa là để lỗi lan truyền đến trình xử lý lỗi chung trong ứng dụng của bạn hoặc chỉ ghi lại chúng cho mục đích kiểm tra, sau đó là chấm dứt luồng thực thi hiện tại (ứng dụng không cần phải gặp sự cố). Nói chung, hãy làm theo các phương pháp hay nhất về xử lý lỗi trong ngôn ngữ lập trình và khung ứng dụng của bạn. Chỉ điều này thường là đủ để xử lý chính xác loại lỗi này.

Thông thường, phần lớn nỗ lực xử lý lỗi của bạn sẽ tập trung vào việc xử lý lỗi API . Một số lỗi trong số này có thể phục hồi được, chẳng hạn như lỗi xảy ra do dịch vụ tạm thời không khả dụng và một số lỗi thậm chí còn được dự đoán trước trong luồng thực thi chương trình thông thường, chẳng hạn như phát hiện mã thông báo ID không hợp lệ hoặc đã hết hạn. Phần còn lại của hướng dẫn này trình bày cách SDK quản trị thể hiện các lỗi API đó và các tùy chọn khác nhau có sẵn để xử lý chúng.

Cấu trúc của lỗi API

Lỗi API bao gồm các thành phần sau:

  1. Mã lỗi
  2. Thông báo lỗi
  3. Mã lỗi dịch vụ (Tùy chọn)
  4. Phản hồi HTTP (Tùy chọn)

Mọi lỗi API đều được đảm bảo chứa mã lỗi và thông báo lỗi. Một số lỗi API nhất định cũng chứa mã lỗi dịch vụ dành riêng cho API đã tạo ra lỗi. Ví dụ: một số lỗi do API xác thực Firebase tạo ra có chứa mã lỗi dịch vụ dành riêng cho Firebase Auth. Nếu lỗi là do phản hồi lỗi HTTP từ dịch vụ phụ trợ thì lỗi API cũng chứa phản hồi HTTP tương ứng. Điều này có thể được sử dụng để kiểm tra tiêu đề và nội dung chính xác của phản hồi ban đầu, rất hữu ích cho việc gỡ lỗi, ghi nhật ký hoặc triển khai logic xử lý lỗi phức tạp hơn.

Tất cả hoạt động triển khai SDK quản trị ngoại trừ Node.js đều cung cấp các API cho phép truy cập vào các thành phần lỗi API nêu trên.

Các loại lỗi và API theo ngôn ngữ

Java

Trong Java, tất cả các lỗi API đều mở rộng lớp FirebaseException . Bạn có thể truy cập mã lỗi, thông báo lỗi và phản hồi HTTP tùy chọn từ lớp cơ sở này.

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

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

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

Các API hiển thị mã lỗi dịch vụ cung cấp các lớp con dành riêng cho API của FirebaseException . Ví dụ: tất cả các phương thức công khai trong API FirebaseAuth đều được khai báo để ném các phiên bản của FirebaseAuthException . Bạn có thể truy cập mã lỗi dịch vụ từ lớp dẫn xuất này.

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

Python

Trong Python, tất cả các lỗi API đều mở rộng lớp exceptions.FirebaseError . Bạn có thể truy cập mã lỗi, thông báo lỗi và phản hồi HTTP tùy chọn từ lớp cơ sở này.

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

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

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

Hơn nữa, SDK quản trị Python cung cấp các lớp dẫn xuất riêng cho từng mã lỗi. Chúng tôi gọi chúng là các lớp lỗi nền tảng .

class InvalidArgumentError(FirebaseError):
    # ...

class NotFoundError(FirebaseError):
    # ...

Bạn có thể phát hiện FirebaseError trong mã của mình và kiểm tra code đó hoặc thực hiện kiểm tra isinstance() đối với lớp lỗi nền tảng. Hoặc bạn có thể viết mã để trực tiếp phát hiện các loại lỗi nền tảng cụ thể. Cách tiếp cận thứ hai có thể dẫn đến mã xử lý lỗi dễ đọc hơn.

Các API hiển thị mã lỗi dịch vụ cung cấp các lớp con dành riêng cho API của các lớp lỗi nền tảng. Ví dụ: tất cả các phương thức công khai trong mô-đun auth có thể đưa ra các loại lỗi dành riêng cho API, chẳng hạn như auth.UserNotFoundErrorauth.ExpiredIdTokenError .

class UserNotFoundError(exceptions.NotFoundError):
    # …

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

Đi

SDK quản trị Go cung cấp gói errorutils chứa một loạt chức năng cho phép kiểm tra mã lỗi.

package errorutils

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

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

Thông báo lỗi chỉ đơn giản là chuỗi được hàm Error() trả về khi có lỗi. Có thể truy cập phản hồi HTTP tùy chọn bằng cách gọi hàm errorutils.HTTPResponse() , hàm này trả về *http.Response .

Việc chuyển nil hoặc bất kỳ giá trị lỗi nào khác tới các hàm kiểm tra lỗi trong gói errorutils là an toàn. Chúng trả về true nếu đối số đầu vào thực sự chứa mã lỗi được đề cập và trả về false cho mọi thứ khác. Hàm HTTPResponse() có hành vi tương tự, ngoại trừ việc nó trả về nil thay vì false .

Các API hiển thị mã lỗi dịch vụ cung cấp chức năng kiểm tra lỗi dành riêng cho API trong các gói tương ứng. Ví dụ: gói auth cung cấp các hàm IsUserNotFound()IsExpiredIDTokenError() .

.MẠNG LƯỚI

Trong .NET, tất cả các lỗi API đều mở rộng lớp FirebaseException . Bạn có thể truy cập mã lỗi nền tảng, thông báo lỗi và phản hồi HTTP tùy chọn từ lớp cơ sở này.

public class FirebaseException : Exception {

    public ErrorCode ErrorCode { get; }

    public String Message { get; }

    public HttpResponseMessage HttpResponse { get; }
}

Các API hiển thị mã lỗi dịch vụ cung cấp các lớp con dành riêng cho API của FirebaseException . Ví dụ: tất cả các phương thức công khai trong API FirebaseAuth đều được khai báo để ném các phiên bản của FirebaseAuthException . Bạn có thể truy cập mã lỗi dịch vụ từ lớp dẫn xuất này.

public class FirebaseAuthException : FirebaseException {

    public AuthErrorCode AuthErrorCode { get; }
}

Mã lỗi nền tảng

Mã lỗi phổ biến trên tất cả các dịch vụ Firebase và Google Cloud Platform. Bảng sau liệt kê tất cả các mã lỗi nền tảng có thể xảy ra. Đây là danh sách ổn định và dự kiến ​​sẽ không thay đổi trong thời gian dài.

INVALID_ARGUMENT Khách hàng đã chỉ định một đối số không hợp lệ.
FAILED_PRCONDITION Yêu cầu không thể được thực thi ở trạng thái hệ thống hiện tại, chẳng hạn như xóa một thư mục không trống.
OUT_OF_RANGE Khách hàng đã chỉ định một phạm vi không hợp lệ.
KHÔNG ĐƯỢC XÁC THỰC Yêu cầu không được xác thực do mã thông báo OAuth bị thiếu, không hợp lệ hoặc đã hết hạn.
PERMISSION_DENIED Khách hàng không có đủ quyền. Điều này có thể xảy ra do mã thông báo OAuth không có phạm vi phù hợp, ứng dụng khách không có quyền hoặc API chưa được bật cho dự án ứng dụng khách.
KHÔNG TÌM THẤY Không tìm thấy tài nguyên được chỉ định hoặc yêu cầu bị từ chối vì những lý do không được tiết lộ, chẳng hạn như đưa vào danh sách trắng.
XUNG ĐỘT Xung đột đồng thời, chẳng hạn như xung đột đọc-sửa-ghi. Chỉ được sử dụng bởi một số dịch vụ cũ. Hầu hết các dịch vụ đều sử dụng AORTED hoặc ALREADY_EXISTS thay vì điều này. Hãy tham khảo tài liệu dành riêng cho từng dịch vụ để biết nên xử lý tài liệu nào trong mã của bạn.
BỎ QUA Xung đột đồng thời, chẳng hạn như xung đột đọc-sửa-ghi.
ĐÃ TỒN TẠI Tài nguyên mà khách hàng cố gắng tạo đã tồn tại.
TÀI NGUYÊN_EXHAUSTED Hết hạn ngạch tài nguyên hoặc đạt đến giới hạn tốc độ.
ĐÃ HỦY Yêu cầu bị hủy bởi khách hàng.
DATA_LOSS Mất dữ liệu không thể phục hồi hoặc hỏng dữ liệu. Khách hàng nên báo cáo lỗi cho người dùng.
KHÔNG XÁC ĐỊNH Lỗi máy chủ không xác định. Điển hình là lỗi máy chủ.

Mã lỗi này cũng được gán cho các lỗi phân tích cú pháp phản hồi cục bộ (không sắp xếp theo thứ tự) và một loạt các lỗi I/O cấp thấp khác không dễ chẩn đoán.

NỘI BỘ Lỗi máy chủ nội bộ. Điển hình là lỗi máy chủ.
KHÔNG CÓ SẴN Dịch vụ Không sẵn có. Thông thường máy chủ tạm thời ngừng hoạt động.

Mã lỗi này cũng được gán cho lỗi mạng cục bộ (kết nối bị từ chối, không có tuyến đến máy chủ).

DEADLINE_EXCEEDED Đã vượt quá thời hạn yêu cầu. Điều này sẽ chỉ xảy ra nếu người gọi đặt thời hạn ngắn hơn thời hạn mặc định của API mục tiêu (tức là thời hạn được yêu cầu không đủ để máy chủ xử lý yêu cầu) và yêu cầu không hoàn thành trong thời hạn.

Mã lỗi này cũng được gán cho kết nối cục bộ và thời gian chờ đọc.

Hầu hết các API chỉ có thể dẫn đến một tập hợp con các mã lỗi trên. Trong mọi trường hợp, bạn không cần phải xử lý rõ ràng tất cả các mã lỗi này khi triển khai trình xử lý lỗi của mình. Hầu hết các ứng dụng sẽ chỉ quan tâm đến 1-2 mã lỗi cụ thể và coi mọi thứ khác là lỗi chung, không thể khắc phục được.

Mã lỗi dành riêng cho dịch vụ

Xác thực Firebase

CERTIFICATE_FETCH_FAILED Không tìm nạp được chứng chỉ khóa công khai cần thiết để xác minh JWT (mã thông báo ID hoặc cookie phiên).
EMAIL ĐÃ TỒN TẠI Một người dùng đã tồn tại với email được cung cấp.
EXPIRED_ID_TOKEN Mã thông báo ID được chỉ định cho verifyIdToken() đã hết hạn.
EXPIRED_SESSION_COOKIE Cookie phiên được chỉ định để verifySessionCookie() i đã hết hạn.
INVALID_DYNAMIC_LINK_DOMAIN Miền liên kết động được cung cấp chưa được định cấu hình hoặc ủy quyền cho dự án hiện tại. Liên quan đến API liên kết hành động email.
INVALID_ID_TOKEN Mã thông báo ID được chỉ định cho verifyIdToken() không hợp lệ.
INVALID_SESSION_COOKIE Cookie phiên được chỉ định để verifySessionCookie() không hợp lệ.
PHONE_NUMBER_ALREADY_EXISTS Một người dùng đã tồn tại với số điện thoại được cung cấp.
REVOKED_ID_TOKEN Mã thông báo ID được chỉ định cho verifyIdToken() đã bị thu hồi.
REVOKED_SESSION_COOKIE Cookie phiên được chỉ định để verifySessionCookie() đã hết hạn.
UNAUTHORIZED_CONTINUE_URL Miền của URL tiếp tục không nằm trong danh sách trắng. Liên quan đến API liên kết hành động email.
USER_NOT_FOUND Không tìm thấy hồ sơ người dùng nào cho mã định danh đã cho.

Nhắn tin qua đám mây Firebase

THIRD_PARTY_AUTH_ERROR Chứng chỉ APN hoặc khóa API xác thực đẩy web không hợp lệ hoặc bị thiếu.
INVALID_ARGUMENT Một hoặc nhiều đối số được chỉ định trong yêu cầu không hợp lệ.
NỘI BỘ Lỗi máy chủ nội bộ.
QUOTA_EXCEEDED Đã vượt quá giới hạn gửi đối với mục tiêu thư.
SENDER_ID_MISMATCH ID người gửi được xác thực khác với ID người gửi cho mã thông báo đăng ký. Điều này thường có nghĩa là người gửi và mã thông báo đăng ký mục tiêu không nằm trong cùng một dự án Firebase.
KHÔNG CÓ SẴN Dịch vụ Nhắn tin qua Đám mây tạm thời không khả dụng.
KHÔNG ĐĂNG KÝ Phiên bản ứng dụng đã bị hủy đăng ký khỏi FCM. Điều này thường có nghĩa là mã thông báo đăng ký thiết bị đã sử dụng không còn hợp lệ và phải sử dụng mã thông báo mới.

Tự động thử lại

SDK quản trị tự động thử lại một số lỗi nhất định trước khi hiển thị những lỗi đó cho người dùng. Nói chung, các loại lỗi sau đây được thử lại một cách minh bạch:

  • Tất cả các lỗi API do phản hồi HTTP 503 (Dịch vụ không khả dụng).
  • Một số lỗi API do phản hồi HTTP 500 (Lỗi máy chủ nội bộ).
  • Hầu hết các lỗi I/O cấp thấp (kết nối bị từ chối, thiết lập lại kết nối, v.v.).

SDK sẽ thử lại mỗi lỗi trên tối đa 5 lần (lần thử ban đầu + 4 lần thử lại) với thời gian chờ theo cấp số nhân. Bạn có thể triển khai cơ chế thử lại của riêng mình ở cấp ứng dụng nếu muốn, nhưng điều này thường không bắt buộc.

Hỗ trợ thử lại sau

Việc triển khai Go và .NET của SDK quản trị có hỗ trợ xử lý tiêu đề HTTP Retry-After . Nghĩa là, nếu phản hồi lỗi do máy chủ phụ trợ gửi có chứa tiêu đề Retry-After tiêu chuẩn, SDK sẽ tôn trọng điều đó khi thử lại miễn là thời gian chờ đã chỉ định không quá dài. Nếu tiêu đề Retry-After cho biết thời gian chờ rất lâu thì SDK sẽ bỏ qua các lần thử lại và đưa ra lỗi API thích hợp.

SDK quản trị Python hiện không hỗ trợ tiêu đề Retry-After và chỉ hỗ trợ rút lui theo cấp số nhân đơn giản.

Ví dụ xử lý lỗi API

Triển khai trình xử lý lỗi chung

Trong hầu hết các trường hợp, điều bạn muốn là một trình xử lý lỗi chung có khả năng phát hiện nhiều loại lỗi nhằm ngăn chặn việc luồng chương trình bị dừng đột ngột do lỗi API. Những trình xử lý lỗi như vậy thường chỉ ghi lại các lỗi cho mục đích kiểm tra hoặc gọi một số quy trình xử lý lỗi mặc định khác cho tất cả các lỗi API gặp phải. Họ không nhất thiết phải quan tâm đến các mã lỗi khác nhau hoặc nguyên nhân có thể gây ra lỗi.

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

Đi

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

performPrivilegedOperation(token)

.Mạng lưới

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

Kiểm tra mã lỗi

Trong một số trường hợp, bạn muốn kiểm tra mã lỗi chính xác và gọi các quy trình xử lý lỗi nhận biết ngữ cảnh khác nhau. Trong ví dụ sau, chúng tôi có trình xử lý lỗi ghi lại các thông báo lỗi cụ thể hơn dựa trên mã lỗi dịch vụ.

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

Đi

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)

.Mạng lưới

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

Đây là một ví dụ khác trong đó chúng tôi kiểm tra cả mã lỗi dịch vụ và mã lỗi cấp cao nhất:

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

Đi

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

.Mạng lưới

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

Truy cập phản hồi HTTP

Trong một số trường hợp hiếm gặp, bạn có thể muốn kiểm tra phản hồi lỗi HTTP do dịch vụ phụ trợ trả về và thực hiện một số hành động xử lý lỗi trên đó. SDK quản trị hiển thị cả tiêu đề và nội dung của các phản hồi lỗi này. Nội dung phản hồi thường được trả về dưới dạng chuỗi hoặc chuỗi byte thô và có thể được phân tích cú pháp thành bất kỳ định dạng đích nào cần thiết.

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

Đi

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

.Mạng lưới

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