SDK ของไคลเอ็นต์ Cloud Functions for Firebase ช่วยให้คุณเรียกใช้ฟังก์ชันได้โดยตรงจากแอป Firebase หากต้องการเรียกใช้ฟังก์ชันจากแอปด้วยวิธีนี้ ให้เขียนและติดตั้งใช้งานฟังก์ชันที่เรียกใช้ได้ผ่าน HTTP ใน Cloud Functions จากนั้นเพิ่มตรรกะไคลเอ็นต์เพื่อเรียกใช้ฟังก์ชันจากแอป
โปรดทราบว่าฟังก์ชันที่เรียกใช้ได้ของ HTTP คล้ายกับฟังก์ชัน HTTP แต่ไม่เหมือนกัน หากต้องการใช้ฟังก์ชันที่เรียกใช้ได้ผ่าน HTTP คุณต้องใช้ SDK ของไคลเอ็นต์สำหรับแพลตฟอร์มของคุณร่วมกับ API แบ็กเอนด์ (หรือใช้โปรโตคอล) ฟังก์ชันที่เรียกใช้ได้มีความแตกต่างที่สำคัญจากฟังก์ชัน HTTP ดังนี้
- เมื่อใช้ Callable ระบบจะรวมโทเค็น Firebase Authentication, โทเค็น FCM และโทเค็น App Check (หากมี) ไว้ในคำขอโดยอัตโนมัติ
- ทริกเกอร์จะดีซีเรียลไลซ์เนื้อหาคำขอและตรวจสอบโทเค็นการตรวจสอบสิทธิ์โดยอัตโนมัติ
SDK Firebase สำหรับ Cloud Functions รุ่นที่ 2 ขึ้นไปจะทำงานร่วมกับ SDK ของไคลเอ็นต์ Firebase เวอร์ชันขั้นต่ำเหล่านี้เพื่อรองรับฟังก์ชันที่เรียกใช้ได้ของ HTTPS
- Firebase SDK สำหรับแพลตฟอร์ม Apple 13.1.0
- Firebase SDK สำหรับ Android 23.0.0
- Firebase Modular Web SDK เวอร์ชัน 9.7.0
หากต้องการเพิ่มฟังก์ชันการทำงานที่คล้ายกันลงในแอปที่สร้างบนแพลตฟอร์มที่ไม่รองรับ โปรดดูข้อกำหนดโปรโตคอลสำหรับ https.onCall ส่วนที่เหลือของคู่มือนี้จะให้วิธีการเขียน ติดตั้งใช้งาน และเรียกใช้ฟังก์ชันที่เรียกใช้ได้ผ่าน HTTP สำหรับแพลตฟอร์ม Apple, Android, เว็บ, C++ และ Unity
เขียนและติดตั้งใช้งานฟังก์ชันที่เรียกใช้ได้
ตัวอย่างโค้ดในส่วนนี้อิงตามตัวอย่างคู่มือเริ่มใช้งานฉบับย่อที่สมบูรณ์ ซึ่งสาธิตวิธีส่งคำขอไปยังฟังก์ชันฝั่งเซิร์ฟเวอร์และรับการตอบกลับโดยใช้ SDK ของไคลเอ็นต์รายการใดรายการหนึ่ง หากต้องการเริ่มต้นใช้งาน ให้นำเข้าโมดูลที่จำเป็นดังนี้
Node.js
// Dependencies for callable functions.
const {onCall, HttpsError} = require("firebase-functions/https");
const {logger} = require("firebase-functions");
// Dependencies for the addMessage function.
const {getDatabase} = require("firebase-admin/database");
const sanitizer = require("./sanitizer");
Python
# Dependencies for callable functions.
from firebase_functions import https_fn, options
# Dependencies for writing to Realtime Database.
from firebase_admin import db, initialize_app
Dart (เวอร์ชันทดลอง)
// Dependencies for callable functions.
import 'package:firebase_functions/firebase_functions.dart';
ใช้เครื่องจัดการคำขอสำหรับแพลตฟอร์มของคุณเพื่อสร้างฟังก์ชันที่เรียกใช้ได้ของ HTTPS เมธอดนี้ใช้พารามิเตอร์คำขอต่อไปนี้
Node.js
// Saves a message to the Firebase Realtime Database but sanitizes the
// text by removing swearwords.
exports.addmessage = onCall((request) => {
// ...
});
Python
@https_fn.on_call()
def addmessage(req: https_fn.CallableRequest) -> Any:
"""Saves a message to the Firebase Realtime Database but sanitizes the text
by removing swear words."""
Dart (เวอร์ชันทดลอง)
// Adds two numbers to each other.
firebase.https.onCall(name: 'addNumbers', (request, response) async {
พารามิเตอร์ request มีข้อมูลที่ส่งมาจากแอปไคลเอ็นต์ รวมถึงบริบทเพิ่มเติม เช่น สถานะการตรวจสอบสิทธิ์ ตัวอย่างเช่น สำหรับฟังก์ชันที่เรียกใช้ได้ซึ่งบันทึก SMS ไปยัง Realtime Database data อาจมีข้อความ SMS พร้อมกับข้อมูลการตรวจสอบสิทธิ์ใน auth ดังนี้
Node.js
// 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;
Python
# Message text passed from the client.
text = req.data["text"]
# Authentication / user information is automatically added to the request.
uid = req.auth.uid
name = req.auth.token.get("name", "")
picture = req.auth.token.get("picture", "")
email = req.auth.token.get("email", "")
Dart (เวอร์ชันทดลอง)
// Numbers passed from the client.
final data = request.data as Map<String, Object?>?;
final firstNumber = data?['firstNumber'];
final secondNumber = data?['secondNumber'];
// Authentication / user information is automatically added to the request.
final uid = request.auth?.uid;
final token = request.auth?.token;
final name = token?['name'];
final picture = token?['picture'];
final email = token?['email'];
// Use variables to suppress 'unused' lint warnings
print(
'User details: uid=$uid, name=$name, picture=$picture, email=$email',
);
ระยะห่างระหว่างตำแหน่งของฟังก์ชันที่เรียกใช้ได้กับตำแหน่งของไคลเอ็นต์ที่เรียกใช้อาจทำให้เกิดเวลาในการตอบสนองของเครือข่าย เพื่อเพิ่มประสิทธิภาพ ให้พิจารณาระบุตำแหน่งของฟังก์ชัน หากเป็นไปได้ และตรวจสอบว่าตำแหน่งของฟังก์ชันที่เรียกใช้ได้สอดคล้องกับตำแหน่งที่ตั้งไว้เมื่อคุณเริ่มต้น SDK ฝั่งไคลเอ็นต์
คุณสามารถแนบเอกสารรับรอง App Check เพื่อช่วยปกป้องทรัพยากรแบ็กเอนด์จากการละเมิด เช่น การฉ้อโกงผ่านการเรียกเก็บเงินหรือฟิชชิง ดูหัวข้อเปิดใช้การบังคับใช้ App Check สำหรับ Cloud Functions
ส่งผลลัพธ์กลับ
หากต้องการส่งข้อมูลกลับไปยังไคลเอ็นต์ ให้แสดงผลข้อมูลที่เข้ารหัส JSON ได้ เช่น หากต้องการแสดงผลลัพธ์ของการดำเนินการบวก ให้ใช้โค้ดต่อไปนี้
Node.js
// returning result.
return {
firstNumber: firstNumber,
secondNumber: secondNumber,
operator: "+",
operationResult: firstNumber + secondNumber,
};
Python
return {
"firstNumber": first_number,
"secondNumber": second_number,
"operator": "+",
"operationResult": first_number + second_number,
}
Dart (เวอร์ชันทดลอง)
// returning result.
return CallableResult({
'firstNumber': firstNumber,
'secondNumber': secondNumber,
'operator': '+',
'operationResult': firstNumber + secondNumber,
});
ระบบจะแสดงข้อความที่ผ่านการตรวจสอบจากตัวอย่างข้อความทั้งต่อไคลเอ็นต์และ Realtime Database ใน Node.js คุณสามารถดำเนินการนี้แบบอะซิงโครนัสโดยใช้ JavaScript Promise ดังนี้
Node.js
// 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};
})
Python
# Saving the new message to the Realtime Database.
sanitized_message = sanitize_text(text) # Sanitize message.
db.reference("/messages").push(
{ # type: ignore
"text": sanitized_message,
"author": {"uid": uid, "name": name, "picture": picture, "email": email},
}
)
print("New message written")
# Returning the sanitized message to the client.
return {"text": sanitized_message}
ฟังก์ชันของคุณต้องแสดงผลค่า หรือในกรณีของ Node.js ต้องแสดงผล Promise ที่แก้ไขด้วยค่า ไม่เช่นนั้น ฟังก์ชันอาจสิ้นสุดก่อนที่จะส่งข้อมูลกลับไปยังไคลเอ็นต์ ดูคำแนะนำได้ที่สิ้นสุดฟังก์ชัน
ส่งและรับผลลัพธ์การสตรีม
ฟังก์ชันที่เรียกใช้ได้มีกลไกในการจัดการผลลัพธ์การสตรีม หากคุณมีกรณีการใช้งานที่ต้องใช้การสตรีม คุณสามารถกำหนดค่าการสตรีมในคำขอที่เรียกใช้ได้ แล้วใช้เมธอดที่เหมาะสมจาก SDK ของไคลเอ็นต์เพื่อเรียกใช้ฟังก์ชัน
ส่งผลลัพธ์สตรีมมิง
หากต้องการสตรีมผลลัพธ์ที่สร้างขึ้นเมื่อเวลาผ่านไปอย่างมีประสิทธิภาพ เช่น จากคำขอ API แยกต่างหากจำนวนมากหรือ API ของ Generative AI ให้ตรวจสอบพร็อพเพอร์ตี้ acceptsStreaming ในคำขอที่เรียกใช้ได้ เมื่อตั้งค่าพร็อพเพอร์ตี้นี้เป็น true คุณจะสตรีมผลลัพธ์กลับไปยังไคลเอ็นต์ด้วย response.sendChunk() ได้
ตัวอย่างเช่น หากแอปจำเป็นต้องดึงข้อมูลพยากรณ์อากาศสำหรับสถานที่หลายแห่ง ฟังก์ชันที่เรียกใช้ได้จะส่งพยากรณ์อากาศของแต่ละสถานที่แยกกันไปยังไคลเอ็นต์ที่ขอการตอบกลับแบบสตรีมมิง แทนที่จะต้องรอจนกว่าคำขอพยากรณ์อากาศทั้งหมดจะได้รับการแก้ไข
exports.getForecast = onCall(async (request, response) => { if (request.data?.locations?.length < 1) { throw new HttpsError("invalid-argument", "Missing locations to forecast"); } // fetch forecast data for all requested locations const allRequests = request.data.locations.map( async ({latitude, longitude}) => { const forecast = await weatherForecastApi(latitude, longitude); const result = {latitude, longitude, forecast}; // clients that support streaming will have each // forecast streamed to them as they complete if (request.acceptsStreaming) { response.sendChunk(result); } return result; }, ); // Return the full set of data to all clients return Promise.all(allRequests); });
โปรดทราบว่าลักษณะการทำงานของ response.sendChunk() จะขึ้นอยู่กับรายละเอียดบางอย่างของคำขอของไคลเอ็นต์ ดังนี้
หากไคลเอ็นต์ขอการตอบกลับแบบสตรีมมิง
response.sendChunk(data)จะส่งข้อมูลทันทีหากไคลเอ็นต์ไม่ขอการตอบกลับแบบสตรีมมิง
response.sendChunk()จะไม่ดำเนินการใดๆ สำหรับการเรียกนั้น ระบบจะส่งคำตอบแบบเต็มเมื่อข้อมูลทั้งหมดพร้อมแล้ว
หากต้องการตรวจสอบว่าไคลเอ็นต์ขอคำตอบแบบสตรีมมิงหรือไม่ ให้ตรวจสอบพร็อพเพอร์ตี้ request.acceptsStreaming ตัวอย่างเช่น หาก request.acceptsStreaming เป็นเท็จ คุณอาจตัดสินใจข้ามงานที่ต้องใช้ทรัพยากรจำนวนมากซึ่งเกี่ยวข้องกับการเตรียมหรือส่งข้อมูลแต่ละส่วนโดยเฉพาะ เนื่องจากไคลเอ็นต์ไม่ได้คาดหวังการส่งข้อมูลแบบเพิ่มทีละส่วน
รับผลลัพธ์การสตรีม
ในสถานการณ์ทั่วไป ไคลเอ็นต์จะขอสตรีมมิงด้วยเมธอด .stream แล้วทำซ้ำผ่านผลลัพธ์ ดังนี้
Swift
func listenToWeatherForecast() async throws {
isLoading = true
defer { isLoading = false }
Functions
.functions(region: "us-central1")
let getForecast: Callable<WeatherRequest, StreamResponse<WeatherResponse, [WeatherResponse]>> = Functions.functions().httpsCallable("getForecast")
let request = WeatherRequest(locations: locations)
let stream = try getForecast.stream(request)
for try await response in stream {
switch response {
case .message(let singleResponse):
weatherData["\(singleResponse.latitude),\(singleResponse.longitude)"] = singleResponse
case .result(let arrayOfResponses):
for response in arrayOfResponses {
weatherData["\(response.latitude),\(response.longitude)"] = response
}
print("Stream ended.")
return
}
}
}
Web
// Get the callable by passing an initialized functions SDK.
const getForecast = httpsCallable(functions, "getForecast");
// Call the function with the `.stream()` method to start streaming.
const { stream, data } = await getForecast.stream({
locations: favoriteLocations,
});
// The `stream` async iterable returned by `.stream()`
// will yield a new value every time the callable
// function calls `sendChunk()`.
for await (const forecastDataChunk of stream) {
// update the UI every time a new chunk is received
// from the callable function
updateUi(forecastDataChunk);
}
// The `data` promise resolves when the callable
// function completes.
const allWeatherForecasts = await data;
finalizeUi(allWeatherForecasts);
วนซ้ำผ่าน stream async iterable ดังที่แสดง การรอ data
promise จะบ่งบอกให้ไคลเอ็นต์ทราบว่าคำขอเสร็จสมบูรณ์แล้ว
Kotlin
// Get the callable by passing an initialized functions SDK.
val getForecast = functions.getHttpsCallable("getForecast");
// Call the function with the `.stream()` method and convert it to a flow
getForecast.stream(
mapOf("locations" to favoriteLocations)
).asFlow().collect { response ->
when (response) {
is StreamResponse.Message -> {
// The flow will emit a [StreamResponse.Message] value every time the
// callable function calls `sendChunk()`.
val forecastDataChunk = response.message.data as Map<String, Any>
// Update the UI every time a new chunk is received
// from the callable function
updateUI(
forecastDataChunk["latitude"] as Double,
forecastDataChunk["longitude"] as Double,
forecastDataChunk["forecast"] as Double,
)
}
is StreamResponse.Result -> {
// The flow will emit a [StreamResponse.Result] value when the
// callable function completes.
val allWeatherForecasts = response.result.data as List<Map<String, Any>>
finalizeUI(allWeatherForecasts)
}
}
}
หากต้องการใช้ฟังก์ชันส่วนขยาย asFlow() ให้เพิ่มไลบรารี org.jetbrains.kotlinx:kotlinx-coroutines-reactive เป็นทรัพยากร Dependency ลงในไฟล์ build.gradle(.kts) ของแอป
Java
// Get the callable by passing an initialized functions SDK.
HttpsCallableReference getForecast = mFunctions.getHttpsCallable("getForecast");
getForecast.stream(
new HashMap<String, Object>() {{
put("locations", favoriteLocations);
}}
).subscribe(new Subscriber<StreamResponse>() {
@Override
public void onSubscribe(Subscription subscription) {
subscription.request(Long.MAX_VALUE);
}
@Override
public void onNext(StreamResponse streamResponse) {
if (streamResponse instanceof StreamResponse.Message) {
// The flow will emit a [StreamResponse.Message] value every time the
// callable function calls `sendChunk()`.
StreamResponse.Message response = (StreamResponse.Message) streamResponse;
Map<String, Object> forecastDataChunk =
(Map<String, Object>) response.getMessage().getData();
// Update the UI every time a new chunk is received
// from the callable function
updateUI(
(double) forecastDataChunk.get("latitude"),
(double) forecastDataChunk.get("longitude"),
(double) forecastDataChunk.get("forecast")
);
} else if(streamResponse instanceof StreamResponse.Result) {
// The flow will emit a [StreamResponse.Result] value when the
// callable function completes.
StreamResponse.Result response = (StreamResponse.Result) streamResponse;
List<Map<String, Object>> allWeatherForecasts =
(List<Map<String, Object>>) response.getResult().getData();
finalizeUI();
}
}
@Override
public void onError(Throwable throwable) {
// an error occurred in the function
}
@Override
public void onComplete() {
}
});
กำหนดค่า CORS (การแชร์ทรัพยากรแบบข้ามต้นทาง)
ใช้ตัวเลือก cors เพื่อควบคุมต้นทางที่เข้าถึงฟังก์ชันได้
โดยค่าเริ่มต้น ฟังก์ชันที่เรียกใช้ได้จะมีการกำหนดค่า CORS เพื่ออนุญาตคำขอจากต้นทางทั้งหมด หากต้องการอนุญาตคำขอแบบข้ามต้นทางบางรายการ แต่ไม่อนุญาตทั้งหมด ให้ส่งรายการโดเมนหรือนิพจน์ทั่วไปที่เฉพาะเจาะจงซึ่งควรได้รับอนุญาต เช่น
Node.js
const { onCall } = require("firebase-functions/v2/https");
exports.getGreeting = onCall(
{ cors: [/firebase\.com$/, "https://flutter.com"] },
(request) => {
return "Hello, world!";
}
);
Python
from firebase_functions import https_fn, options
@https_fn.on_call(
cors=options.CorsOptions(
cors_origins=[r"firebase\.com$", r"https://flutter\.com"],
cors_methods=["get", "post"],
)
)
def say_hello(req: https_fn.CallableRequest) -> Any:
return "Hello world!"
Dart (เวอร์ชันทดลอง)
import 'package:firebase_functions/firebase_functions.dart';
void main(List<String> args) {
fireUp(args, (firebase) {
firebase.https.onCall(
name: 'getGreeting',
options: CallableOptions(
cors: Cors([RegExp(r'^https:\/\/firebase\.com$'), 'https://flutter.com']),
),
(request) async {
return CallableResult('Hello, world!');
},
);
});
}
หากต้องการห้ามคำขอแบบข้ามต้นทาง ให้ตั้งค่านโยบาย cors เป็น false
จัดการข้อผิดพลาด
เพื่อให้แน่ใจว่าไคลเอ็นต์ได้รับรายละเอียดข้อผิดพลาดที่เป็นประโยชน์ ให้แสดงข้อผิดพลาดจากฟังก์ชันที่เรียกใช้ได้โดยการส่ง (หรือสำหรับ Node.js ให้แสดงผล Promise ที่ถูกปฏิเสธด้วย) อินสแตนซ์ของ functions.https.HttpsError หรือ https_fn.HttpsError
ข้อผิดพลาดมีแอตทริบิวต์ code ซึ่งอาจเป็นค่าใดค่าหนึ่งที่แสดงในรหัสสถานะ gRPC
ข้อผิดพลาดจะมีสตริง message ด้วย ซึ่งค่าเริ่มต้นจะเป็นสตริงว่าง นอกจากนี้ยังสามารถมีช่อง details ที่ไม่บังคับซึ่งมีค่าที่กำหนดเองได้ด้วย หากมีการแสดงข้อผิดพลาดอื่นที่ไม่ใช่ข้อผิดพลาดของ HTTPS จากฟังก์ชัน ไคลเอ็นต์จะได้รับข้อผิดพลาดพร้อมข้อความ INTERNAL และรหัส internal แทน
ตัวอย่างเช่น ฟังก์ชันอาจแสดงข้อผิดพลาดในการตรวจสอบข้อมูลและการตรวจสอบสิทธิ์พร้อมข้อความแสดงข้อผิดพลาดเพื่อส่งกลับไปยังไคลเอ็นต์ที่เรียกใช้ ดังนี้
Node.js
// 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.");
}
Python
# Checking attribute.
if not isinstance(text, str) or len(text) < 1:
# Throwing an HttpsError so that the client gets the error details.
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
message=(
'The function must be called with one argument, "text",'
" containing the message text to add."
),
)
# Checking that the user is authenticated.
if req.auth is None:
# Throwing an HttpsError so that the client gets the error details.
raise https_fn.HttpsError(
code=https_fn.FunctionsErrorCode.FAILED_PRECONDITION,
message="The function must be called while authenticated.",
)
Dart (เวอร์ชันทดลอง)
// Checking that attributes are present and are numbers.
if (firstNumber is! num || secondNumber is! num) {
// Throwing an HttpsError so that the client gets the error details.
throw InvalidArgumentError(
'The function must be called with two arguments "firstNumber" and "secondNumber" which must both be numbers.',
);
}
ทำให้ฟังก์ชันที่เรียกใช้ได้ใช้งานได้
หลังจากบันทึกฟังก์ชันที่เรียกใช้ได้ที่สมบูรณ์ภายใน index.js แล้ว ระบบจะทำให้ใช้งานได้พร้อมกับฟังก์ชันอื่นๆ ทั้งหมดเมื่อคุณเรียกใช้ firebase deploy
หากต้องการติดตั้งใช้งานเฉพาะฟังก์ชันที่เรียกใช้ได้ ให้ใช้อาร์กิวเมนต์ --only ตามที่แสดงเพื่อดำเนินการติดตั้งใช้งานบางส่วน
firebase deploy --only functions:addMessage
หากพบข้อผิดพลาดเกี่ยวกับสิทธิ์เมื่อติดตั้งใช้งานฟังก์ชัน ให้ตรวจสอบว่าได้มอบหมายบทบาท IAM ที่เหมาะสมให้กับผู้ใช้ที่เรียกใช้คำสั่งติดตั้งใช้งาน
ตั้งค่าสภาพแวดล้อมในการพัฒนาซอฟต์แวร์ของไคลเอ็นต์
ตรวจสอบว่าคุณมีคุณสมบัติตรงตามข้อกำหนดเบื้องต้นทั้งหมด จากนั้นเพิ่มทรัพยากร Dependency และไลบรารีของไคลเอ็นต์ที่จำเป็นลงในแอป
iOS+
ทำตามวิธีการเพื่อเพิ่ม Firebase ลงในแอป Apple
ใช้ Swift Package Manager เพื่อติดตั้งและจัดการทรัพยากร Dependency ของ Firebase
- ใน Xcode ขณะที่โปรเจ็กต์แอปเปิดอยู่ ให้ไปที่ไฟล์ > เพิ่มแพ็กเกจ
- เมื่อได้รับข้อความแจ้ง ให้เพิ่มที่เก็บ SDK ของ Firebase สำหรับแพลตฟอร์ม Apple ดังนี้
- เลือกคลัง Cloud Functions
- เพิ่มแฟล็ก
-ObjCลงในส่วน Other Linker Flags ของการตั้งค่าบิลด์ของเป้าหมาย - เมื่อเสร็จแล้ว Xcode จะเริ่มแก้ไขและดาวน์โหลดทรัพยากร Dependency ในเบื้องหลังโดยอัตโนมัติ
https://github.com/firebase/firebase-ios-sdk.git
Web
- ทำตามวิธีการเพื่อเพิ่ม Firebase ลงในเว็บแอป อย่าลืมเรียกใช้คำสั่งต่อไปนี้จากเทอร์มินัล
npm install firebase@13.1.0 --save
กำหนดให้ต้องใช้ทั้ง Firebase Core และ 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);
Android
ทำตามวิธีการเพื่อเพิ่ม Firebase ลงในแอป Android
ในไฟล์ Gradle ของโมดูล (ระดับแอป) (โดยปกติคือ
<project>/<app-module>/build.gradle.ktsหรือ<project>/<app-module>/build.gradle) ให้เพิ่มทรัพยากร Dependency สำหรับไลบรารี Cloud Functions สำหรับ Android เราขอแนะนำให้ใช้ Firebase Android BoM เพื่อควบคุมการกำหนดเวอร์ชันของไลบรารีdependencies { // Import the BoM for the Firebase platform implementation(platform("com.google.firebase:firebase-bom:35.0.2")) // 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 เวอร์ชันที่เข้ากันได้อยู่เสมอ
(ทางเลือก) เพิ่มทรัพยากร Dependency ของไลบรารี Firebase โดยไม่ใช้ BoM
หากเลือกที่จะไม่ใช้ Firebase BoM คุณต้องระบุเวอร์ชันของไลบรารี Firebase แต่ละเวอร์ชันในบรรทัดทรัพยากร Dependency
โปรดทราบว่าหากคุณใช้ไลบรารี 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:23.0.0") }
เริ่มต้น SDK ของไคลเอ็นต์
เริ่มต้นอินสแตนซ์ของ Cloud Functions:
Swift
lazy var functions = Functions.functions()
Objective-C
@property(strong, nonatomic) FIRFunctions *functions;
// ...
self.functions = [FIRFunctions functions];
Web
const app = initializeApp({
projectId: '### CLOUD FUNCTIONS PROJECT ID ###',
apiKey: '### FIREBASE API KEY ###',
authDomain: '### FIREBASE AUTH DOMAIN ###',
});
const functions = getFunctions(app);
Kotlin
private lateinit var functions: FirebaseFunctions // ... functions = Firebase.functions
Java
private FirebaseFunctions mFunctions; // ... mFunctions = FirebaseFunctions.getInstance();
เรียกใช้ฟังก์ชัน
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
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
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;
}
});
ตั้งค่าระยะหมดเวลาของไคลเอ็นต์
โดยค่าเริ่มต้น SDK ของไคลเอ็นต์ Cloud Functions จะใช้ระยะหมดเวลา 70 วินาทีสำหรับคำขอที่เรียกใช้ได้ หากฟังก์ชันที่เรียกใช้ได้ใช้เวลาดำเนินการนานกว่า 70 วินาที SDK ของฝั่งไคลเอ็นต์จะสิ้นสุดคำขอด้วยข้อผิดพลาด DEADLINE_EXCEEDED (deadline-exceeded) แม้ว่าคุณจะกำหนดค่าระยะหมดเวลาฝั่งเซิร์ฟเวอร์ให้นานกว่านั้นก็ตาม
หากต้องการรองรับฟังก์ชันที่เรียกใช้ได้ซึ่งทำงานเป็นเวลานาน คุณต้องเพิ่มระยะหมดเวลาทั้งในเซิร์ฟเวอร์และไคลเอ็นต์ โดยทำดังนี้
- เพิ่มระยะหมดเวลาของฟังก์ชันฝั่งเซิร์ฟเวอร์ (ดูหัวข้อตั้งค่าระยะหมดเวลาและการจัดสรรหน่วยความจำ)
- เพิ่มระยะหมดเวลาฝั่งไคลเอ็นต์เมื่อกำหนดค่าการอ้างอิงที่เรียกใช้ได้ในแอป โดยทำดังนี้
Swift
let callable = functions.httpsCallable("addMessage")
callable.timeoutInterval = 300 // 300 seconds (default is 70)
let result = try await callable.call(["text": text, "push": true])
Objective-C
FIRHTTPSCallable *callable = [self.functions HTTPSCallableWithName:@"addMessage"];
callable.timeoutInterval = 300; // 300 seconds (default is 70)
[callable callWithObject:@{@"text": text, @"push": @YES}
completion:^(FIRHTTPSCallableResult * _Nullable result, NSError * _Nullable error) {
// ...
}];
Web
var addMessage = firebase.functions().httpsCallable('addMessage', {
timeout: 300000, // 300 seconds in milliseconds (default is 70000)
});
addMessage({ text: messageText })
.then((result) => {
// ...
});
Web
import { getFunctions, httpsCallable } from "firebase/functions";
const functions = getFunctions();
const addMessage = httpsCallable(functions, 'addMessage', {
timeout: 300000, // 300 seconds in milliseconds (default is 70000)
});
const result = await addMessage({ text: messageText });
Kotlin
val callable = functions.getHttpsCallable("addMessage")
callable.setTimeout(300L, TimeUnit.SECONDS)
val result = callable.call(data).await()
Java
HttpsCallableReference callable = mFunctions.getHttpsCallable("addMessage");
callable.setTimeout(300L, TimeUnit.SECONDS);
Task<HttpsCallableResult> result = callable.call(data);
Dart
final callable = FirebaseFunctions.instance.httpsCallable(
'addMessage',
options: HttpsCallableOptions(
timeout: const Duration(seconds: 300),
),
);
final result = await callable.call({
'text': text,
'push': true,
});
แนะนำ: ป้องกันการละเมิดด้วย App Check
ก่อนเปิดตัวแอป คุณควรเปิดใช้ App Check เพื่อช่วยให้มั่นใจว่ามีเพียงแอปของคุณเท่านั้นที่เข้าถึงปลายทางของฟังก์ชันที่เรียกใช้ได้