Błędy pakietu Admin SDK dzielą się na dwie kategorie:
- Błędy programistyczne: Są to błędy programowania i konfiguracji w aplikacji użytkownika. Występują one najczęściej z powodu nieprawidłowego użycia pakietu SDK (takiego jak przekazanie wartości
null
do metody, która nie akceptuje wartościnull
) i innych błędów konfiguracyjnych na poziomie projektu Firebase lub pakietu SDK (brakujące dane uwierzytelniające, nieprawidłowy ciąg identyfikatora projektu itp. NA). - Błędy API: obejmują one różne możliwe do naprawienia błędy występujące w implementacji SDK, wszystkie błędy pochodzące z usług zaplecza Firebase i inne błędy przejściowe (takie jak przekroczenia limitu czasu), które mogą wystąpić podczas wykonywania wywołań RPC.
Pakiet Admin SDK sygnalizuje błędy programowania , zgłaszając błąd natywny dla danej platformy.
- Java: Zgłasza wystąpienia
IllegalArgumentException
,NullPointerException
lub podobnego wbudowanego typu błędu środowiska wykonawczego. - Python: wywołuje instancje
ValueError
,TypeError
lub innego wbudowanego typu błędu. - Idź: Zwraca ogólny błąd.
- .NET: Zgłasza wystąpienia
ArgumentException
,ArgumentNullException
lub podobnego wbudowanego typu błędu.
W większości sytuacji nie należy bezpośrednio zajmować się błędami programistycznymi. Zamiast tego powinieneś naprawić swój kod i konfigurację, aby całkowicie uniknąć błędów programistycznych. Rozważ następujący fragment kodu Java:
String uid = getUserInput();
UserRecord user = FirebaseAuth.getInstance().getUser(uid);
Jeśli metoda getUserInput()
zwróci ciągi null
lub puste, funkcja API FirebaseAuth.getUser()
zgłosi wyjątek IllegalArgumentException
. Zamiast zajmować się tym bezpośrednio, możesz złagodzić problem, upewniając się, że metoda getUserInput()
nigdy nie zwraca nieprawidłowego ciągu UID. Jeśli nie jest to możliwe, zaimplementuj niezbędne sprawdzanie argumentów we własnym kodzie w następujący sposób:
String uid = getUserInput();
if (Strings.isNullOrEmpty(uid)) {
log.warn("UID must not be null or empty");
return;
}
UserRecord user = FirebaseAuth.getInstance().getUser(uid);
Z zasady nigdy nie należy ponawiać błędów programistycznych. Zezwolenie na niezawodną semantykę błędów programistycznych jest często najlepszym sposobem działania, ponieważ ujawnia błędy programistyczne i konfiguracyjne podczas programowania, gdzie można je szybko naprawić. W tym kontekście odporność na awarie może oznaczać pozwolenie błędom na propagację do globalnego modułu obsługi błędów w aplikacji lub po prostu rejestrowanie ich w celach audytu, po czym następuje zakończenie bieżącego przepływu wykonywania (aplikacja nie powinna ulegać awarii). Ogólnie rzecz biorąc, postępuj zgodnie z najlepszymi praktykami obsługi błędów swojego języka programowania i struktury aplikacji. Samo to często wystarcza, aby poprawnie uporać się z tą klasą błędów.
Zwykle większość wysiłków związanych z obsługą błędów skupia się na obsłudze błędów interfejsu API . Niektóre z tych błędów można naprawić, na przykład błędy wynikające z tymczasowo niedostępnej usługi, a niektóre można nawet przewidzieć podczas normalnego wykonywania programu, na przykład podczas wykrycia nieprawidłowych lub wygasłych tokenów identyfikacyjnych. W pozostałej części tego przewodnika opisano, w jaki sposób pakiet Admin SDK reprezentuje takie błędy interfejsu API oraz różne dostępne opcje ich obsługi.
Struktura błędu API
Błąd API składa się z następujących elementów:
- Kod błędu
- Komunikat o błędzie
- Kod błędu serwisowego (opcjonalnie)
- Odpowiedź HTTP (opcjonalnie)
Gwarantujemy, że każdy błąd API będzie zawierał kod błędu i komunikat o błędzie. Niektóre błędy interfejsu API zawierają również kod błędu usługi specyficzny dla interfejsu API, który wygenerował błąd. Na przykład niektóre błędy generowane przez interfejs API Firebase Auth zawierają kod błędu usługi specyficzny dla Firebase Auth. Jeśli błąd był wynikiem odpowiedzi na błąd HTTP z usługi zaplecza, błąd interfejsu API zawiera również odpowiednią odpowiedź HTTP. Można to wykorzystać do sprawdzenia dokładnych nagłówków i zawartości oryginalnej odpowiedzi, co jest przydatne do debugowania, rejestrowania lub implementowania bardziej wyrafinowanej logiki obsługi błędów.
Wszystkie implementacje Admin SDK z wyjątkiem Node.js udostępniają interfejsy API umożliwiające dostęp do powyższych składników błędów interfejsu API.
Typy błędów i interfejsy API według języka
Jawa
W Javie wszystkie błędy API rozszerzają klasę FirebaseException
. Z tej klasy bazowej można uzyskać dostęp do kodu błędu, komunikatu o błędzie i opcjonalnej odpowiedzi HTTP.
public class FirebaseException extends Exception {
@NonNull
public ErrorCode getErrorCode() {
// ...
}
@NonNull
public String getMessage() {
// ...
}
@Nullable
public IncomingHttpResponse getHttpResponse() {
// ...
}
}
Interfejsy API, które ujawniają kody błędów usług, udostępniają specyficzne dla interfejsu API podklasy FirebaseException
. Na przykład wszystkie metody publiczne w interfejsie API FirebaseAuth
są deklarowane jako zgłaszające instancje FirebaseAuthException
. Dostęp do kodu błędu usługi można uzyskać z tej klasy pochodnej.
public class FirebaseAuthException extends FirebaseException {
@Nullable
public AuthErrorCode getAuthErrorCode() {
// ...
}
}
Pyton
W Pythonie wszystkie błędy API rozszerzają klasę exceptions.FirebaseError
. Z tej klasy bazowej można uzyskać dostęp do kodu błędu, komunikatu o błędzie i opcjonalnej odpowiedzi HTTP.
class FirebaseError(Exception):
@property
def code(self):
# ...
@property
def message(self):
# ...
@property
def http_response(self):
# ...
Co więcej, pakiet Python Admin SDK oferuje osobne klasy pochodne dla każdego kodu błędu. Nazywamy je klasami błędów platformy .
class InvalidArgumentError(FirebaseError):
# ...
class NotFoundError(FirebaseError):
# ...
Możesz albo złapać FirebaseError
w swoim kodzie i sprawdzić jego code
, albo wykonać kontrolę isinstance()
pod kątem klasy błędów platformy. Możesz też napisać kod, aby bezpośrednio wychwytywać określone typy błędów platformy. To drugie podejście prawdopodobnie spowoduje bardziej czytelny kod obsługi błędów.
Interfejsy API ujawniające kody błędów usług udostępniają specyficzne dla interfejsu API podklasy klas błędów platformy. Na przykład wszystkie metody publiczne w module auth
mogą zgłaszać typy błędów specyficzne dla interfejsu API, takie jak auth.UserNotFoundError
i auth.ExpiredIdTokenError
.
class UserNotFoundError(exceptions.NotFoundError):
# …
class ExpiredIdTokenError(exceptions.InvalidArgumentError):
# ...
Iść
Pakiet Go Admin SDK udostępnia pakiet errorutils
, który zawiera szereg funkcji umożliwiających testowanie kodów błędów.
package errorutils
func IsInvalidArgument(err error) bool {
// ...
}
func IsNotFound(err error) bool {
// ...
}
Komunikat o błędzie to po prostu ciąg znaków zwrócony przez funkcję Error()
błędu. Dostęp do opcjonalnej odpowiedzi HTTP można uzyskać, wywołując funkcję errorutils.HTTPResponse()
, która zwraca wartość *http.Response
.
Bezpieczne jest przekazanie nil
lub dowolnej innej wartości błędu do funkcji sprawdzania błędów w pakiecie errorutils
. Zwracają true
, jeśli argument wejściowy faktycznie zawiera kod błędu, o którym mowa, i zwracają false
w przypadku wszystkich pozostałych parametrów. Funkcja HTTPResponse()
zachowuje się podobnie, z tą różnicą, że zwraca nil
zamiast false
.
Interfejsy API ujawniające kody błędów usług udostępniają w odpowiednich pakietach funkcje sprawdzania błędów specyficzne dla interfejsu API. Na przykład pakiet auth
udostępnia funkcje IsUserNotFound()
i IsExpiredIDTokenError()
.
.INTERNET
W .NET wszystkie błędy API rozszerzają klasę FirebaseException
. Z tej klasy bazowej można uzyskać dostęp do kodu błędu platformy, komunikatu o błędzie i opcjonalnej odpowiedzi HTTP.
public class FirebaseException : Exception {
public ErrorCode ErrorCode { get; }
public String Message { get; }
public HttpResponseMessage HttpResponse { get; }
}
Interfejsy API, które ujawniają kody błędów usług, udostępniają specyficzne dla interfejsu API podklasy FirebaseException
. Na przykład wszystkie metody publiczne w interfejsie API FirebaseAuth
są zadeklarowane w celu zgłaszania wystąpień FirebaseAuthException
. Dostęp do kodu błędu usługi można uzyskać z tej klasy pochodnej.
public class FirebaseAuthException : FirebaseException {
public AuthErrorCode AuthErrorCode { get; }
}
Kody błędów platformy
Kody błędów są wspólne dla wszystkich usług Firebase i Google Cloud Platform. W poniższej tabeli przedstawiono wszystkie możliwe kody błędów platformy. Jest to lista stabilna i oczekuje się, że pozostanie niezmieniona przez długi okres.
BŁĘDNY ARGUMENT | Klient podał nieprawidłowy argument. |
NIEUDANY_WARUNEK WSTĘPNY | Żądania nie można wykonać w bieżącym stanie systemu, na przykład w celu usunięcia niepustego katalogu. |
POZA ZAKRESEM | Klient podał nieprawidłowy zakres. |
NIEUWIERZYTELNIONE | Żądanie nie zostało uwierzytelnione z powodu brakującego, nieprawidłowego lub wygasłego tokena OAuth. |
PERMISSION_DENIED | Klient nie ma wystarczających uprawnień. Może się tak zdarzyć, ponieważ token OAuth nie ma odpowiednich zakresów, klient nie ma uprawnień lub nie włączono interfejsu API dla projektu klienta. |
NIE ZNALEZIONO | Nie znaleziono określonego zasobu lub żądanie zostało odrzucone z nieujawnionych powodów, takich jak umieszczenie na białej liście. |
KONFLIKT | Konflikt współbieżności, taki jak konflikt odczytu, modyfikacji i zapisu. Używany tylko przez kilka starszych usług. Większość usług używa zamiast tego ABORTED lub ALREADY_EXISTS. Zapoznaj się z dokumentacją specyficzną dla usługi, aby zobaczyć, który z nich ma być obsługiwany w kodzie. |
NIEDONOSZONY | Konflikt współbieżności, taki jak konflikt odczytu, modyfikacji i zapisu. |
JUŻ ISTNIEJE | Zasób, który klient próbował utworzyć, już istnieje. |
ZASOBY_WYKOŃCZONE | Albo wyczerpał się limit zasobów, albo osiągnięto limit szybkości. |
ODWOŁANY | Zapytanie anulowane przez klienta. |
UTRATA DANYCH | Nieodwracalna utrata lub uszkodzenie danych. Klient powinien zgłosić błąd użytkownikowi. |
NIEZNANY | Nieznany błąd serwera. Typowy błąd serwera. Ten kod błędu jest również przypisany do błędów analizy lokalnej odpowiedzi (unmarshal) i szerokiej gamy innych błędów we/wy niskiego poziomu, które nie są łatwe do zdiagnozowania. |
WEWNĘTRZNY | Wewnętrzny błąd serwera. Typowy błąd serwera. |
NIEDOSTĘPNE | Serwis niedostępny. Zazwyczaj serwer jest chwilowo wyłączony. Ten kod błędu jest również przypisany do błędów sieci lokalnej (odmowa połączenia, brak trasy do hosta). |
DEADLINE_EXCEEDED | Przekroczono termin składania wniosków. Stanie się tak tylko wtedy, gdy wywołujący ustawi termin krótszy niż domyślny termin docelowego API (tj. żądany termin nie jest wystarczający, aby serwer mógł przetworzyć żądanie), a żądanie nie zostało zakończone w wyznaczonym terminie. Ten kod błędu jest również przypisany do limitów czasu połączenia lokalnego i odczytu. |
Większość interfejsów API może skutkować jedynie wyświetleniem podzbioru powyższych kodów błędów. W każdym razie nie oczekuje się, że będziesz jawnie obsługiwał wszystkie te kody błędów podczas implementowania programów obsługi błędów. Większość aplikacji byłaby zainteresowana tylko 1-2 konkretnymi kodami błędów, a wszystko inne traktowałaby jako ogólną, nieodwracalną awarię.
Kody błędów specyficzne dla usługi
Uwierzytelnianie Firebase
CERTIFICATE_FETCH_FAILED | Nie udało się pobrać certyfikatów klucza publicznego wymaganych do zweryfikowania tokenu identyfikacyjnego lub pliku cookie sesji. |
EMAIL JUŻ ISTNIEJE | Użytkownik z podanym adresem e-mail już istnieje. |
EXPIRED_ID_TOKEN | Token identyfikatora określony w funkcji verifyIdToken() wygasł. |
EXPIRED_SESSION_COOKIE | Sesyjny plik cookie określony w verifySessionCookie() ii wygasł. |
INVALID_DYNAMIC_LINK_DOMAIN | Podana domena łącza dynamicznego nie jest skonfigurowana lub autoryzowana dla bieżącego projektu. Powiązane z interfejsami API linków do akcji e-mail. |
INVALID_ID_TOKEN | Token identyfikatora określony w funkcji verifyIdToken() jest nieprawidłowy. |
INVALID_SESSION_COOKIE | Plik cookie sesji określony w funkcji verifySessionCookie() jest nieprawidłowy. |
PHONE_NUMBER_ALREADY_EXISTS | Użytkownik o podanym numerze telefonu już istnieje. |
REVOKED_ID_TOKEN | Token identyfikacyjny określony w funkcji verifyIdToken() został unieważniony. |
REVOKED_SESSION_COOKIE | Plik cookie sesji określony w funkcji verifySessionCookie() wygasł. |
UNAUTHORIZED_CONTINUE_URL | Domena adresu URL kontynuacji nie znajduje się na białej liście. Powiązane z interfejsami API linków do akcji e-mail. |
UŻYTKOWNIK NIE ZNALEZIONY | Nie znaleziono rekordu użytkownika dla podanego identyfikatora. |
Wiadomości w chmurze Firebase
THIRD_PARTY_AUTH_ERROR | Certyfikat APNs lub klucz API uwierzytelniania web push jest nieprawidłowy lub go brak. |
BŁĘDNY ARGUMENT | Co najmniej jeden argument określony w żądaniu jest nieprawidłowy. |
WEWNĘTRZNY | Wewnętrzny błąd serwera. |
MOZLIWA ILOŚĆ PRZEKROCZONA | Przekroczono limit wysyłania dla celu wiadomości. |
SENDER_ID_MISMATCH | Uwierzytelniony identyfikator nadawcy różni się od identyfikatora nadawcy w tokenie rejestracyjnym. Zwykle oznacza to, że nadawca i docelowy token rejestracji nie znajdują się w tym samym projekcie Firebase. |
NIEDOSTĘPNE | Usługa Cloud Messaging jest tymczasowo niedostępna. |
NIEZAREJESTROWANY | Instancja aplikacji została wyrejestrowana z FCM. Zwykle oznacza to, że użyty token rejestracyjny urządzenia nie jest już ważny i należy użyć nowego. |
Automatyczne ponowne próby
Pakiet Admin SDK automatycznie ponawia próbę wystąpienia określonych błędów, zanim udostępni je użytkownikom. Ogólnie rzecz biorąc, w przejrzysty sposób ponawiane są następujące typy błędów:
- Wszystkie błędy API wynikające z odpowiedzi HTTP 503 (Usługa niedostępna).
- Niektóre błędy API wynikające z odpowiedzi HTTP 500 (wewnętrzny błąd serwera).
- Większość błędów we/wy niskiego poziomu (odmowa połączenia, reset połączenia itp.).
Zestaw SDK ponawia próbę każdego z powyższych błędów maksymalnie 5 razy (pierwotna próba + 4 próby) z wykładniczym wycofywaniem. Jeśli chcesz, możesz zaimplementować własne mechanizmy ponawiania prób na poziomie aplikacji, ale zazwyczaj nie jest to wymagane.
Spróbuj ponownie – po wsparciu
Implementacje Go i .NET pakietu Admin SDK obsługują obsługę nagłówka HTTP Retry-After
. Oznacza to, że jeśli odpowiedź na błąd wysłana przez serwery zaplecza zawiera standardowy nagłówek Retry-After
, zestaw SDK uwzględni go przy ponawianiu próby, o ile określony czas oczekiwania nie będzie zbyt długi. Jeśli nagłówek Retry-After
wskazuje bardzo długi czas oczekiwania, zestaw SDK pominie ponowne próby i zgłosi odpowiedni błąd interfejsu API.
Pakiet SDK administratora języka Python nie obsługuje obecnie nagłówka Retry-After
i obsługuje jedynie proste wycofywanie wykładnicze.
Przykłady obsługi błędów API
Implementacja ogólnej procedury obsługi błędów
W większości przypadków potrzebna jest ogólna obsługa błędów, która wychwytuje szeroki zakres błędów, aby zapobiec nieoczekiwanemu zakończeniu działania programu z powodu błędu API. Takie programy obsługi błędów zwykle po prostu rejestrują błędy w celach kontrolnych lub wywołują inną domyślną procedurę obsługi błędów dla wszystkich napotkanych błędów interfejsu API. Niekoniecznie interesują ich różne kody błędów lub przyczyny, które mogły spowodować błąd.
Jawa
try {
FirebaseToken token = FirebaseAuth.getInstance().verifyIdToken(idToken);
performPrivilegedOperation(token.getUid());
} catch (FirebaseAuthException ex) {
System.err.println("Failed to verify ID token: " + ex.getMessage());
}
Pyton
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)
.Internet
try
{
var token = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(idToken);
PerformPrivilegedOperation(token.getUid());
}
catch (FirebaseAuthException ex)
{
Conole.WriteLine($"Failed to verify ID token: {ex.Message}");
}
Sprawdzanie kodów błędów
W niektórych przypadkach warto sprawdzić dokładne kody błędów i wywołać różne kontekstowe procedury obsługi błędów. W poniższym przykładzie mamy procedurę obsługi błędów, która rejestruje bardziej szczegółowe komunikaty o błędach w oparciu o kod błędu usługi.
Jawa
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());
}
}
Pyton
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)
.Internet
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}");
}
}
Oto kolejny przykład, w którym sprawdzamy kody błędów najwyższego poziomu i kody usług:
Jawa
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());
}
}
Pyton
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
}
.Internet
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}");
}
}
Dostęp do odpowiedzi HTTP
W niektórych rzadkich przypadkach możesz chcieć sprawdzić odpowiedź na błąd HTTP zwróconą przez usługę zaplecza i wykonać na niej pewne działania związane z obsługą błędów. Pakiet Admin SDK udostępnia zarówno nagłówki, jak i treść tych odpowiedzi na błędy. Treść odpowiedzi jest zwykle zwracana jako ciąg znaków lub surowa sekwencja bajtów i może zostać przeanalizowana do dowolnego niezbędnego formatu docelowego.
Jawa
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());
}
}
Pyton
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
}
.Internet
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}");
}
}