開發AEM Edge功能
我們的目標是建置動態Edge Delivery Services區塊,該區塊會呼叫AEM Edge函式以從協力廠商API擷取動態資料。
第一步是開發AEM Edge函式,此函式會公開一個端點,將兩個上游API呼叫合併為一個回應,並啟用CORS以進行本機開發。 如需要求與回應合約、端點比對及輸出fetch()基本資訊,請參閱使用Edge函式建置API端點。 此處僅涵蓋本教學課程的特定部分。
從樣板開始
您的AEM Edge Functions專案(複製於在Edge Delivery Services上設定AEM Edge Functions )隨附兩個範例路線: /hello-world和/weather,因此您在第一天有需要測試的專案。 兩者都不屬於實際專案,因此請先檢閱範本並移除您不需要的專案。
定義API合約
在寫入處理常式之前,請定義請求和回應圖形,這樣就能根據合約建立Edge Delivery Services區塊或任何其他呼叫者,而不需讀取實作。
端點: GET /api/frescopa/estimated-delivery
GET /api/frescopa/estimated-delivery?sku=house-blend-medium-roast&postcode=90210
postcode為必要項。 sku為選用專案,預設值為house-blend-medium-roast。
回應內文:
成功的回應會傳回此圖形:
// 200 OK
{
"sku": "house-blend-medium-roast",
"productName": "House Blend - Medium Roast",
"inventoryStatus": "in-stock", // or "low-stock", "out-of-stock"
"qtyLeft": 12,
"fulfillmentRegion": "West Coast Fulfillment",
"deliveryEta": "tomorrow",
"cutoffMessage": "Order by 2:00 PM for same-day dispatch.",
"message": "Arrives tomorrow in West Coast Fulfillment"
}
錯誤回應共用此圖形,每個失敗原因的code不同:
// 400 missing postcode, 404 unknown sku, 405 wrong method: same shape, different code
{
"error": "Missing required parameter",
"code": "MISSING_POSTCODE", // or "UNKNOWN_SKU", "METHOD_NOT_ALLOWED"
"message": "The postcode query parameter is required"
}
每個錯誤本文都包含code欄位,因此呼叫者可以在失敗原因上分支,而不是剖析message。 OPTIONS個要求會取得CORS預檢的個別回應,範圍在下方的為本機開發啟用CORS中。
實作商業邏輯
將index.js保留為僅限路由。 商務邏輯位於src/handlers/下,每個端點一個資料夾。
您將新增的檔案
src/
├── index.js # routes to a handler, nothing else
├── handlers/estimated-delivery/
│ ├── handler.js # orchestrates the two upstream calls
│ ├── responses.js # builds the success and error JSON payloads
│ └── constants.js # route path and query-param defaults
├── lib/
│ ├── api-client.js # shared per-API token loading
│ └── cors.js # origin allow-list, applied to every response
└── mocks/ # tutorial stand-ins for the two upstream APIs
├── catalog/product-api.js
└── delivery/delivery-api.js
處理常式與上游呼叫
handler.js中的處理常式接受sku和postcode,呼叫這兩個上游服務,並將其結果合併為一個回應:
// src/handlers/estimated-delivery/handler.js
async function estimatedDeliveryBySkuAndPostcodeHandler(req) {
const url = new URL(req.url);
const sku = (url.searchParams.get("sku") ?? DEFAULT_SKU).trim();
const postcode = (url.searchParams.get("postcode") ?? "").trim();
if (!postcode) {
return missingPostcodeError(sku);
}
// API 1: catalog lookup
const product = await getProductBySku(sku);
if (!product) {
return unknownSkuError(sku, postcode);
}
// API 2: fulfillment lookup
const fulfillment = await getEstimatedDeliveryByPostcode(postcode);
return json(buildSuccessResponse(product, fulfillment));
}
responses.js將錯誤和成功JSON圖形保留在處理常式之外,而constants.js保留路由路徑和預設SKU,因此handler.js將焦點放在雙呼叫序列上。 src/mocks/中的每個模型都會先載入自己的權杖,然後再傳回資料,也就是實際API使用者端會使用的形狀:
// src/mocks/catalog/product-api.js
export async function getProductBySku(sku) {
await getApiToken(SECRETS.CATALOG);
// Real API: uncomment and replace with your catalog endpoint.
// const response = await authorizedFetch(
// `https://api.example.com/products?sku=${encodeURIComponent(sku)}`,
// SECRETS.CATALOG,
// );
// return response.ok ? response.json() : null;
return PRODUCTS[sku] || null;
}
getApiToken()和authorizedFetch()在lib/api-client.js中上線。 product-api.js和delivery-api.js都共用此協助程式,因此每個上游API會保留自己的秘密存放區金鑰(CATALOG_API_TOKEN、DELIVERY_API_TOKEN),而不會複製驗證邏輯。 當您準備好以真正的API取代模擬的呼叫時,請依照使用Edge函式設定設定設定和密碼在部署的網站上進行密碼設定,然後取消註解上述區塊。
每個平台限制單一叫用為32個傳出擷取呼叫,因此這裡有兩個呼叫留下足夠的空間。
為本機開發啟用CORS
Edge Delivery Services區塊的本機開發伺服器在http://localhost:3000上執行。 AEM Edge函式的本機開發伺服器在http://127.0.0.1:7676上執行。 這兩者具有不同的來源,因此若沒有CORS標頭,瀏覽器會在您的程式碼看到請求之前加以封鎖。
根據cors.js中的允許清單檢查要求來源,並將相同的檢查套用至瀏覽器先傳送的OPTIONS預檢要求:
// src/lib/cors.js
const ALLOWED_ORIGIN_EXACT = new Set([
"http://localhost:3000",
"http://127.0.0.1:3000",
]);
function isAllowedOrigin(origin) {
return Boolean(origin) && ALLOWED_ORIGIN_EXACT.has(origin);
}
function applyCors(request, response) {
const origin = request.headers.get("Origin");
if (!isAllowedOrigin(origin)) {
return response;
}
const headers = new Headers(response.headers);
headers.set("access-control-allow-origin", origin);
headers.set("vary", "Origin");
return new Response(response.body, { status: response.status, headers });
}
function corsPreflightResponse(request) {
const origin = request.headers.get("Origin");
if (!isAllowedOrigin(origin)) {
return new Response(null, { status: 403 });
}
return new Response(null, {
status: 204,
headers: {
"access-control-allow-origin": origin,
"access-control-allow-methods": "GET, OPTIONS",
"access-control-allow-headers": "Content-Type",
vary: "Origin",
},
});
}
export { applyCors, corsPreflightResponse };
// src/index.js
if (url.pathname === "/api/frescopa/estimated-delivery") {
if (req.method === "OPTIONS") {
finalResponse = corsPreflightResponse(req);
} else if (req.method === "GET") {
finalResponse = await estimatedDeliveryBySkuAndPostcodeHandler(req);
}
}
finalResponse = applyCors(req, finalResponse);
參考實作使用規則運算式擴充ALLOWED_ORIGIN_EXACT,以允許部署的開發、測試和生產網域,因為對這些網域的請求是透過CDN的同源網域,技術上不需要CORS標頭,但明確比對它們會使允許清單自我記錄。 部署後,瀏覽器絕不會針對相同來源請求傳送CORS檢查,因此額外的標頭是無害的。 您可以保留CORS協助程式;它只會在要求的原點符合允許清單時新增標頭。
在fastly.toml中設定本機開發伺服器
fastly.toml設定由aio aem edge-functions serve啟動的本機開發伺服器。 在此教學課程中,其[local_server.secret_stores]區塊會提供每個上游API一個本機權杖,因此處理常式可以在沒有真正認證的情況下呼叫SecretStoreManager.getSecret():
# fastly.toml
[local_server.secret_stores]
[[local_server.secret_stores.secret_default]]
key = "CATALOG_API_TOKEN"
data = "catalog-tutorial-token"
[[local_server.secret_stores.secret_default]]
key = "DELIVERY_API_TOKEN"
data = "delivery-tutorial-token"
樣板也會針對天氣樣本的Open-Meteo API宣告[local_server.backends]專案。 由於本教學課程會模擬兩個上游呼叫,而非呼叫真正的主機,因此請移除該專案,而非重新指向該專案。
如需完整的[local_server]選項集,請參閱Fastly.toml參考。
在本機測試端點
此時,您僅測試API合約:端點的要求與回應形狀,與稍後將呼叫它的Edge Delivery Services區塊無關。
從在Edge Delivery Services上設定AEM Edge功能啟動本機開發伺服器:
$ aio aem edge-functions serve
使用curl呼叫快樂路徑,或在瀏覽器中開啟URL:
$ curl "http://127.0.0.1:7676/api/frescopa/estimated-delivery?sku=house-blend-medium-roast&postcode=90210"
確認回應會合併來自兩個上游呼叫的資料,例如來自目錄查詢的產品名稱以及來自相同JSON內文中的履行查詢的傳遞預估。
然後確認錯誤路徑responses.js組建:
# missing postcode
$ curl "http://127.0.0.1:7676/api/frescopa/estimated-delivery?sku=house-blend-medium-roast"
# unknown SKU
$ curl "http://127.0.0.1:7676/api/frescopa/estimated-delivery?sku=not-a-real-product&postcode=90210"
每個都應該傳回具有code欄位(MISSING_POSTCODE, UNKNOWN_SKU)的JSON錯誤內文,而不是泛型500,以確認處理常式在輸入到達上游呼叫之前驗證輸入。
新增端點
兩個設定檔宣告路由,遵循建置API端點中的模式。
config/edgeFunctions.yaml宣告AEM Edge函式本身,與樣板未變更:
# config/edgeFunctions.yaml
kind: "EdgeFunctions"
version: "1"
data:
functions:
- name: my-edge-function
config/cdn.yaml新增來源選取器規則,將此教學課程的路徑轉送至該函式:
# config/cdn.yaml
- name: route-estimated-delivery-to-edge-function
when: { reqProperty: path, equals: "/api/frescopa/estimated-delivery" }
action:
type: selectAemOrigin
originName: edgefunction-my-edge-function
skipCache: true
檢視參考實作中的完整檔案: config/edgeFunctions.yaml和config/cdn.yaml。