Сохранение данных

Способы экономии трафика

PUT Запись или замена данных по определенному пути, например fireblog/users/user1/<data>.
PATCH Обновить некоторые ключи для определенного пути, не заменяя все данные.
POST Добавление в список данных в базе данных Firebase. Каждый раз, когда мы отправляем запрос POST, клиент Firebase генерирует уникальный ключ, например fireblog/users/<unique-id>/<data>.
УДАЛИТЬ Удаляет данные из указанной базы данных Firebase.

Запись данных с помощью PUT

Базовая операция записи через REST API – PUT. Чтобы продемонстрировать сохранение данных, мы создадим приложение для ведения блогов с публикациями и пользователями. Все данные для нашего приложения будут храниться по пути fireblog в URL базы данных Firebase https://docs-examples.firebaseio.com/fireblog.

Для начала сохраним некоторые пользовательские данные в базе данных Firebase. Мы будем хранить данные каждого пользователя по уникальному имени пользователя, а также его полное имя и дату рождения. Поскольку у каждого пользователя будет уникальное имя, здесь имеет смысл использовать PUT вместо POST, так как у нас уже есть ключ и нам не нужно его создавать.

С помощью PUT можно записать в базу данных Firebase строку, число, логическое значение, массив или любой объект JSON. В этом случае мы передадим ему объект:

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

Когда объект JSON сохраняется в базе данных, его свойства автоматически сопоставляются с дочерними местоположениями вложенным образом. Если мы перейдем к только что созданному узлу, то увидим значение "Alan Turing". Мы также можем сохранять данные непосредственно в дочернем местоположении:

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'

В обоих случаях (при записи значения одновременно с объектом и при записи в дочерние местоположения) в базу данных Firebase будут сохранены одни и те же данные:

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

При успешном выполнении запроса будет получен код статуса HTTP 200 OK, а ответ будет содержать данные, которые мы записали в базу данных. В первом примере будет активировано только одно событие на клиентах, которые отслеживают данные, а во втором – два. Важно отметить, что если данные уже существовали по пути users, первый подход перезапишет их, а второй метод изменит только значение каждого отдельного дочернего узла, оставив другие дочерние узлы без изменений. PUT эквивалентно set() в нашем JavaScript SDK.

Обновление данных с помощью PATCH

С помощью запроса PATCH можно обновить определенные дочерние узлы в местоположении, не перезаписывая существующие данные. Добавим псевдоним Тьюринга в его пользовательские данные с помощью запроса PATCH:

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

Приведенный выше запрос запишет nickname в объект alanisawesome, не удаляя дочерние элементы name и birthday. Обратите внимание, что если бы мы отправили запрос PUT, то name и birthday были бы удалены, поскольку они не были включены в запрос. Данные в базе данных Firebase теперь выглядят следующим образом:

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

В случае успешного выполнения запроса будет возвращен код статуса HTTP 200 OK, а ответ будет содержать обновленные данные, записанные в базу данных.

Firebase также поддерживает многопутевые обновления. Это означает, что PATCH теперь может одновременно обновлять значения в нескольких местах базы данных Firebase. Эта мощная функция позволяет денормализовать данные. Используя многопутевые обновления, мы можем одновременно добавить псевдонимы Алану и Грейс:

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

После этого обновления у Алана и Грейс появились псевдонимы:

{
  "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"
    }
  }
}

Обратите внимание, что попытка обновить объекты путем записи объектов с включенными путями приведет к другому поведению. Давайте посмотрим, что произойдет, если мы вместо этого попытаемся обновить Грейс и Алана следующим образом:

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

Это приводит к другому поведению, а именно к перезаписи всего узла /fireblog/users:

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

Обновление данных с помощью условных запросов

Вы можете использовать условные запросы, эквивалентные транзакциям REST, для обновления данных в соответствии с их существующим состоянием. Например, если вы хотите увеличить счетчик голосов и убедиться, что он точно отражает несколько одновременных голосов, используйте условный запрос, чтобы записать новое значение в счетчик. Вместо двух записей, которые изменяют счетчик на одно и то же число, один из запросов на запись завершается ошибкой, и вы можете повторить запрос с новым значением.
  1. Чтобы выполнить условный запрос в определенном местоположении, получите уникальный идентификатор текущих данных в этом местоположении или ETag. Если данные в этом месте изменяются, ETag также изменяется. Вы можете запросить ETag с помощью любого метода, кроме PATCH. В примере ниже используется запрос GET.
    curl -i 'https://test.example.com/posts/12345/upvotes.json' -H 'X-Firebase-ETag: true'
    Вызов ETag в заголовке возвращает ETag указанного местоположения в 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. Включите возвращенный ETag в следующий запрос PUT или DELETE, чтобы обновить данные, которые соответствуют этому значению ETag. В нашем примере, чтобы обновить счетчик до 11 (на 1 больше, чем исходное полученное значение 10) и отклонить запрос, если значение больше не совпадает, используйте следующий код:
    curl -iX PUT -d '11' 'https://[PROJECT_ID].firebaseio.com/posts/12345/upvotes.json' -H 'if-match:[ETAG_VALUE]'
    Если значение данных в указанном местоположении по-прежнему равно 10, ETag в запросе PUT совпадает, запрос выполняется успешно и в базу данных записывается 11.
    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
    Если местоположение больше не соответствует ETag (например, если другой пользователь записал новое значение в базу данных), запрос не выполняется и запись в местоположение не производится. Ответ содержит новое значение и 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. Используйте новую информацию, если решите повторить запрос. Realtime Database не повторяет автоматически условные запросы, которые не удалось выполнить. Однако вы можете использовать новое значение и ETag, чтобы создать новый условный запрос с информацией, возвращенной в ответе об ошибке.

Условные запросы на основе REST реализуют стандарт HTTP if-match. Однако у них есть следующие отличия:

  • Для каждого запроса if-match можно указать только одно значение ETag.
  • Хотя стандарт предполагает, что ETag должны возвращаться со всеми запросами, Realtime Database возвращает ETag только с запросами, включающими заголовок X-Firebase-ETag. Это снижает сумму к оплате за стандартные запросы.

Условные запросы также могут выполняться медленнее, чем обычные запросы REST.

Сохранение списков данных

Чтобы создать уникальный ключ на основе временной метки для каждого дочернего элемента, добавленного в ссылку на базу данных Firebase, можно отправить запрос POST. Для пути users имеет смысл определить собственные ключи, поскольку у каждого пользователя уникальное имя. Но когда пользователи добавляют записи в блог, мы используем запрос POST, чтобы автоматически сгенерировать ключ для каждой записи:

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

Теперь путь posts содержит следующие данные:

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

Обратите внимание, что ключ -JSOpn9ZC54A4P4RoqVa был сгенерирован автоматически, поскольку мы использовали запрос POST. Если запрос выполнен успешно, будет возвращен код статуса HTTP 200 OK, а ответ будет содержать ключ добавленных данных:

{"name":"-JSOpn9ZC54A4P4RoqVa"}

Удаление данных

Чтобы удалить данные из базы данных, мы можем отправить запрос DELETE с URL пути, из которого мы хотим удалить данные. Следующий запрос удалит пользователя Alan из пути users:

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

При успешном выполнении запроса DELETE будет получен код статуса HTTP 200 OK с ответом, содержащим JSON null.

Параметры URI

При записи данных в базу данных REST API принимает следующие параметры URI:

аутентификация

Параметр запроса auth позволяет получить доступ к данным, защищенным Firebase Realtime Database Security Rules, и поддерживается всеми типами запросов. В качестве аргумента можно использовать секретный ключ приложения Firebase или токен аутентификации, о котором мы поговорим в разделе об авторизации пользователей. В примере ниже мы отправляем запрос POST с параметром auth, где CREDENTIAL – это секретный ключ приложения Firebase или токен аутентификации:

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

печать

Параметр print позволяет указать формат ответа из базы данных. Добавление print=pretty в запрос вернет данные в человекочитаемом формате. print=pretty поддерживается запросами GET, PUT, POST, PATCH и DELETE.

Чтобы подавить вывод с сервера при записи данных, мы можем добавить в запрос print=silent. Если запрос будет выполнен успешно, то ответ будет пустым и будет сопровождаться кодом статуса HTTP 204 No Content. Параметр print=silent поддерживается в запросах GET, PUT, POST и PATCH.

Запись значений сервера

Значения сервера можно записать в местоположение, используя значение-плейсхолдер, которое является объектом с одним ключом ".sv". Значение для этого ключа – это тип значения сервера, которое мы хотим задать. Например, чтобы установить временную метку при создании пользователя, можно сделать следующее:

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

"timestamp" – единственное поддерживаемое значение сервера, которое представляет собой время в миллисекундах, прошедшее с начала эпохи UNIX.

Как повысить эффективность записи

Если мы записываем в базу данных большие объемы данных, мы можем использовать параметр print=silent, чтобы повысить производительность записи и снизить использование пропускной способности. При обычной записи сервер отвечает данными JSON, которые были записаны. Если указано значение print=silent, сервер сразу же закрывает соединение после получения данных, что позволяет снизить нагрузку на сеть.

Если мы отправляем много запросов к базе данных, то можем повторно использовать HTTPS-подключение, отправив запрос Keep-Alive в HTTP-заголовке.

Условия возникновения ошибок

REST API возвращает коды ошибок в следующих случаях:

Коды статуса HTTP
400 Недопустимый запрос

Одна из следующих ошибок:

  • Не удалось проанализировать данные PUT или POST.
  • Отсутствуют данные PUT или POST.
  • Запрос пытается PUT или POST слишком большой объем данных.
  • Вызов REST API содержит недопустимые дочерние имена в пути.
  • Путь вызова REST API слишком длинный.
  • Запрос содержит нераспознанное значение сервера.
  • Индекс для запроса не определен в файле Firebase Realtime Database Security Rules.
  • Запрос не поддерживает один из указанных параметров.
  • В запросе смешаны параметры запроса и поверхностный запрос GET.
401: доступ запрещен

Одна из следующих ошибок:

  • Срок действия токена аутентификации истек.
  • Токен аутентификации, используемый в запросе, недействителен.
  • Не удалось выполнить аутентификацию с помощью access_token.
  • Запрос нарушает ваши Firebase Realtime Database Security Rules.
404 Не найдено Указанная база данных Firebase не найдена.
500 Внутренняя ошибка сервера Сервер вернул ошибку. Подробные сведения приведены в сообщении об ошибке.
503 Service Unavailable Указанная база данных Firebase Realtime Database временно недоступна, поэтому запрос не был выполнен.

Защита данных

В Firebase есть язык безопасности, который позволяет нам определять, у каких пользователей есть доступ на чтение и запись к различным узлам наших данных. Подробнее об этом рассказывается в Realtime Database Security Rules.

Теперь, когда мы рассмотрели сохранение данных, в следующем разделе мы можем узнать, как получить наши данные из базы данных Firebase через REST API.