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調用函數,相似但不相同的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.8.0 的 Firebase SDK
  • 適用於 Android 20.0.1 的 Firebase SDK
  • Firebase JavaScript SDK 8.10.0
  • 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 證明以幫助保護您的後端資源免遭濫用,例如賬單欺詐或網絡釣魚。請參閱啟用應用檢查執法雲功能

結果發回

要將數據發送回客戶端,請返回可以採用 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 };
})

處理錯誤

為了確保客戶通過投擲(或返回承諾拒絕使用)的情況下獲得有用的錯誤細節,從一個可調用的回傳失誤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

  1. 按照說明火力地堡添加到您的iOS應用
  2. 雲功能莢添加到您的Podfile
    pod 'Firebase/Functions'
  3. 保存文件,然後運行:
    pod install

網頁版 9

  1. 按照說明火力地堡添加到您的Web應用程序。確保你的終端運行下面的命令:
    npm install firebase@9.1.3 --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);
    

網頁版 8

  1. 按照說明火力地堡添加到您的Web應用程序
  2. 在火力地堡的核心和雲功能的客戶端庫添加到應用程序:
    <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. 手動既需要火力地堡的核心和雲功能:
    const firebase = require("firebase");
    // Required for side-effects
    require("firebase/functions");
    

爪哇

  1. 按照說明火力地堡添加到您的Android應用

  2. 使用火力地堡Android的物料清單,聲明你的模塊(應用程序級)搖籃文件(通常為雲功能的Android庫的依賴app/build.gradle )。

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

    通過使用火力地堡Android的物料清單,您的應用程序將始終使用火力地堡的Android庫的兼容版本。

    (替代)聲明火力地堡庫依賴使用物料清單

    如果您選擇不使用 Firebase BoM,則必須在其依賴項行中指定每個 Firebase 庫版本。

    需要注意的是,如果你在你的應用程序使用多個火力地堡庫,我們強烈建議您使用的物料清單管理庫版本,以保證所有版本相互兼容。

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

科特林+KTX

  1. 按照說明火力地堡添加到您的Android應用

  2. 使用火力地堡Android的物料清單,聲明你的模塊(應用程序級)搖籃文件(通常為雲功能的Android庫的依賴app/build.gradle )。

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

    通過使用火力地堡Android的物料清單,您的應用程序將始終使用火力地堡的Android庫的兼容版本。

    (替代)聲明火力地堡庫依賴使用物料清單

    如果您選擇不使用 Firebase BoM,則必須在其依賴項行中指定每個 Firebase 庫版本。

    需要注意的是,如果你在你的應用程序使用多個火力地堡庫,我們強烈建議您使用的物料清單管理庫版本,以保證所有版本相互兼容。

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

對於C ++與Android:

  1. 按照說明火力地堡添加到您的C ++項目
  2. 添加firebase_functions庫到您CMakeLists.txt文件。

對於C ++與iOS:

  1. 按照說明火力地堡添加到您的C ++項目
  2. 雲功能莢添加到您的Podfile
    pod 'Firebase/Functions'
  3. 保存文件,然後運行:
    pod install
  4. 在火力地堡的核心和雲功能框架,從添加火力地堡C ++ SDK到您的Xcode項目。
    • firebase.framework
    • firebase_functions.framework

統一

  1. 按照說明火力地堡添加到您的統一項目
  2. 添加FirebaseFunctions.unitypackage火力地堡統一SDK ,以您的統一項目。

初始化客戶端 SDK

初始化 Cloud Functions 實例:

迅速

lazy var functions = Functions.functions()

目標-C

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

網頁版 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();

網頁版 9

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

爪哇

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

網頁版 8

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

網頁版 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;
  });

爪哇

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 ,則客戶端收到錯誤codemessage ,和details從服務器錯誤。否則,錯誤包含消息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];
  }
  // ...
}

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

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

爪哇

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

之前您啟動應用程序,你應該讓應用程序檢查,以確保只有您的應用程序可以訪問您的可調用函數的端點。