欢迎参加我们将于 2022 年 10 月 18 日举办的 Firebase 峰会(线上线下同时进行),了解 Firebase 如何帮助您加快应用开发速度、满怀信心地发布应用并在之后需要时轻松地扩大应用规模。立即报名

從您的應用調用函數

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

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

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

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

  • 適用於 Apple 平台的 Firebase SDK 9.6.0
  • 適用於 Android 20.1.1 的 Firebase SDK
  • Firebase JavaScript SDK 8.10.1
  • Firebase 模塊化 Web SDK v. 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 啟用應用檢查強制

發回結果

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

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

要在異步操作後返回數據,請返回一個 Promise。 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.10.0 --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");
    

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:30.5.0')
    
        // 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.1.1'
    }
    

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:30.5.0')
    
        // 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.1.1'
    }
    

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. FirebaseFunctions.unitypackage Unity SDK中的 FirebaseFunctions.unitypackage 添加到您的 Unity 項目中。

初始化客戶端 SDK

初始化 Cloud Functions 的實例:

迅速

lazy var functions = Functions.functions()

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

Java

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

Kotlin+KTX

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

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

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

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

Dart

final result =
    await FirebaseFunctions.instance.httpsCallable('addMessage').call();

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

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

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

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