ערכות ה-SDK של הלקוח ב-Cloud Functions for Firebase מאפשרות להפעיל פונקציות ישירות מאפליקציית Firebase. כדי להפעיל פונקציה מהאפליקציה באופן הזה, כותבים ומפרסים פונקציה ניתנת לקריאה ב-HTTP ב-Cloud Functions, ואז מוסיפים לוגיקה של לקוח כדי להפעיל את הפונקציה מהאפליקציה.
חשוב לזכור שפונקציות קריאה ל-HTTP דומות לפונקציות HTTP, אבל לא זהות להן. כדי להשתמש בפונקציות שניתן להפעיל באמצעות HTTP, צריך להשתמש ב-SDK של הלקוח בפלטפורמה שלכם יחד עם ה-API לקצה העורפי (או להטמיע את הפרוטוקול). ל-callables יש את ההבדלים העיקריים הבאים מפונקציות HTTP:
- כשמשתמשים ב-callables, אסימוני Firebase Authentication, אסימוני FCM ואסימוני App Check, אם הם זמינים, נכללים באופן אוטומטי בבקשות.
- הטריגר מבצע באופן אוטומטי דה-סריאליזציה של גוף הבקשה ומאמת את אסימוני האימות.
ה-SDK Firebase של Cloud Functions מדור שני ואילך פועל באופן הדדי עם גרסאות המינימום של ה-SDK של לקוח Firebase כדי לתמוך בפונקציות קריאה ב-HTTPS:
- Firebase SDK לפלטפורמות Apple 11.4.0
- Firebase SDK עבור Android 21.0.0
- Firebase Modular Web SDK גרסה 9.7.0
אם רוצים להוסיף פונקציונליות דומה לאפליקציה שנוצרה בפלטפורמה שאינה נתמכת, אפשר לעיין במפרט הפרוטוקול של https.onCall
. בהמשך המדריך מפורטות הוראות לכתיבת פונקציה ניתנת לקריאה ב-HTTP, לפריסה שלה ולקריאה אליה בפלטפורמות של Apple, ב-Android, באינטרנט, ב-C++ וב-Unity.
כתיבת הפונקציה שניתן לבצע קריאה אליה ופריסה שלה
משתמשים ב-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) => { // ... });
בפונקציה שניתן להתקשר אליה ששומרת הודעת טקסט ב-Realtime Database, לדוגמה, data
יכול להכיל את הטקסט של ההודעה, והפרמטרים context
מייצגים את פרטי אימות המשתמש:
// Message text passed from the client.
const text = request.data.text;
// Authentication / user information is automatically added to the request.
const uid = request.auth.uid;
const name = request.auth.token.name || null;
const picture = request.auth.token.picture || null;
const email = request.auth.token.email || null;
המרחק בין המיקום של הפונקציה שניתן להפעיל לבין המיקום של הלקוח הקורא יכול ליצור זמן אחזור ברשת. כדי לשפר את הביצועים, מומלץ לציין את מיקום הפונקציה במקרים הרלוונטיים, ולוודא שהמיקום של הקריאה לפונקציה תואם למיקום שהוגדר בזמן הפעלת ה-SDK בצד הלקוח.
לחלופין, תוכלו לצרף אימות App Check כדי להגן על משאבי הקצה העורפי שלכם מפני ניצול לרעה, כמו תרמיות חיוב או פישינג. למידע נוסף, אפשר לקרוא את המאמר הפעלת האכיפה App Check ל-Cloud Functions.
שליחת התוצאה חזרה
כדי לשלוח נתונים חזרה ללקוח, מחזירים נתונים שאפשר לקודד ב-JSON. לדוגמה, כדי להחזיר את התוצאה של פעולת חיבור:
// returning result.
return {
firstNumber: firstNumber,
secondNumber: secondNumber,
operator: "+",
operationResult: firstNumber + secondNumber,
};
כדי להחזיר נתונים אחרי פעולה אסינכררונית, מחזירים הבטחה (promise). הנתונים שמוחזרים על ידי ה-promise נשלחים בחזרה ללקוח. לדוגמה, אפשר להחזיר טקסט "מחוספס" שהפונקציה שניתנת לקריאה נכתבה ב-Realtime Database:
// Saving the new message to the Realtime Database.
const sanitizedMessage = sanitizer.sanitizeText(text); // Sanitize message.
return getDatabase().ref("/messages").push({
text: sanitizedMessage,
author: {uid, name, picture, email},
}).then(() => {
logger.info("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 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 (!request.auth) {
// Throwing an HttpsError so that the client gets the error details.
throw new HttpsError("failed-precondition", "The function must be " +
"called while authenticated.");
}
פריסת הפונקציה שניתן לקרוא לה
אחרי ששומרים פונקציה ניתנת לקריאה ב-index.js
, היא נפרסת יחד עם כל שאר הפונקציות כשמריצים את firebase deploy
.
כדי לפרוס רק את ה-callable, משתמשים בארגומנט --only
כפי שמוצג כדי לבצע פריסות חלקיות:
firebase deploy --only functions:addMessage
אם נתקלתם בשגיאות הרשאות בזמן הפריסה של הפונקציות, ודאו שהמשתמש שמריץ את פקודות הפריסה הוקצו לו תפקידי IAM מתאימים.
הגדרת סביבת פיתוח ללקוחות
עליכם לוודא שאתם עומדים בדרישות המוקדמות, ואז להוסיף לאפליקציה את יחסי התלות ואת ספריות הלקוח הנדרשות.
iOS+
פועלים לפי ההוראות להוספת Firebase לאפליקציה ל-Apple.
אפשר להשתמש ב-Swift Package Manager כדי להתקין ולנהל יחסי תלות של Firebase.
- ב-Xcode, כשפרויקט האפליקציה פתוח, עוברים אל קובץ > הוספת חבילות.
- כשמופיעה בקשה, מוסיפים את המאגר של Firebase SDK לפלטפורמות של Apple:
- בוחרים את הספרייה Cloud Functions.
- מוסיפים את הדגל
-ObjC
לקטע Other Linker Flags (דגלים אחרים של קישור) בהגדרות ה-build של היעד. - בסיום, Xcode יתחיל לפתור את הבעיה ותוריד את יחסי התלות באופן אוטומטי ברקע.
https://github.com/firebase/firebase-ios-sdk.git
Web
- פועלים לפי ההוראות להוספת Firebase לאפליקציית האינטרנט. חשוב להריץ את הפקודה הבאה מהטרמינל:
npm install firebase@11.0.1 --save
מחייב באופן ידני גם את הליבה של 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
- פועלים לפי ההוראות כדי להוסיף את Firebase לאפליקציית האינטרנט.
- מוסיפים לאפליקציה את ספריית הליבה של 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>
ערכת ה-SDK של Cloud Functions זמינה גם כחבילת npm.
- מריצים את הפקודה הבאה מהטרמינל:
npm install firebase@8.10.1 --save
- לדרוש באופן ידני גם את הליבה של Firebase וגם את Cloud Functions:
const firebase = require("firebase"); // Required for side-effects require("firebase/functions");
Kotlin+KTX
פועלים לפי ההוראות כדי להוסיף את Firebase לאפליקציה ל-Android.
בקובץ Gradle של המודול (ברמת האפליקציה) (בדרך כלל
<project>/<app-module>/build.gradle.kts
או<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:33.5.1")) // 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.
(חלופה) מוסיפים יחסי תלות לספריות של Firebase בלי להשתמש ב-BoM
אם בוחרים לא להשתמש ב-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:21.0.0") }
Java
פועלים לפי ההוראות כדי להוסיף את Firebase לאפליקציה ל-Android.
בקובץ Gradle של המודול (ברמת האפליקציה) (בדרך כלל
<project>/<app-module>/build.gradle.kts
או<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:33.5.1")) // 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.
(חלופה) מוסיפים יחסי תלות לספריות של Firebase בלי להשתמש ב-BoM
אם בוחרים לא להשתמש ב-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:21.0.0") }
Dart
פועלים לפי ההוראות להוספת Firebase לאפליקציית Flutter.
כדי להתקין את הפלאגין, מריצים את הפקודה הבאה מהרמה הבסיסית (root) של פרויקט Flutter:
flutter pub add cloud_functions
בסיום, יוצרים מחדש את אפליקציית Flutter:
flutter run
אחרי ההתקנה, אפשר לגשת לפלאגין
cloud_functions
על ידי ייבוא שלו לקוד Dart:import 'package:cloud_functions/cloud_functions.dart';
C++
ב-C++ עם Android:
- פועלים לפי ההוראות להוספת Firebase לפרויקט C++.
- מוסיפים את הספרייה
firebase_functions
לקובץCMakeLists.txt
.
עבור C++ עם פלטפורמות של Apple:
- פועלים לפי ההוראות כדי להוסיף את Firebase לפרויקט C++.
- מוסיפים את ה-pod של Cloud Functions ל-
Podfile
:pod 'Firebase/Functions'
- שומרים את הקובץ ומריצים:
pod install
- מוסיפים את הליבה של Firebase ו-Cloud Functions frameworks מ-Firebase C++ SDK לפרויקט Xcode.
firebase.framework
firebase_functions.framework
Unity
- פועלים לפי ההוראות להוספת Firebase לפרויקט ב-Unity.
- מוסיפים את
FirebaseFunctions.unitypackage
מ-Firebase Unity SDK לפרויקט ב-Unity.
אתחול ה-SDK של הלקוח
מאתחלים מופע של Cloud Functions:
Swift
lazy var functions = Functions.functions()
Objective-C
@property(strong, nonatomic) FIRFunctions *functions;
// ...
self.functions = [FIRFunctions functions];
Web
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
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);
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 isEqual:@"com.firebase.functions"]) {
FIRFunctionsErrorCode code = error.code;
NSString *message = error.localizedDescription;
NSObject *details = error.userInfo[@"details"];
}
// ...
}
self->_resultField.text = result.data[@"text"];
}];
Web
var addMessage = firebase.functions().httpsCallable('addMessage');
addMessage({ text: messageText })
.then((result) => {
// Read result of the Cloud Function.
var sanitizedMessage = result.data.text;
});
Web
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);
}
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;
});
}
טיפול בשגיאות בצד הלקוח
הלקוח מקבל הודעת שגיאה אם השרת החזיר שגיאה או אם הבטחה שהתקבלה נדחתה.
אם השגיאה שמוחזרת על ידי הפונקציה היא מסוג function.https.HttpsError
, הלקוח מקבל את השגיאות code
, message
ו-details
מהשגיאה בשרת. אחרת, השגיאה מכילה את ההודעה INTERNAL
ואת הקוד INTERNAL
. כך מטפלים בשגיאות בפונקציה שניתן להפעיל.
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 isEqual:@"com.firebase.functions"]) {
FIRFunctionsErrorCode code = error.code;
NSString *message = error.localizedDescription;
NSObject *details = error.userInfo[@"details"];
}
// ...
}
Web
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
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);
// ...
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
לפני שמשיקים את האפליקציה, כדאי להפעיל את App Check כדי להבטיח שרק האפליקציות שלכם יוכלו לגשת לנקודות הקצה (endpoint) של הפונקציות שניתנות לקריאה.