Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

从您的应用中调用函数

借助 Cloud Functions for Firebase 客户端 SDK,您可以直接从 Firebase 应用调用函数。如需以这种方式从您的应用调用函数,请在 Cloud Functions 中编写和部署 HTTPS Callable 函数,然后添加用于从您的应用调用该函数的客户端逻辑。

请务必注意,HTTPS Callable 函数与 HTTP 函数类似,但并不完全相同。如需使用 HTTPS Callable 函数,您必须将适用于您平台的客户端 SDK 与 functions.https 后端 API 搭配使用(或实现相关协议)。Callable 函数与 HTTP 函数存在以下重要区别:

  • 使用 Callable 函数时,Firebase Authentication 令牌、FCM 令牌和 App Check 令牌(如果可用)会自动包含在请求中。
  • functions.https.onCall 触发器会自动对请求正文进行反序列化,并验证身份验证令牌。

Firebase SDK for Cloud Functions(v0.9.1 及更高版本)可与下列 Firebase 客户端 SDK 最低版本交互,以支持 HTTPS Callable 函数:

  • Firebase SDK for iOS 8.8.0
  • Firebase SDK for Android 20.0.1
  • Firebase JavaScript SDK 8.10.0
  • Firebase Modular Web SDK v. 9.0

如果您要在不受支持的平台上构建的应用中添加类似功能,请参阅 https.onCall 协议规范。本指南的其余部分提供了有关如何编写、部署和调用面向 iOS、Android、Web、C++ 和 Unity 平台的 HTTPS Callable 函数的说明。

编写和部署 Callable 函数

使用 functions.https.onCall 方法创建 HTTPS Callable 函数。该方法需要两个参数:data 和可选的 context

// Saves a message to the Firebase Realtime Database but sanitizes the text by removing swearwords.
exports.addMessage = functions.https.onCall((data, context) => {
  // ...
});

例如,如果某个 Callable 函数用于将文本消息保存到 Realtime Database,则可以在 data 中包含消息文本,用 context 参数表示用户身份验证信息:

// Message text passed from the client.
const text = data.text;
// Authentication / user information is automatically added to the request.
const uid = context.auth.uid;
const name = context.auth.token.name || null;
const picture = context.auth.token.picture || null;
const email = context.auth.token.email || null;

Callable 函数的位置与调用客户端位置之间的距离可能会造成网络延迟。为了优化性能,请考虑在适用时指定函数位置,并确保将 Callable 函数的位置与您在客户端初始化 SDK 时设置的位置保持一致。

(可选)您可以附加 App Check 证明,帮助保护您的后端资源免遭滥用(例如受到帐单欺诈或钓鱼式攻击)。请参阅为 Cloud Functions 函数启用 App Check 实施

返回结果

如需向客户端发回数据,请返回可以进行 JSON 编码的数据。例如,需要返回加法运算的结果:

// returning result.
return {
  firstNumber: firstNumber,
  secondNumber: secondNumber,
  operator: '+',
  operationResult: firstNumber + secondNumber,
};

如需在异步操作之后返回数据,请返回一个 Promise。Promise 返回的数据将发送给客户端。例如,您可以返回经过净化 (sanitize) 的文本(由 Callable 函数写入 Realtime Database):

// Saving the new message to the Realtime Database.
const sanitizedMessage = sanitizer.sanitizeText(text); // Sanitize the message.
return admin.database().ref('/messages').push({
  text: sanitizedMessage,
  author: { uid, name, picture, email },
}).then(() => {
  console.log('New Message written');
  // Returning the sanitized message to the client.
  return { text: sanitizedMessage };
})

处理错误

为了确保客户端获得有用的错误详情,可以通过抛出一个 functions.https.HttpsError 实例(或返回包含此实例的被拒 Promise)从 Callable 函数返回错误。此类错误有一个 code 特性,该特性可以是 functions.https.HttpsError 中列出的某个值。这些错误还有一个字符串 message,默认为空字符串。它们可能还有一个含任意值的可选 details 字段。如果您的函数了抛出一个非 HttpsError 错误,您的客户端会收到包含消息 INTERNAL 和代码 internal 的错误。

例如,函数可以抛出数据验证和身份验证错误,并将错误消息返回给调用客户端:

// Checking attribute.
if (!(typeof text === 'string') || text.length === 0) {
  // Throwing an HttpsError so that the client gets the error details.
  throw new functions.https.HttpsError('invalid-argument', 'The function must be called with ' +
      'one arguments "text" containing the message text to add.');
}
// Checking that the user is authenticated.
if (!context.auth) {
  // Throwing an HttpsError so that the client gets the error details.
  throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
      'while authenticated.');
}

部署 Callable 函数

当您在 index.js 内保存已完成的 Callable 函数后,系统会在在您运行 firebase deploy 时部署该函数以及其他所有函数。如需只部署 Callable 函数,请按如下所示的方式使用 --only 参数执行部分部署

$ firebase deploy --only functions:addMessage

如果在部署函数时遇到权限错误,请确保已将适当的 IAM 角色分配给运行部署命令的用户。

设置您的客户端开发环境

请确保您满足所有前提条件,并将必要的依赖项和客户端库添加到您的应用。

iOS

  1. 按照相关说明将 Firebase 添加到您的 iOS 应用
  2. 将 Cloud Functions pod 添加到您的 Podfile
    pod 'Firebase/Functions'
  3. 保存文件,然后运行:
    pod install

Web 版本 9

  1. 按照相关说明将 Firebase 添加到您的 Web 应用。请务必在终端运行以下命令:
    npm install firebase@9.1.0 --save
    
  2. 需要手动通过 require() 函数引入 Firebase 核心和 Cloud Functions:

     import { initializeApp } from 'firebase/app';
     import { getFunctions } from 'firebase/functions';
    
     const app = initializeApp({
         projectId: '### CLOUD FUNCTIONS PROJECT ID ###',
         apiKey: '### FIREBASE API KEY ###',
         authDomain: '### FIREBASE AUTH DOMAIN ###',
       });
     const functions = getFunctions(app);
    

Web 版本 8

  1. 按照相关说明将 Firebase 添加到您的 Web 应用
  2. 将 Firebase 核心客户端库和 Cloud Functions 客户端库添加到您的应用:
    <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-functions.js"></script>
    

Cloud Functions SDK 也作为 npm 软件包提供。

  1. 从终端运行以下命令:
    npm install firebase@8.10.0 --save
    
  2. 需要手动通过 require() 函数引入 Firebase 核心和 Cloud Functions:
    const firebase = require("firebase");
    // Required for side-effects
    require("firebase/functions");
    

Java

  1. 按照相关说明将 Firebase 添加到您的 Android 应用

  2. 使用 Firebase Android BoM模块(应用级)Gradle 文件(通常为 app/build.gradle)中声明 Cloud Functions Android 库的依赖项。

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:28.4.1')
    
        // Declare the dependency for the Cloud Functions library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-functions'
    }
    

    使用 Firebase Android BoM,可确保您的应用始终使用 Firebase Android 库的兼容版本。

    (替代方法) 在不使用 BoM 的情况下声明 Firebase 库依赖项

    如果您选择不使用 Firebase BoM,则必须在每个 Firebase 库的依赖项行中指定相应的库版本。

    请注意,如果您在应用中使用多个 Firebase 库,我们强烈建议您使用 BoM 来管理库版本,从而确保所有版本都兼容。

    dependencies {
        // Declare the dependency for the Cloud Functions library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-functions:20.0.1'
    }
    

Kotlin+KTX

  1. 按照相关说明将 Firebase 添加到您的 Android 应用

  2. 使用 Firebase Android BoM模块(应用级)Gradle 文件(通常为 app/build.gradle)中声明 Cloud Functions Android 库的依赖项。

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:28.4.1')
    
        // Declare the dependency for the Cloud Functions library
        // When using the BoM, you don't specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-functions-ktx'
    }
    

    使用 Firebase Android BoM,可确保您的应用始终使用 Firebase Android 库的兼容版本。

    (替代方法) 在不使用 BoM 的情况下声明 Firebase 库依赖项

    如果您选择不使用 Firebase BoM,则必须在每个 Firebase 库的依赖项行中指定相应的库版本。

    请注意,如果您在应用中使用多个 Firebase 库,我们强烈建议您使用 BoM 来管理库版本,从而确保所有版本都兼容。

    dependencies {
        // Declare the dependency for the Cloud Functions library
        // When NOT using the BoM, you must specify versions in Firebase library dependencies
        implementation 'com.google.firebase:firebase-functions-ktx:20.0.1'
    }
    

C++

对于 Android 平台的 C++ 项目

  1. 按照相关说明将 Firebase 添加到您的 C++ 项目
  2. firebase_functions 库添加到您的 CMakeLists.txt 文件中。

对于 iOS 平台的 C++ 项目

  1. 按照相关说明将 Firebase 添加到您的 C++ 项目
  2. 将 Cloud Functions pod 添加到您的 Podfile
    pod 'Firebase/Functions'
  3. 保存文件,然后运行:
    pod install
  4. Firebase C++ SDK 中的 Firebase 核心框架和 Cloud Functions 框架添加到您的 Xcode 项目。
    • firebase.framework
    • firebase_functions.framework

Unity

  1. 按照相关说明将 Firebase 添加到您的 Unity 项目
  2. Firebase Unity SDK 中的 FirebaseFunctions.unitypackage 添加到您的 Unity 项目。

初始化客户端 SDK

初始化 Cloud Functions 的实例:

Swift

lazy var functions = Functions.functions()

Objective-C

@property(strong, nonatomic) FIRFunctions *functions;
// ...
self.functions = [FIRFunctions functions];

Web 版本 8

firebase.initializeApp({
  apiKey: '### FIREBASE API KEY ###',
  authDomain: '### FIREBASE AUTH DOMAIN ###',
  projectId: '### CLOUD FUNCTIONS PROJECT ID ###'
  databaseURL: 'https://### YOUR DATABASE NAME ###.firebaseio.com',
});

// Initialize Cloud Functions through Firebase
var functions = firebase.functions();

Web 版本 9

const app = initializeApp({
  projectId: '### CLOUD FUNCTIONS PROJECT ID ###',
  apiKey: '### FIREBASE API KEY ###',
  authDomain: '### FIREBASE AUTH DOMAIN ###',
});
const functions = getFunctions(app);

Java

private FirebaseFunctions mFunctions;
// ...
mFunctions = FirebaseFunctions.getInstance();

Kotlin+KTX

private lateinit var functions: FirebaseFunctions
// ...
functions = Firebase.functions

C++

firebase::functions::Functions* functions;
// ...
functions = firebase::functions::Functions::GetInstance(app);

Unity

functions = Firebase.Functions.DefaultInstance;

调用函数

Swift

functions.httpsCallable("addMessage").call(["text": inputField.text]) { result, error in
  if let error = error as NSError? {
    if error.domain == FunctionsErrorDomain {
      let code = FunctionsErrorCode(rawValue: error.code)
      let message = error.localizedDescription
      let details = error.userInfo[FunctionsErrorDetailsKey]
    }
    // ...
  }
  if let data = result?.data as? [String: Any], let text = data["text"] as? String {
    self.resultField.text = text
  }
}

Objective-C

[[_functions HTTPSCallableWithName:@"addMessage"] callWithObject:@{@"text": _inputField.text}
                                                      completion:^(FIRHTTPSCallableResult * _Nullable result, NSError * _Nullable error) {
  if (error) {
    if (error.domain == FIRFunctionsErrorDomain) {
      FIRFunctionsErrorCode code = error.code;
      NSString *message = error.localizedDescription;
      NSObject *details = error.userInfo[FIRFunctionsErrorDetailsKey];
    }
    // ...
  }
  self->_resultField.text = result.data[@"text"];
}];

Web 版本 8

var addMessage = firebase.functions().httpsCallable('addMessage');
addMessage({ text: messageText })
  .then((result) => {
    // Read result of the Cloud Function.
    var sanitizedMessage = result.data.text;
  });

Web 版本 9

import { getFunctions, httpsCallable } from "firebase/functions";

const functions = getFunctions();
const addMessage = httpsCallable(functions, 'addMessage');
addMessage({ text: messageText })
  .then((result) => {
    // Read result of the Cloud Function.
    /** @type {any} */
    const data = result.data;
    const sanitizedMessage = data.text;
  });

Java

private Task<String> addMessage(String text) {
    // Create the arguments to the callable function.
    Map<String, Object> data = new HashMap<>();
    data.put("text", text);
    data.put("push", true);

    return mFunctions
            .getHttpsCallable("addMessage")
            .call(data)
            .continueWith(new Continuation<HttpsCallableResult, String>() {
                @Override
                public String then(@NonNull Task<HttpsCallableResult> task) throws Exception {
                    // This continuation runs on either success or failure, but if the task
                    // has failed then getResult() will throw an Exception which will be
                    // propagated down.
                    String result = (String) task.getResult().getData();
                    return result;
                }
            });
}

Kotlin+KTX

private fun addMessage(text: String): Task<String> {
    // Create the arguments to the callable function.
    val data = hashMapOf(
        "text" to text,
        "push" to true
    )

    return functions
            .getHttpsCallable("addMessage")
            .call(data)
            .continueWith { task ->
                // This continuation runs on either success or failure, but if the task
                // has failed then result will throw an Exception which will be
                // propagated down.
                val result = task.result?.data as String
                result
            }
}

C++

firebase::Future<firebase::functions::HttpsCallableResult> AddMessage(
    const std::string& text) {
  // Create the arguments to the callable function.
  firebase::Variant data = firebase::Variant::EmptyMap();
  data.map()["text"] = firebase::Variant(text);
  data.map()["push"] = true;

  // Call the function and add a callback for the result.
  firebase::functions::HttpsCallableReference doSomething =
      functions->GetHttpsCallable("addMessage");
  return doSomething.Call(data);
}

Unity

private Task<string> addMessage(string text) {
  // Create the arguments to the callable function.
  var data = new Dictionary<string, object>();
  data["text"] = text;
  data["push"] = true;

  // Call the function and extract the operation from the result.
  var function = functions.GetHttpsCallable("addMessage");
  return function.CallAsync(data).ContinueWith((task) => {
    return (string) task.Result.Data;
  });
}

在客户端上处理错误

如果服务器抛出错误或者生成的 Promise 被拒绝,客户端就会收到错误。

如果函数返回的错误类型为 function.https.HttpsError,客户端会从该服务器错误中收到错误的 codemessagedetails。否则,错误会包含消息 INTERNAL 和代码 INTERNAL。请参阅有关如何处理 Callable 函数中的错误的指南。

Swift

if let error = error as NSError? {
  if error.domain == FunctionsErrorDomain {
    let code = FunctionsErrorCode(rawValue: error.code)
    let message = error.localizedDescription
    let details = error.userInfo[FunctionsErrorDetailsKey]
  }
  // ...
}

Objective-C

if (error) {
  if (error.domain == FIRFunctionsErrorDomain) {
    FIRFunctionsErrorCode code = error.code;
    NSString *message = error.localizedDescription;
    NSObject *details = error.userInfo[FIRFunctionsErrorDetailsKey];
  }
  // ...
}

Web 版本 8

var addMessage = firebase.functions().httpsCallable('addMessage');
addMessage({ text: messageText })
  .then((result) => {
    // Read result of the Cloud Function.
    var sanitizedMessage = result.data.text;
  })
  .catch((error) => {
    // Getting the Error details.
    var code = error.code;
    var message = error.message;
    var details = error.details;
    // ...
  });

Web 版本 9

import { getFunctions, httpsCallable } from "firebase/functions";

const functions = getFunctions();
const addMessage = httpsCallable(functions, 'addMessage');
addMessage({ text: messageText })
  .then((result) => {
    // Read result of the Cloud Function.
    /** @type {any} */
    const data = result.data;
    const sanitizedMessage = data.text;
  })
  .catch((error) => {
    // Getting the Error details.
    const code = error.code;
    const message = error.message;
    const details = error.details;
    // ...
  });

Java

addMessage(inputMessage)
        .addOnCompleteListener(new OnCompleteListener<String>() {
            @Override
            public void onComplete(@NonNull Task<String> task) {
                if (!task.isSuccessful()) {
                    Exception e = task.getException();
                    if (e instanceof FirebaseFunctionsException) {
                        FirebaseFunctionsException ffe = (FirebaseFunctionsException) e;
                        FirebaseFunctionsException.Code code = ffe.getCode();
                        Object details = ffe.getDetails();
                    }

                    // ...
                }

                // ...
            }
        });

Kotlin+KTX

addMessage(inputMessage)
        .addOnCompleteListener(OnCompleteListener { task ->
            if (!task.isSuccessful) {
                val e = task.exception
                if (e is FirebaseFunctionsException) {
                    val code = e.code
                    val details = e.details
                }

                // ...
            }

            // ...
        })

C++

void OnAddMessageCallback(
    const firebase::Future<firebase::functions::HttpsCallableResult>& future) {
  if (future.error() != firebase::functions::kErrorNone) {
    // Function error code, will be kErrorInternal if the failure was not
    // handled properly in the function call.
    auto code = static_cast<firebase::functions::Error>(future.error());

    // Display the error in the UI.
    DisplayError(code, future.error_message());
    return;
  }

  const firebase::functions::HttpsCallableResult *result = future.result();
  firebase::Variant data = result->data();
  // This will assert if the result returned from the function wasn't a string.
  std::string message = data.string_value();
  // Display the result in the UI.
  DisplayResult(message);
}

// ...

// ...
  auto future = AddMessage(message);
  future.OnCompletion(OnAddMessageCallback);
// ...

Unity

 addMessage(text).ContinueWith((task) => {
  if (task.IsFaulted) {
    foreach (var inner in task.Exception.InnerExceptions) {
      if (inner is FunctionsException) {
        var e = (FunctionsException) inner;
        // Function error code, will be INTERNAL if the failure
        // was not handled properly in the function call.
        var code = e.ErrorCode;
        var message = e.ErrorMessage;
      }
    }
  } else {
    string result = task.Result;
  }
});

在发布应用之前,您应启用 App Check,以便确保只有您的应用可以访问您的 Callable 函数端点。