Check out what’s new from Firebase@ Google I/O 2021, and join our alpha program for early access to the new Remote Config personalization feature. Learn more

從您的應用調用函數

Cloud Functions for Firebase 客戶端 SDK 可讓您直接從 Firebase 應用調用函數。要以這種方式從您的應用程序調用函數,請在 Cloud Functions 中編寫並部署 HTTPS Callable 函數,然後添加客戶端邏輯以從您的應用程序調用該函數。

請務必記住,HTTPS 可調用函數HTTP 函數相似但不完全相同。要使用 HTTPS 可調用函數,您必須使用適用於您平台的客戶端 SDK 以及functions.https後端 API(或實現協議)。 Callables 與 HTTP 函數有以下主要區別:

  • 對於可調用對象,Firebase 身份驗證令牌、FCM 令牌和 App Check 令牌(如果可用)會自動包含在請求中。
  • functions.https.onCall觸發器會自動反序列化請求正文並驗證身份驗證令牌。

Firebase SDK for Cloud Functions v0.9.1 及更高版本與這些 Firebase 客戶端 SDK 最低版本互操作以支持 HTTPS 可調用函數:

  • 適用於 iOS 8.1.1 的 Firebase SDK
  • 適用於 Android 20.0.0 的 Firebase SDK
  • Firebase JavaScript SDK 8.6.7
  • Firebase 模塊化 Web SDK v. 9.0

如果您想向在不受支持的平台上構建的應用程序添加類似的功能,請參閱https.onCall協議規範。本指南的其餘部分提供有關如何為 iOS、Android、Web、C++ 和 Unity 編寫、部署和調用 HTTPS 可調用函數的說明。

編寫和部署可調用函數

使用functions.https.onCall創建一個 HTTPS 可調用函數。此方法有兩個參數: data和可選的context

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

例如,對於將文本消息保存到實時數據庫的可調用函數, 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;

可調用函數的位置與調用客戶端的位置之間的距離會造成網絡延遲。要優化性能,請考慮在適用的情況下指定函數位置,並確保在客戶端初始化 SDK時將可調用的位置與設置的位置對齊。

或者,您可以附加 App Check 證明以幫助保護您的後端資源免遭濫用,例如賬單欺詐或網絡釣魚。請參閱為 Cloud Functions 啟用應用檢查強制

結果發回

要將數據發送回客戶端,請返回可以採用 JSON 編碼的數據。例如,要返回加法運算的結果:

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

要在異步操作後返回數據,請返回承諾。承諾返回的數據被發送回客戶端。例如,您可以返回可調用函數寫入實時數據庫的經過清理的文本:

// 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 };
})

處理錯誤

為確保客戶端獲得有用的錯誤詳細信息,請通過拋出(或返回被拒絕的 Promise )一個functions.https.HttpsError實例從可調用對象返回錯誤。該錯誤有一個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.');
}

部署可調用函數

index.js保存完整的可調用函數後,它會在您運行firebase deploy時與所有其他函數一起firebase deploy 。要僅部署可調用對象,請使用--only參數,如圖所示執行部分部署

$ firebase deploy --only functions:addMessage

如果您在部署函數時遇到權限錯誤,請確保為運行部署命令的用戶分配了適當的IAM 角色

設置您的客戶端開發環境

確保您滿足任何先決條件,然後將所需的依賴項和客戶端庫添加到您的應用程序。

IOS

  1. 按照說明將 Firebase 添加到您的 iOS 應用
  2. 將 Cloud Functions pod 添加到您的Podfile
    pod 'Firebase/Functions'
  3. 保存文件,然後運行:
    pod install

網頁版 v9

  1. 按照說明將 Firebase 添加到您的網絡應用程序
  2. 將 Firebase 核心和 Cloud Functions 客戶端庫添加到您的應用:
    <script src="https://www.gstatic.com/firebasejs/8.6.7/firebase.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.6.7/firebase-functions.js"></script>
    

Cloud Functions SDK 也可作為 npm 包使用。

  1. 從終端運行以下命令:
    npm install firebase@8.6.7 --save
    
  2. 手動需要 Firebase 核心和雲函數:

    import { initializeApp } from 'firebase/app';
    import { initializeFunctions } from 'firebase/functions';

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

網頁版 v8

  1. 按照說明將 Firebase 添加到您的網絡應用程序
  2. 將 Firebase 核心和 Cloud Functions 客戶端庫添加到您的應用:
    <script src="https://www.gstatic.com/firebasejs/8.6.7/firebase.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.6.7/firebase-functions.js"></script>
    

Cloud Functions SDK 也可作為 npm 包使用。

  1. 從終端運行以下命令:
    npm install firebase@8.6.7 --save
    
  2. 手動需要 Firebase 核心和雲函數:
    const firebase = require("firebase");
    // Required for side-effects
    require("firebase/functions");
    

爪哇

  1. 按照說明將 Firebase 添加到您的 Android 應用

  2. 使用Firebase Android BoM在您的模塊(應用級)Gradle 文件(通常是app/build.gradle )中聲明 Cloud Functions Android 庫的依賴app/build.gradle

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:28.1.0')
    
        // 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.0'
    }
    

科特林+KTX

  1. 按照說明將 Firebase 添加到您的 Android 應用

  2. 使用Firebase Android BoM在您的模塊(應用級)Gradle 文件(通常為app/build.gradle )中聲明 Cloud Functions Android 庫的依賴app/build.gradle

    dependencies {
        // Import the BoM for the Firebase platform
        implementation platform('com.google.firebase:firebase-bom:28.1.0')
    
        // 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.0'
    }
    

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

統一

  1. 按照說明將 Firebase 添加到您的 Unity 項目
  2. FirebaseFunctions.unitypackageFirebase Unity SDK 添加到您的 Unity 項目。

初始化客戶端 SDK

初始化 Cloud Functions 實例:

迅速

lazy var functions = Functions.functions()

目標-C

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

網頁版 v8

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();

網頁版 v9

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

爪哇

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

科特林+KTX

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

C++

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

統一

functions = Firebase.Functions.DefaultInstance;

調用函數

迅速

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

目標-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"];
}];

網頁版 v8

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

網頁版 v9

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;
  });

爪哇

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;
                }
            });
}

科特林+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);
}

統一

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;
  });
}

處理客戶端上的錯誤

如果服務器拋出錯誤或結果承諾被拒絕,則客戶端會收到錯誤。

如果函數返回的錯誤類型為function.https.HttpsError ,則客戶端會收到來自服務器錯誤的錯誤codemessagedetails 。否則,錯誤包含消息INTERNAL和代碼INTERNAL 。請參閱有關如何處理可調用函數中的錯誤的指南。

迅速

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]
  }
  // ...
}

目標-C

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

網頁版 v8

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;
    // ...
  });

網頁版 v9

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;
    // ...
  });

爪哇

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();
                    }

                    // ...
                }

                // ...
            }
        });

科特林+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);
// ...

統一

 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以幫助確保只有您的應用程序可以訪問您的可調用函數端點。