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

Триггер https.onCall для облачных функций — это триггер 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, предоставленный клиентским приложением, отправляющим запрос. Бэкэнд автоматически проверяет этот токен и декодирует его, внедряя 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 в стандартном формате HTTP-сопоставления Google Cloud для ошибок, с полями для status , message и (необязательно) details . Поле code не должно быть включено. Если поле status не установлено или имеет недопустимое значение, клиент должен рассматривать статус как INTERNAL в соответствии с code.proto . Если details присутствуют, они включаются в любую информацию о пользователе, прикрепленную к ошибке в клиентском SDK, если это применимо.
    Примечание. Поле details здесь представляет собой значение, введенное пользователем. Это не обязательно список значений, привязанных к типу прототипа, как в формате Status Google.
  • 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]
  • длинный (со знаком или без знака, до 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"
        }
    }
}