Error Admin SDK dibagi menjadi dua kategori:
- Error pemrograman: Ini adalah kesalahan pemrograman dan konfigurasi dalam
aplikasi pengguna. Masalah ini sebagian besar terjadi karena penggunaan SDK yang salah
(seperti meneruskan
null
ke metode yang tidak menerima nilainull
), dan error konfigurasi lainnya di level project Firebase atau SDK (tidak ada kredensial, string ID project salah, dan sebagainya). - Error API: Error ini menyertakan berbagai error yang dapat dipulihkan yang terjadi dalam implementasi SDK, error yang berasal dari layanan backend Firebase, dan error sementara lainnya (seperti waktu tunggu) yang mungkin terjadi saat melakukan panggilan RPC.
Admin SDK menandai error pemrograman dengan menampilkan error yang berasal dari platform yang bersangkutan.
- Java: Menampilkan instance
IllegalArgumentException
,NullPointerException
atau jenis error runtime bawaan yang serupa. - Python: Memunculkan instance
ValueError
,TypeError
, atau jenis error bawaan lainnya. - Go: Menampilkan error umum.
- .NET: Menampilkan instance
ArgumentException
,ArgumentNullException
, atau jenis {i>error<i} bawaan yang serupa.
Dalam sebagian besar situasi, Anda tidak boleh menangani error pemrograman secara eksplisit. Sebagai gantinya, Anda harus memperbaiki kode dan konfigurasi Anda untuk menghindari {i>error <i}pemrograman sama sekali. Perhatikan cuplikan Java berikut:
String uid = getUserInput();
UserRecord user = FirebaseAuth.getInstance().getUser(uid);
Jika metode getUserInput()
menampilkan null
atau string kosong, metode
FirebaseAuth.getUser()
API akan menampilkan IllegalArgumentException
. Daripada fokus pada
menanganinya secara eksplisit, Anda dapat memitigasi
masalah dengan memastikan
Metode getUserInput()
tidak pernah menampilkan string UID yang tidak valid. Jika tidak
mungkin, terapkan pemeriksaan argumen yang diperlukan dalam kode Anda sendiri sebagai berikut:
String uid = getUserInput();
if (Strings.isNullOrEmpty(uid)) {
log.warn("UID must not be null or empty");
return;
}
UserRecord user = FirebaseAuth.getInstance().getUser(uid);
Pada prinsipnya, jangan mencoba ulang error pemrograman. Mengizinkan kegagalan cepat semantik pada kesalahan pemrograman sering kali merupakan tindakan terbaik karena mengekspos {i>bug<i} pemrograman dan kesalahan konfigurasi selama pengembangan, di mana mereka dapat segera diperbaiki. Gagal cepat dalam konteks ini berarti membiarkan {i>error<i} diterapkan ke pengendali error global dalam aplikasi Anda, atau hanya mencatatnya untuk tujuan audit yang diikuti dengan penghentian alur eksekusi saat ini (aplikasi tidak seharusnya error). Secara umum, ikuti petunjuk menangani praktik terbaik bahasa pemrograman dan aplikasi Google Workspace for Education. Ini saja sudah cukup untuk menangani kelas penyimpan{i> <i}ini yang sama.
Biasanya, sebagian besar upaya penanganan error akan berfokus pada penanganan API error. Sebagian error ini dapat dipulihkan, seperti error yang disebabkan oleh layanan yang tidak tersedia untuk sementara, dan beberapa bahkan diantisipasi selama alur eksekusi program yang normal, seperti mendeteksi token ID yang tidak valid atau sudah tidak berlaku. Bagian selanjutnya dari panduan ini menguraikan bagaimana Admin SDK merepresentasikan error API tersebut, dan berbagai opsi yang tersedia untuk menanganinya.
Struktur error API
Error API terdiri dari komponen berikut:
- Kode error
- Pesan error
- Kode error layanan (Opsional)
- Respons HTTP (Opsional)
Setiap error API dijamin berisi kode error dan pesan error. Error API tertentu juga berisi kode error layanan yang spesifik untuk API tersebut yang menyebabkan error. Misalnya, beberapa error yang dihasilkan oleh Firebase Auth API berisi kode error layanan yang spesifik untuk Firebase Auth. Jika kesalahan adalah hasil respons error HTTP dari layanan backend, error API juga berisi respons HTTP yang sesuai. Alat ini dapat digunakan untuk memeriksa {i>header<i} dan isi respon awal yang tepat, yang berguna untuk proses debug, logging, atau penerapan logika penanganan error yang lebih canggih.
Semua penerapan Admin SDK kecuali Node.js menyediakan API yang memungkinkan akses komponen error API di atas.
Jenis error dan API menurut bahasa
Java
Di Java, semua error API memperluas class FirebaseException
. Anda dapat mengakses
kode error, pesan error, dan respons HTTP opsional dari class dasar ini.
public class FirebaseException extends Exception {
@NonNull
public ErrorCode getErrorCode() {
// ...
}
@NonNull
public String getMessage() {
// ...
}
@Nullable
public IncomingHttpResponse getHttpResponse() {
// ...
}
}
API yang mengekspos kode error layanan menyediakan subclass khusus API dari
FirebaseException
. Misalnya, semua metode publik di FirebaseAuth
API
dideklarasikan untuk menampilkan instance FirebaseAuthException
. Anda dapat mengakses
kode error layanan dari kelas turunan ini.
public class FirebaseAuthException extends FirebaseException {
@Nullable
public AuthErrorCode getAuthErrorCode() {
// ...
}
}
Python
Di Python, semua error API memperluas exceptions.FirebaseError
. Anda dapat mengakses kode error, pesan error, dan permintaan
dari class dasar ini.
class FirebaseError(Exception):
@property
def code(self):
# ...
@property
def message(self):
# ...
@property
def http_response(self):
# ...
Selain itu, Python Admin SDK menawarkan class turunan terpisah untuk setiap kode error. Kami menyebutnya sebagai class error platform.
class InvalidArgumentError(FirebaseError):
# ...
class NotFoundError(FirebaseError):
# ...
Anda dapat menangkap FirebaseError
dalam kode Anda dan memeriksa code
-nya, atau
lakukan pemeriksaan isinstance()
terhadap class error platform. Atau Anda bisa menulis
kode untuk secara langsung menangkap
tipe {i>error<i} platform tertentu. Pendekatan yang terakhir adalah
akan menghasilkan kode penanganan error yang lebih mudah dibaca.
API yang mengekspos kode error layanan menyediakan subclass platform khusus API
. Misalnya, semua metode publik dalam modul auth
dapat menampilkan
Jenis error khusus API seperti auth.UserNotFoundError
dan
auth.ExpiredIdTokenError
.
class UserNotFoundError(exceptions.NotFoundError):
# …
class ExpiredIdTokenError(exceptions.InvalidArgumentError):
# ...
Go
Go Admin SDK menyediakan paket errorutils
yang berisi serangkaian
yang memungkinkan pengujian kode {i>error<i}.
package errorutils
func IsInvalidArgument(err error) bool {
// ...
}
func IsNotFound(err error) bool {
// ...
}
Pesan error berupa string yang ditampilkan oleh fungsi Error()
dari
{i>error<i}. Respons HTTP opsional bisa diakses dengan memanggil metode
Fungsi errorutils.HTTPResponse()
, yang menampilkan *http.Response
.
Anda dapat meneruskan nil
atau nilai error lainnya ke pemeriksaan error
fungsi dalam paket errorutils
. Menampilkan true
jika argumen input
benar-benar berisi kode error yang dimaksud, dan menampilkan false
untuk semuanya
lain. Fungsi HTTPResponse()
memiliki perilaku yang serupa, kecuali jika menampilkan
nil
, bukan false
.
API yang mengekspos kode error layanan menyediakan pemeriksaan error khusus API
fungsi-fungsi lainnya
dalam paket yang sesuai. Misalnya, paket auth
menyediakan fungsi IsUserNotFound()
dan IsExpiredIDTokenError()
.
.NET
Di .NET, semua error API memperluas FirebaseException
. Anda dapat mengakses
kode error platform, pesan error, dan respons HTTP opsional dari basis ini
.
public class FirebaseException : Exception {
public ErrorCode ErrorCode { get; }
public String Message { get; }
public HttpResponseMessage HttpResponse { get; }
}
API yang mengekspos kode error layanan menyediakan subclass khusus API dari
FirebaseException
. Misalnya, semua metode publik di FirebaseAuth
API
dideklarasikan untuk menampilkan instance FirebaseAuthException
.
Anda dapat mengakses
kode error layanan dari kelas turunan ini.
public class FirebaseAuthException : FirebaseException {
public AuthErrorCode AuthErrorCode { get; }
}
Kode error platform
Kode error bersifat umum di semua layanan Firebase dan Google Cloud Platform. Tabel berikut menguraikan semua kode error platform yang mungkin. Ini adalah stabil, dan diperkirakan tidak akan berubah untuk jangka waktu yang lama.
INVALID_ARGUMENT | Klien menentukan argumen yang tidak valid. |
FAILED_PRECONDITION | Permintaan tidak dapat dijalankan dalam status sistem saat ini, seperti menghapus direktori yang tidak kosong. |
OUT_OF_RANGE | Klien menentukan rentang yang tidak valid. |
UNAUTHENTICATED | Permintaan tidak diautentikasi karena token OAuth tidak ada, tidak valid, atau sudah tidak berlaku. |
PERMISSION_DENIED | Klien tidak memiliki izin yang memadai. Hal ini dapat terjadi karena token OAuth tidak memiliki cakupan yang tepat, klien tidak memiliki izin, atau API belum diaktifkan untuk project klien. |
NOT_FOUND | Resource yang ditentukan tidak ditemukan, atau permintaan ditolak karena alasan yang tidak diungkapkan seperti pemberian izin. |
KONFLIK | Konflik serentak, seperti konflik baca-ubah-tulis. Hanya digunakan oleh beberapa layanan lama. Sebagian besar layanan menggunakan ABORTED atau ALREADY_EXISTS, bukan ini. Lihat dokumentasi khusus layanan untuk mengetahui layanan mana yang harus ditangani dalam kode Anda. |
ABORTED | Konflik serentak, seperti konflik baca-ubah-tulis. |
ALREADY_EXIST | Resource yang klien coba buat sudah ada. |
RESOURCE_EXHAUSTED | Kuota resource telah habis atau mencapai pembatasan kapasitas. |
DIBATALKAN | Permintaan dibatalkan oleh klien. |
DATA_LOSS | Kehilangan data atau kerusakan data yang tidak dapat dipulihkan. Klien harus melaporkan error tersebut kepada pengguna. |
TIDAK DIKETAHUI | Terjadi error yang tidak diketahui pada server. Biasanya berupa {i>bug<i} server.
Kode error ini juga ditetapkan untuk error penguraian respons lokal (unmarshal), dan berbagai error I/O tingkat rendah lainnya yang tidak mudah didiagnosis. |
INTERNAL | Error server internal. Biasanya berupa {i>bug<i} server. |
TIDAK TERSEDIA | Layanan tidak tersedia. Biasanya server sedang mati untuk sementara.
Kode error ini juga ditetapkan untuk error jaringan lokal (koneksi ditolak, tidak ada rute ke host). |
DEADLINE_EXCEEDED | Batas waktu permintaan terlampaui. Hal ini hanya akan terjadi jika pemanggil menetapkan batas waktu yang lebih pendek dari batas waktu default API target (yaitu batas waktu yang diminta tidak cukup bagi server untuk memproses permintaan), dan permintaan tidak selesai dalam batas waktu tersebut.
Kode error ini juga ditetapkan untuk koneksi lokal dan waktu tunggu baca. |
Sebagian besar API hanya dapat menghasilkan subset kode error di atas. Bagaimanapun, Anda tidak diharapkan untuk menangani semua kode kesalahan ini secara eksplisit menerapkan pengendali error Anda. Sebagian besar aplikasi hanya akan tertarik pada 1-2 kode error spesifik dan perlakukan yang lain sebagai generik dan tidak dapat dipulihkan gagal.
Kode error khusus layanan
Firebase Auth
SERTIFIKAT_FETCH_FAILED | Gagal mengambil sertifikat kunci publik yang diperlukan untuk memverifikasi JWT (token ID atau cookie sesi). |
EMAIL_ALREADY_EXISTS | Pengguna dengan email yang diberikan sudah ada. |
EXPIRED_ID_TOKEN | Masa berlaku token ID yang ditentukan untuk verifyIdToken() sudah berakhir.
|
BERAKHIR_SESSION_COOKIE | Cookie sesi yang ditentukan untuk verifySessionCookie() sudah tidak berlaku.
|
DOMAIN LINK_DYNAMIC_INVALID | Domain link dinamis yang disediakan tidak dikonfigurasi atau diberi otorisasi untuk project saat ini. Terkait dengan API link tindakan email. |
TOKEN_ID_TIDAK_VALID | Token ID yang ditentukan untuk verifyIdToken() tidak valid.
|
TIDAK VALID_SESSION_COOKIE | Cookie sesi yang ditentukan untuk verifySessionCookie() tidak valid.
|
PHONE_NUMBER_ALREADY_EXISTS | Pengguna dengan nomor telepon yang diberikan sudah ada. |
{i>REVOKED_ID_TOKEN<i} | Token ID yang ditentukan untuk verifyIdToken() dicabut.
|
{i>REVOKED_SESSION_COOKIE<i} | Cookie sesi yang ditentukan untuk verifySessionCookie() sudah tidak berlaku.
|
UNAUTHORIZED_CONTINUE_URL | Domain continue URL tidak diizinkan. Terkait dengan API link tindakan email. |
PENGGUNA_TIDAK_DITEMUKAN | Tidak ada data pengguna yang ditemukan untuk ID yang diberikan. |
Firebase Cloud Messaging
KETIGA_PARTY_AUTH_ERROR | Sertifikat APN atau kunci API autentikasi push web tidak valid atau tidak ada. |
INVALID_ARGUMENT | Satu atau beberapa argumen yang ditentukan dalam permintaan tidak valid. |
INTERNAL | Error server internal. |
QUOTA_EXCEEDED | Batas pengiriman untuk target pesan terlampaui. |
SENDER_ID_MISMATCH | ID pengirim yang diautentikasi berbeda dengan ID pengirim untuk token pendaftaran. Ini biasanya berarti pengirim dan token pendaftaran target tidak berada dalam project Firebase yang sama. |
TIDAK TERSEDIA | Layanan Cloud Messaging untuk sementara tidak tersedia. |
BELUM TERDAFTAR | Instance aplikasi dibatalkan pendaftarannya dari FCM. Hal ini biasanya berarti bahwa token pendaftaran perangkat yang digunakan sudah tidak valid dan token baru harus digunakan. |
Percobaan ulang otomatis
Admin SDK akan otomatis mencoba kembali error tertentu sebelum menampilkan error tersebut kepada pengguna. Secara umum, jenis error berikut akan dicoba ulang secara transparan:
- Semua error API yang dihasilkan dari respons HTTP 503 (Layanan Tidak Tersedia).
- Beberapa error API yang dihasilkan dari respons HTTP 500 (Error Server Internal).
- Sebagian besar error I/O tingkat rendah (koneksi ditolak, koneksi direset, dll.).
SDK akan mencoba kembali setiap error di atas hingga 5 kali (upaya awal + 4 percobaan ulang) dengan backoff eksponensial. Anda dapat menerapkan percobaan ulang Anda sendiri mekanisme di tingkat aplikasi jika Anda mau, tetapi biasanya tidak tidak diperlukan.
Dukungan Coba Lagi-Setelah
Implementasi Go dan .NET dari Admin SDK dilengkapi dengan dukungan untuk
menangani header Retry-After
HTTP. Artinya, jika respons {i>error<i}
yang dikirim oleh
server backend berisi header Retry-After
standar, SDK akan
ketika mencoba lagi, selama durasi tunggu yang ditentukan tidak terlalu
panjang. Jika header Retry-After
menunjukkan durasi tunggu yang sangat lama, SDK
akan mengabaikan percobaan ulang dan menampilkan error API yang sesuai.
Python Admin SDK saat ini tidak mendukung header Retry-After
, dan
hanya mendukung backoff eksponensial sederhana.
Contoh penanganan error API
Mengimplementasikan pengendali error umum
Dalam kebanyakan kasus yang Anda inginkan adalah pengendali {i>error<i} generik yang menangkap rentang kesalahan untuk mencegah penghentian alur program yang tidak terduga karena Error API. Pengendali {i>error<i} itu biasanya hanya mencatat {i>error<i} untuk tujuan audit, atau panggil beberapa rutinitas penanganan error default lainnya untuk semua API yang ditemukan yang sama. Mereka belum tentu tertarik pada kode {i>error<i} yang berbeda atau alasan yang mungkin menyebabkan error.
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}");
}
Memeriksa kode error
Dalam beberapa kasus, Anda perlu memeriksa kode {i>error<i} yang tepat, dan memanggil penanganan {i>error<i} pada konteks yang berbeda-beda. Pada contoh berikut, memiliki pengendali {i>error<i} yang mencatat log pesan {i>error<i} yang lebih spesifik berdasarkan kode error layanan.
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}");
}
}
Berikut contoh lain saat kami memeriksa kode error tingkat teratas dan layanan:
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}");
}
}
Mengakses respons HTTP
Dalam beberapa kasus yang jarang terjadi, Anda mungkin ingin memeriksa respons kesalahan HTTP yang ditampilkan oleh layanan backend dan melakukan beberapa tindakan penanganan {i>error<i}. Admin SDK mengekspos header dan konten respons error ini. Respons konten biasanya dikembalikan sebagai string atau urutan byte mentah, dan dapat diuraikan menjadi format target apa pun yang diperlukan.
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}");
}
}