开发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。