Firebase is back at Google I/O on May 10! Register now

從您的應用調用函數

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

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

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

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

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

  • 適用於 Apple 平台的 Firebase SDK 10.6.0
  • 適用於 Android 的 Firebase SDK 20.2.2
  • Firebase JavaScript SDK 8.10.1
  • Firebase 模塊化 Web SDK 9.0 版

如果您想向在不受支持的平台上構建的應用程序添加類似功能,請參閱https.onCall的協議規範。本指南的其餘部分提供有關如何為 Apple 平台、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 啟用 App Check 強制執行

發回結果

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

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

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

// 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時與所有其他函數一起部署。要僅部署可調用對象,請使用所示的--only參數來執行部分部署

firebase deploy --only functions:addMessage

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

設置客戶端開發環境

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

iOS+

按照說明將Firebase 添加到您的 Apple 應用程序

使用 Swift Package Manager 安裝和管理 Firebase 依賴項。

  1. 在 Xcode 中,打開您的應用程序項目,導航至File > Add Packages
  2. 出現提示時,添加 Firebase Apple 平台 SDK 存儲庫:
  3.   https://github.com/firebase/firebase-ios-sdk
  4. 選擇 Cloud Functions 庫。
  5. 完成後,Xcode 將自動開始在後台解析和下載您的依賴項。

Web version 9

  1. 按照說明將Firebase 添加到您的 Web 應用程序。確保從終端運行以下命令:
    npm install firebase@9.17.2 --save
    
  2. 手動要求 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 version 8

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

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

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

Kotlin+KTX

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

  2. 在您的模塊(應用程序級)Gradle 文件(通常為<project>/<app-module>/build.gradle )中,添加 Cloud Functions Android 庫的依賴項。我們建議使用Firebase Android BoM來控制庫版本。

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

Java

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

  2. 在您的模塊(應用程序級)Gradle 文件(通常為<project>/<app-module>/build.gradle )中,添加 Cloud Functions Android 庫的依賴項。我們建議使用Firebase Android BoM來控制庫版本。

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

Dart

  1. 按照說明將Firebase 添加到您的 Flutter 應用程序

  2. 從 Flutter 項目的根目錄,運行以下命令來安裝插件:

    flutter pub add cloud_functions
    
  3. 完成後,重建您的 Flutter 應用程序:

    flutter run
    
  4. 安裝後,您可以通過在 Dart 代碼中導入來訪問cloud_functions插件:

    import 'package:cloud_functions/cloud_functions.dart';
    

C++

對於帶有 Android 的 C++

  1. 按照說明將Firebase 添加到您的 C++ 項目
  2. firebase_functions庫添加到您的CMakeLists.txt文件中。

對於 Apple 平台的 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. Firebase Unity SDK中的FirebaseFunctions.unitypackage添加到您的 Unity 項目中。

初始化客戶端SDK

初始化 Cloud Functions 的實例:

迅速

lazy var functions = Functions.functions()

目標-C

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

Web version 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 version 9

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

Kotlin+KTX

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

Java

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

Dart

final functions = FirebaseFunctions.instance;

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 isEqual:@"com.firebase.functions"]) {
      FIRFunctionsErrorCode code = error.code;
      NSString *message = error.localizedDescription;
      NSObject *details = error.userInfo[@"details"];
    }
    // ...
  }
  self->_resultField.text = result.data[@"text"];
}];

Web version 8

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

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

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

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

Dart

    final result = await FirebaseFunctions.instance.httpsCallable('addMessage').call(
      {
        "text": text,
        "push": true,
      },
    );
    _response = result.data as String;

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 isEqual:@"com.firebase.functions"]) {
    FIRFunctionsErrorCode code = error.code;
    NSString *message = error.localizedDescription;
    NSObject *details = error.userInfo[@"details"];
  }
  // ...
}

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

Kotlin+KTX

addMessage(inputMessage)
    .addOnCompleteListener { task ->
        if (!task.isSuccessful) {
            val e = task.exception
            if (e is FirebaseFunctionsException) {
                val code = e.code
                val details = e.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();
                    }
                }
            }
        });

Dart

try {
  final result =
      await FirebaseFunctions.instance.httpsCallable('addMessage').call();
} on FirebaseFunctionsException catch (error) {
  print(error.code);
  print(error.details);
  print(error.message);
}

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