Zapisywanie danych

Sposoby oszczędzania danych

PUT Zapisz lub zastąp dane zdefiniowaną ścieżką, np. fireblog/users/user1/<data>
ŁADKA Zaktualizuj niektóre klucze zdefiniowanej ścieżki bez zastępowania wszystkich danych.
POST Dodaj do listy danych w naszej bazie danych Firebase. Za każdym razem, gdy wysyłamy żądanie POST, klient Firebase generuje unikalny klucz, np. fireblog/users/<unique-id>/<data>
USUŃ Usuń dane z określonego odwołania do bazy danych Firebase.

Zapisywanie danych za pomocą PUT

Podstawowa operacja zapisu za pomocą interfejsu API REST to PUT. Aby zademonstrować oszczędzanie danych, utworzymy aplikację do blogowania z postami i użytkownikami. Wszystkie dane dla naszej aplikacji będą przechowywane pod ścieżką „fireblog” pod adresem URL bazy danych Firebase „https://docs-examples.firebaseio.com/fireblog”.

Zacznijmy od zapisania w bazie danych Firebase części danych użytkowników. Każdego użytkownika będziemy przechowywać według unikalnej nazwy użytkownika, a także jego imienia i nazwiska oraz daty urodzenia. Każdy użytkownik będzie miał unikalną nazwę użytkownika, dlatego warto użyć PUT zamiast POST, bo mamy już klucz i nie musimy go tworzyć.

Za pomocą PUT można zapisać w bazie danych Firebase ciąg, liczbę, wartość logiczną, tablicę lub dowolny obiekt JSON. W tym przypadku przekażemy obiekt:

curl -X PUT -d '{
  "alanisawesome": {
    "name": "Alan Turing",
    "birthday": "June 23, 1912"
  }
}' 'https://docs-examples.firebaseio.com/fireblog/users.json'

Gdy obiekt JSON jest zapisany w bazie danych, właściwości obiektu są automatycznie mapowane na lokalizacje podrzędne w sposób zagnieżdżony. Po przejściu do nowo utworzonego węzła zobaczysz wartość „Alan Turing”. Możemy też zapisywać dane bezpośrednio w lokalizacji podrzędnej:

curl -X PUT -d '"Alan Turing"' \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome/name.json'
curl -X PUT -d '"June 23, 1912"' \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome/birthday.json'

W przypadku 2 powyższych przykładów – zapisanie wartości w tym samym czasie co obiektu i zapisanie ich oddzielnie w lokalizacjach podrzędnych – spowoduje to zapisanie tych samych danych w bazie danych Firebase:

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing"
    }
  }
}

Pomyślne żądanie będzie sygnalizowane kodem stanu HTTP 200 OK, a odpowiedź będzie zawierać dane, które zapisaliśmy w bazie danych. Pierwszy przykład aktywuje tylko 1 zdarzenie u klientów, które oglądają dane, a drugi – dwa. Warto zauważyć, że jeśli dane istniały już na ścieżce użytkowników, pierwsze podejście spowodowałoby ich zastąpienie, natomiast druga metoda jedynie zmodyfikuje wartość każdego oddzielnego węzła podrzędnego, a pozostałe węzły podrzędne pozostaną niezmienione. PUT jest odpowiednikiem set() w naszym pakiecie JavaScript SDK.

Aktualizowanie danych za pomocą parametru PATCH

Za pomocą żądania PATCH możemy zaktualizować określone konta podrzędne w danej lokalizacji bez zastępowania obecnych danych. Dodajmy pseudonim Turinga do jego danych użytkownika za pomocą żądania PATCH:

curl -X PATCH -d '{
  "nickname": "Alan The Machine"
}' \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome.json'

Powyższe żądanie zapisze nickname w naszym obiekcie alanisawesome bez usuwania obiektów podrzędnych name i birthday. Pamiętaj, że gdybyśmy zamiast tego wysłali tutaj prośbę o PUT, adresy name i birthday zostałyby usunięte, bo nie były uwzględnione w żądaniu. Dane w naszej bazie danych Firebase wyglądają teraz tak:

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing",
      "nickname": "Alan The Machine"
    }
  }
}

Udane żądanie będzie sygnalizowane kodem stanu HTTP 200 OK, a odpowiedź będzie zawierać zaktualizowane dane zapisane w bazie danych.

Firebase obsługuje też aktualizacje wielościeżkowe. Oznacza to, że PATCH może teraz aktualizować wartości w wielu lokalizacjach w bazie danych Firebase jednocześnie. Jest to zaawansowana funkcja, która pomaga denormalizować dane. Dzięki aktualizacjom na wielu ścieżkach możemy dodawać pseudonimy zarówno Alanowi, jak i Grace jednocześnie:

curl -X PATCH -d '{
  "alanisawesome/nickname": "Alan The Machine",
  "gracehopper/nickname": "Amazing Grace"
}' \
  'https://docs-examples.firebaseio.com/fireblog/users.json'

Po tej aktualizacji zarówno Alan, jak i Grace mają swoje pseudonimy:

{
  "users": {
    "alanisawesome": {
      "date_of_birth": "June 23, 1912",
      "full_name": "Alan Turing",
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "date_of_birth": "December 9, 1906",
      "full_name": "Grace Hopper",
      "nickname": "Amazing Grace"
    }
  }
}

Pamiętaj, że próba zaktualizowania obiektów przez wpisanie obiektów z uwzględnionymi ścieżkami spowoduje inne zachowanie. Zobaczmy, co się stanie, jeśli spróbujemy zaktualizować Grace i Alana w ten sposób:

curl -X PATCH -d '{
  "alanisawesome": {"nickname": "Alan The Machine"},
  "gracehopper": {"nickname": "Amazing Grace"}
}' \
  'https://docs-examples.firebaseio.com/fireblog/users.json'

Powoduje to inne zachowanie, czyli zastąpienie całego węzła /fireblog/users:

{
  "users": {
    "alanisawesome": {
      "nickname": "Alan The Machine"
    },
    "gracehop": {
      "nickname": "Amazing Grace"
    }
  }
}

Aktualizowanie danych za pomocą żądań warunkowych

Do aktualizowania danych zgodnie z obecnym stanem możesz używać żądań warunkowych, które są odpowiednikiem transakcji typu REST. Jeśli na przykład chcesz zwiększyć licznik głosów za i chcesz mieć pewność, że liczba ta będzie dokładnie odzwierciedlała wiele jednoczesnych głosów za, użyj żądania warunkowego, aby zapisać nową wartość w liczniku. Zamiast dwóch zapisów, które powodują zmianę licznika na tę samą liczbę, jedno z nich kończy się niepowodzeniem i możesz spróbować ponownie z nową wartością.
  1. Aby wykonać żądanie warunkowe w lokalizacji, uzyskaj unikalny identyfikator bieżących danych w tej lokalizacji lub identyfikator ETag. Jeśli w tej lokalizacji dane ulegną zmianie, ETag też się zmieni. Możesz zażądać ETag za pomocą dowolnej metody innej niż PATCH. Poniższy przykład korzysta z żądania GET.
    curl -i 'https://test.example.com/posts/12345/upvotes.json' -H 'X-Firebase-ETag: true'
    
    W szczególności wywołanie ETag w nagłówku zwraca ETag z określonej lokalizacji w odpowiedzi HTTP.
    HTTP/1.1 200 OK
    Content-Length: 6
    Content-Type: application/json; charset=utf-8
    Access-Control-Allow-Origin: *
    ETag: [ETAG_VALUE]
    Cache-Control: no-cache
    
    10 // Current value of the data at the specified location
    
  2. Uwzględnij zwrócony ETag w kolejnym żądaniu PUT lub DELETE, aby zaktualizować dane, które dokładnie pasują do tej wartości ETag. Zgodnie z naszym przykładem, aby zaktualizować licznik do wartości 11 lub 1 większej od pierwotnie pobranej wartości wynoszącej 10 i uniknąć żądania, jeśli wartość się nie zgadza, użyj tego kodu:
    curl -iX PUT -d '11' 'https://[PROJECT_ID].firebaseio.com/posts/12345/upvotes.json' -H 'if-match:[ETAG_VALUE]'
    
    Jeśli wartość danych w określonej lokalizacji nadal wynosi 10, ETag w żądaniu PUT jest zgodny, a żądanie się powiedzie, i zapis 11 w bazie danych.
    HTTP/1.1 200 OK
    Content-Length: 6
    Content-Type: application/json; charset=utf-8
    Access-Control-Allow-Origin: *
    Cache-Control: no-cache
    
    11 // New value of the data at the specified location, written by the conditional request
    
    Jeśli lokalizacja nie pasuje już do ETag, co może wystąpić, gdy inny użytkownik zapisze nową wartość w bazie danych, żądanie zakończy się niepowodzeniem bez zapisywania danych lokalizacji. Odpowiedź zwrotna zawiera nową wartość i ETag.
    HTTP/1.1 412 Precondition Failed
    Content-Length: 6
    Content-Type: application/json; charset=utf-8
    Access-Control-Allow-Origin: *
    ETag: [ETAG_VALUE]
    Cache-Control: no-cache
    
    12 // New value of the data at the specified location
    
  3. Jeśli zdecydujesz się jeszcze raz przesłać prośbę, użyj nowych informacji. Baza danych czasu rzeczywistego nie ponawia automatycznie nieudanych żądań warunkowych. Możesz jednak użyć nowej wartości i ETag do utworzenia nowego żądania warunkowego z informacjami zwróconymi w wyniku błędu.

Żądania warunkowe oparte na REST implementują standard HTTP if-match. Różnią się jednak od standardu pod kilkoma względami:

  • Możesz podać tylko jedną wartość ETag dla każdego żądania dopasowania, a nie kilka.
  • Zgodnie z normą standard wskazuje, że znaczniki ETag są zwracane ze wszystkimi żądaniami, natomiast Baza danych czasu rzeczywistego zwraca tylko tagi ETag z żądaniami zawierającymi nagłówek X-Firebase-ETag. Zmniejsza to koszty rozliczeń za żądania standardowe.

Żądania warunkowe mogą też działać wolniej niż typowe żądania REST.

Zapisywanie list danych

Aby wygenerować unikalny klucz oparty na sygnaturze czasowej dla każdego elementu podrzędnego dodanego do odniesienia do bazy danych Firebase, możemy wysłać żądanie POST. W przypadku ścieżki users warto było zdefiniować własne klucze, ponieważ każdy użytkownik ma unikalną nazwę. Gdy jednak użytkownicy dodadzą do aplikacji posty na blogu, użyjemy żądania POST do automatycznego wygenerowania klucza dla każdego posta na blogu:

curl -X POST -d '{
  "author": "alanisawesome",
  "title": "The Turing Machine"
}' 'https://docs-examples.firebaseio.com/fireblog/posts.json'

Nasza ścieżka posts zawiera teraz te dane:

{
  "posts": {
    "-JSOpn9ZC54A4P4RoqVa": {
      "author": "alanisawesome",
      "title": "The Turing Machine"
    }
  }
}

Zwróć uwagę, że klucz -JSOpn9ZC54A4P4RoqVa został dla nas automatycznie wygenerowany, ponieważ wykorzystaliśmy żądanie POST. Pomyślne żądanie będzie sygnalizowane kodem stanu HTTP 200 OK, a odpowiedź będzie zawierać klucz dodawanych nowych danych:

{"name":"-JSOpn9ZC54A4P4RoqVa"}

Usuwanie danych

Aby usunąć dane z bazy danych, możemy wysłać żądanie DELETE z adresem URL ścieżki, z której chcemy usunąć dane. Spowoduje to usunięcie Alana z naszej ścieżki users:

curl -X DELETE \
  'https://docs-examples.firebaseio.com/fireblog/users/alanisawesome.json'

Udane żądanie DELETE będzie sygnalizowane kodem stanu HTTP 200 OK wraz z odpowiedzią zawierającą null JSON.

Parametry URI

Podczas zapisywania danych w bazie danych interfejs API REST akceptuje te parametry identyfikatora URI:

uwierzytelnienie

Parametr żądania auth umożliwia dostęp do danych chronionych przez reguły zabezpieczeń Bazy danych czasu rzeczywistego Firebase i jest obsługiwany w przypadku wszystkich typów żądań. Może to być tajny klucz aplikacji Firebase lub token uwierzytelniania, które omówimy w sekcji dotyczącej autoryzacji użytkownika. W poniższym przykładzie wysyłamy żądanie POST z parametrem auth, gdzie CREDENTIAL to tajny klucz aplikacji Firebase lub token uwierzytelniania:

curl -X POST -d '{"Authenticated POST request"}' \
  'https://docs-examples.firebaseio.com/auth-example.json?auth=CREDENTIAL'

wyświetlić

Parametr print pozwala nam określić format odpowiedzi z bazy danych. Dodanie do naszego żądania atrybutu print=pretty spowoduje zwrócenie danych w formacie zrozumiałym dla człowieka. print=pretty jest obsługiwany w przypadku żądań GET, PUT, POST, PATCH i DELETE.

Aby wyłączyć dane wyjściowe z serwera podczas zapisywania danych, możemy dodać do żądania print=silent. Jeśli żądanie zostanie zrealizowane, odpowiedź będzie pusta i wskazana przez kod stanu HTTP 204 No Content. Obiekt print=silent jest obsługiwany przez żądania GET, PUT, POST i PATCH.

Zapisywanie wartości serwera

Wartości serwera można zapisywać w lokalizacji za pomocą wartości zmiennej, która jest obiektem z jednym kluczem ".sv". Wartość tego klucza to typ wartości serwera, którą chcemy ustawić. Aby na przykład ustawić sygnaturę czasową tworzenia użytkownika, możemy wykonać te czynności:

curl -X PUT -d '{".sv": "timestamp"}' \
  'https://docs-examples.firebaseio.com/alanisawesome/createdAt.json'

Jedyną obsługiwaną wartością serwera jest "timestamp", który stanowi czas od początku epoki systemu UNIX (w milisekundach).

Poprawianie wydajności zapisu

Jeśli zapisujemy w bazie danych duże ilości danych, możemy użyć parametru print=silent, aby zwiększyć wydajność zapisu i zmniejszyć wykorzystanie przepustowości. W normalnym trybie zapisu serwer odpowiada, przesyłając zapisane dane JSON. Jeśli podasz print=silent, serwer natychmiast zamknie połączenie po odebraniu danych, co zmniejszy wykorzystanie przepustowości.

Jeśli otrzymujemy wiele żądań do bazy danych, możemy ponownie użyć połączenia HTTPS, wysyłając żądanie Keep-Alive w nagłówku HTTP.

Warunki błędu

Interfejs API REST zwraca kody błędów w tych sytuacjach:

Kody stanu HTTP
400 Nieprawidłowe żądanie

Jeden z tych warunków błędu:

  • Nie udało się przeanalizować danych PUT ani POST.
  • Brak danych: PUT lub POST.
  • Żądanie ma na celu próbę PUT lub POST danych, które są zbyt duże.
  • Wywołanie interfejsu API REST zawiera w ścieżce nieprawidłowe nazwy podrzędne.
  • Ścieżka wywołania interfejsu API REST jest za długa.
  • Żądanie zawiera nierozpoznaną wartość serwera.
  • Indeks dla zapytania nie jest zdefiniowany w regułach zabezpieczeń Bazy danych czasu rzeczywistego Firebase.
  • Żądanie nie obsługuje jednego ze podanych parametrów zapytania.
  • Żądanie łączy parametry zapytania z płytkim żądaniem GET.
401 Brak autoryzacji

Jeden z tych warunków błędu:

404 Nie znaleziono Nie znaleziono określonej bazy danych Firebase.
500 – wewnętrzny błąd serwera Serwer zwrócił błąd. Więcej informacji znajdziesz w komunikacie o błędzie.
503 Usługa niedostępna Podana Baza danych czasu rzeczywistego Firebase jest tymczasowo niedostępna, co oznacza, że nie podjęto próby wykonania żądania.

Zabezpieczanie danych

Firebase oferuje język zabezpieczeń umożliwiający określanie, którzy użytkownicy mają uprawnienia do odczytu i zapisu w różnych węzłach naszych danych. Więcej informacji na ten temat znajdziesz w artykule Reguły zabezpieczeń Bazy danych czasu rzeczywistego.

Omówiliśmy już zapisywanie danych. W następnej sekcji dowiesz się, jak pobierać dane z bazy danych Firebase za pomocą interfejsu API REST.