Спецификация протокола для https.onCall

Триггер https.onCall для Cloud Functions — это триггер HTTPS с определенным форматом запроса и ответа. В этом разделе представлена ​​спецификация форматов запросов и ответов HTTPS, используемых клиентскими пакетами SDK для реализации API. Эта информация может быть вам полезна, если ваши требования не могут быть выполнены с помощью платформ Android, Apple или веб-SDK.

Формат запроса: заголовки

HTTP-запрос к вызываемой конечной точке триггера должен быть POST со следующими заголовками:

  • Требуется: Content-Type: application/json
    • Необязательный ; charset=utf-8 разрешено.
  • Необязательно: Authorization: Bearer <token>
    • Токен идентификатора пользователя Firebase Authentication для вошедшего в систему пользователя, отправляющего запрос. Серверная часть автоматически проверяет этот токен и делает его доступным в context обработчика. Если токен недействителен, запрос отклоняется.
  • Необязательно: Firebase-Instance-ID-Token: <iid>
    • Токен регистрации FCM из клиентского SDK Firebase. Это должна быть строка. Это доступно в context обработчика. Он используется для таргетинга push-уведомлений.
  • Необязательно: X-Firebase-AppCheck: <token>
    • Токен Firebase App Check, предоставленный клиентским приложением, выполняющим запрос. Бэкенд автоматически проверяет этот токен и декодирует его, appId в context обработчика. Если токен не может быть проверен, запрос отклоняется. (Доступно для SDK >=3.14.0)

Если включены какие-либо другие заголовки, запрос отклоняется, как описано в документации по ответу ниже.

Примечание. В клиентах JavaScript эти запросы запускают предварительную проверку CORS OPTIONS , потому что:

Вызываемый триггер автоматически обрабатывает эти запросы OPTIONS .

Тело запроса

Тело HTTP-запроса должно быть объектом JSON с любым из следующих полей:

  • Требуется: data — аргумент, передаваемый функции. Это может быть любое допустимое значение JSON. Это автоматически декодируется в собственные типы JavaScript в соответствии с форматом сериализации, описанным ниже.

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

Формат ответа: коды состояния

Есть несколько случаев, которые могут привести к различным кодам состояния HTTP и строковым кодам состояния для ошибок в ответе.

  1. В случае ошибки HTTP до вызова client триггера ответ не обрабатывается как клиентская функция. Например, если клиент пытается вызвать несуществующую функцию, он получает ответ 404 Not Found .

  2. Если клиентский триггер вызывается, но запрос имеет неправильный формат, например не является JSON, содержит недопустимые поля или отсутствует поле data , запрос отклоняется с кодом ошибки 400 Bad Request с кодом ошибки INVALID_ARGUMENT .

  3. Если токен аутентификации, указанный в запросе, недействителен, запрос отклоняется с кодом ошибки 401 Unauthorized с кодом ошибки UNAUTHENTICATED .

  4. Если маркер регистрации FCM, предоставленный в запросе, недействителен, поведение не определено. Токен не проверяется при каждом запросе, за исключением случаев, когда он используется для отправки push-уведомления с помощью FCM.

  5. Если вызываемый триггер вызывается, но завершается сбоем с необработанным исключением или возвращает невыполненное обещание, запрос отклоняется с кодом 500 Internal Server Error с кодом ошибки INTERNAL . Это предотвращает случайное раскрытие ошибок кодирования конечным пользователям.

  6. Если вызываемый объект вызывается и возвращает явное условие ошибки с использованием API, предоставленного для вызываемых функций, то запрос завершается неудачно. Возвращаемый код состояния HTTP основан на официальном сопоставлении статуса ошибки со статусом HTTP, как определено в code.proto . Конкретный код ошибки, сообщение и возвращаемые сведения кодируются в тексте ответа, как описано ниже. Это означает, что если функция возвращает явную ошибку со статусом OK , то ответ имеет статус 200 OK , но в ответе установлено поле error .

  7. Если клиентский триггер выполнен успешно, статус ответа — 200 OK .

Формат ответа: заголовки

Ответ имеет следующие заголовки:

  • Content-Type: application/json
  • Необязательный ; charset=utf-8 разрешено.

Тело ответа

Ответ от конечной точки клиента всегда представляет собой объект JSON. Как минимум, он содержит либо result , либо error , а также любые необязательные поля. Если ответ не является объектом JSON или не содержит data или error , клиентский SDK должен рассматривать запрос как неудавшийся с кодом ошибки Google INTERNAL (13) .

  • error — если это поле присутствует, то запрос считается неудачным, независимо от кода состояния HTTP или наличия data . Значение этого поля должно быть объектом JSON в стандартном формате Google Cloud HTTP Mapping для ошибок, с полями для status , message и (необязательно) details . Поле code не должно быть включено. Если поле status не задано или имеет недопустимое значение, клиент должен рассматривать состояние как INTERNAL в соответствии с code.proto . Если details присутствуют, они включаются в любую информацию о пользователе, прикрепленную к ошибке в клиентском SDK, если это применимо.
    Примечание. Поле details здесь задается пользователем. Это не обязательно список значений с ключами по типу прототипа, как в формате Google Status .
  • result - значение, возвращаемое функцией. Это может быть любое допустимое значение JSON. SDK firebase-functions автоматически кодирует значение, возвращаемое пользователем, в этот формат JSON. Клиентские SDK автоматически декодируют эти параметры в собственные типы в соответствии с форматом сериализации, описанным ниже.

Если присутствуют другие поля, их следует игнорировать.

Сериализация

Формат сериализации для произвольных полезных данных одинаков как для запроса, так и для ответа.

Для согласованности с платформой они кодируются в JSON, как если бы они были значением поля Any в буфере протокола proto3, с использованием стандартного сопоставления JSON . Значения простых типов, таких как null , int , double или string , кодируются напрямую и не включают их явный тип. Таким образом, float и double кодируются одинаково, и вы можете не знать, что получено на другом конце вызова. Для типов, не являющихся родными для JSON, используется типизированная кодировка proto3 для значения. Дополнительные сведения см. в документации по любой кодировке JSON .

Допускаются следующие типы:

  • ноль - null
  • int (со знаком или без знака, до 32 бит) - например, 3 или -30 .
  • поплавок - например 3.14
  • двойной - например 3.14
  • логическое значение - true или false
  • строка - например "hello world"
  • карта - например, {"x": 3}
  • список - например [1, 2, 3]
  • long (со знаком или без знака, до 64 бит) - [подробности см. ниже]

Значения NaN и Infinity для float и double не поддерживаются.

Обратите внимание, что long — это специальный тип, обычно не разрешенный в JSON, но на него распространяется спецификация proto3. Например, они кодируются как:

длинная

{
    '@type': 'type.googleapis.com/google.protobuf.Int64Value',
    'value': '-123456789123456'
}

беззнаковый длинный

{
    '@type': 'type.googleapis.com/google.protobuf.UInt64Value',
    'value': '123456789123456'
}

В общем, ключ @type следует считать зарезервированным и не использовать для передаваемых карт.

Поскольку для простых типов тип не указан, некоторые значения изменят тип после передачи по сети. float , переданный внутрь, становится double . short становится int и так далее. В Android для значений списка поддерживаются как List , так и JSONArray . В этих случаях передача JSONArray даст List .

Если карта с неизвестным полем @type десериализована, она остается картой. Это позволяет разработчикам добавлять поля с новыми типами к своим возвращаемым значениям, не нарушая работу старых клиентов.

Примеры кода

Примеры в этом разделе иллюстрируют, как кодировать следующее:

  • Пример callable.call в Swift
  • Успешный ответ на вызов
  • Неудачный ответ на вызов

Пример Callable.call в Swift для кодирования

callable.call([
    "aString": "some string",
    "anInt": 57,
    "aFloat": 1.23,
    "aLong": -123456789123456 as Int64
])

Заголовок запроса:

Method: POST
Content-Type: application/json; charset=utf-8
Authorization: Bearer some-auth-token
Firebase-Instance-ID-Token: some-iid-token

Тело запроса:

{
    "data": {
        "aString": "some string",
        "anInt": 57,
        "aFloat": 1.23,
        "aLong": {
            "@type": "type.googleapis.com/google.protobuf.Int64Value",
            "value": "-123456789123456"
        }
    }
}

Ответ на кодирование

return {
    "aString": "some string",
    "anInt": 57,
    "aFloat": 1.23
};

Заголовок успешного ответа:

200 OK
Content-Type: application/json; charset=utf-8

Успешный ответ:

{
    "response": {
        "aString": "some string",
        "anInt": 57,
        "aFloat": 1.23
    }
}

Ошибка ответа на кодирование

throw new HttpsError("unauthenticated", "Request had invalid credentials.", {
  "some-key": "some-value"
});

Неудачный заголовок ответа:

401 UNAUTHENTICATED
Content-Type: application/json; charset=utf-8

Неудачный ответ:

{
    "error": {
        "message": "Request had invalid credentials.",
        "status": "UNAUTHENTICATED",
        "details": {
            "some-key": "some-value"
        }
    }
}