開發AEM Edge功能

IMPORTANT
AEM Edge功能目前仍在測試階段。 功能和檔案可能會變更。 如需意見回饋,請連絡aemcs-edgecompute-feedback@adobe.com

我們的目標是建置動態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欄位,因此呼叫者可以在失敗原因上分支,而不是剖析messageOPTIONS個要求會取得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中的處理常式接受skupostcode,呼叫這兩個上游服務,並將其結果合併為一個回應:

// 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.jsdelivery-api.js都共用此協助程式,因此每個上游API會保留自己的秘密存放區金鑰(CATALOG_API_TOKENDELIVERY_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_POSTCODEUNKNOWN_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.yamlconfig/cdn.yaml

後續步驟

開發Edge Delivery Services區塊中,您將呼叫此端點的區塊製成支架,然後在通用編輯器中編寫它。

其他資源

recommendation-more-help
experience-manager-learn-help-cloud-service