包含Edge函数的HTTP请求过滤器
了解如何使用AEM Edge函数实施HTTP请求筛选,以重写或重定向请求,或修改HTTP响应。
何时使用Edge函数筛选HTTP请求
AEM Edge函数可以充当反向代理,在请求和响应到达源或客户端之前对其进行高级处理。
这两种情况是:
在请求到达源之前对其进行调整
在前往来源的路上截获流量以:
- 重写请求源、路径或查询。 将请求路由到不同的后端、路径或查询字符串,以便源接收正确的资源。
- 重写请求标头。 在请求到达源之前,添加、删除或修改标头。 例如,添加来源需要的地理位置或访问标头,删除来源忽略的标头,或更改标头值以改变请求行为。
在响应到达客户端之前对其进行修改
在返回客户端(访客)的路上拦截流量,以:
- 更改响应HTML。 在页面到达浏览器之前,在页眉和页脚中插入JavaScript、个性化内容、重写链接或拼合。
- 在边缘重定向。 从CDN提供大量旧版到新版URL重定向,尤其是当重定向规则需要编程逻辑时。
- 按请求上下文个性化。 对于公共访客,根据地理位置、设备或受众而改变页面或页面部分。 这适用于网站域上的发布流量,不适用于创作流量。
实施HTTP请求筛选
HTTP请求筛选使用两个文件。 config/cdn.yaml文件决定要拦截哪些流量并路由到您的AEM Edge函数。 src/index.js文件执行请求或响应调整。
原始选择器和函数名称必须对齐。 如果edgeFunctions.yaml声明my-edge-function,则原始选择器在cdn.yaml中使用edgefunction-my-edge-function。
配置CDN过滤器
# config/cdn.yaml (origin selector excerpt)
kind: "CDN"
version: "1"
data:
originSelectors:
rules:
- name: route-to-edge-function
when:
allOf:
- { reqProperty: tier, equals: "publish" } # publish traffic only; skip author
- { reqProperty: domain, equals: "www.example.com" } # your site hostname
- { reqProperty: originalPath, matches: "(/[^./]+|\\.html|/)$" } # page URLs; skip static assets (.css, .js, images, fonts)
# - { reqProperty: method, in: ["GET", "HEAD"] } # optional: navigation only; skip POST, PUT, and other methods with bodies
- { reqHeader: x-edgefunction-request, exists: false } # skip loopback requests to prevent infinite loops
action:
type: selectAemOrigin
originName: edgefunction-my-edge-function # edgefunction-<name-of-the-function>
如果任何条件失败,则请求永远不会到达AEM Edge函数。 从函数所需的最少条件开始,仅添加更多条件以提高路由精度。 有关所有支持的属性和运算符,请参阅原始选择器。
实施处理程序
每个AEM Edge函数都注册一个获取事件处理程序。 使用处理程序在请求到达源主机之前调整请求,在响应到达客户端之前修改响应,或同时修改两者。
以下代码片段演示了AEM Edge函数中的请求过滤和响应修改。
// src/index.js
addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));
async function handleRequest(event) {
const req = event.request;
const url = new URL(req.url);
try {
// ------------------------------------------------------------
// --- Request filtering: adjust before origin ---
// ------------------------------------------------------------
// Example: Rewrite origin, path, or query
// Replace origin based on some criteria such as path or query
const newOriginRequest = new Request(`https://origin.example.com${url.pathname}${url.search}`);
const newOriginResponse = await fetch(newOriginRequest);
// Return the new origin response
return newOriginResponse;
...
// Example: Rewrite request headers before reaching the origin
// Add or modify headers before the request reaches the origin, like adding an authorization token based on exchange with an external service.
const originRequest = new Request(req, {
headers: new Headers({ ...Object.fromEntries(req.headers), "Authorization": "Bearer <token>" }),
});
const originResponse = await fetch(originRequest);
return originResponse;
...
// ------------------------------------------------------------
// --- Response filtering: modify before client ---
// ------------------------------------------------------------
// Example: Change response HTML (fetch from origin, transform the body)
// Fetch the response from the origin, transform the body, and return the transformed response.
const originRequest = new Request(`https://origin.example.com${url.pathname}`);
const originResponse = await fetch(originRequest);
const transformedHtml = transformHtml(await originResponse.text());
return new Response(transformedHtml, { status: 200, headers: originResponse.headers });
...
// Example: Redirect at the edge
// Redirect the client to a new path, like a legacy URL to a new URL.
return Response.redirect("https://www.example.com/new-path", 301);
...
// Example: Personalize by request context (geo, device, or audience)
// Fetch the response from the origin, personalize the body, and return the personalized response.
const originRequest = new Request(`https://origin.example.com${url.pathname}`);
const originResponse = await fetch(originRequest);
const personalizedHtml = personalizeHtml(await originResponse.text());
return new Response(personalizedHtml, { status: 200, headers: originResponse.headers });
return new Response("Not implemented", { status: 501 });
} catch (err) {
console.log(err);
return new Response("Error", { status: 500 });
}
}
要点:
cdn.yaml路由中的when + allOf仅路由与您的AEM Edge函数匹配的流量fetch()之前生成一个新Request,其来源、路径、查询或标头不相同,以作为来源Responsefetch()调用上设置Sentinel标头,以便CDN将环回流量路由到源设计准则
- 筛选器应尽量缩小。
- 将导航功能限制为
GET和HEAD。 - 通过在内部
fetch()调用上设置Sentinel标头,避免在CDN环回上出现无限循环。 - 通过您的Cloud Manager配置管道部署更新的
cdn.yaml。
防止CDN环回上的无限循环
当AEM Edge函数通过CDN从源获取内容时,可能会发生无限循环。 该获取操作将重新进入CDN,匹配相同的原点选择器规则,然后路由返回函数。
要防止出现这种情况,您可以通过设置一个Sentinel(例如,x-edgefunction-request)标头,从源选择器规则中排除环回请求。 初始访客请求没有标头,达到了AEM Edge功能。 环回请求携带标头,未能满足条件,转而路由到原点。
以下代码和配置片段演示了如何防止在CDN环回上出现无限循环。
// src/index.js
addEventListener("fetch", (event) => event.respondWith(handleRequest(event)));
...
// In the handler: set the sentinel on the loopback fetch
const loopbackRequest = new Request(`https://www.example.com${url.pathname}`, {
headers: { "x-edgefunction-request": "true" },
});
await fetch(loopbackRequest);
有关如何在AEM Edge函数处理程序中设置Sentinel标头的信息,请参阅AEM Edge函数示例。
# config/cdn.yaml (origin selector excerpt)
kind: "CDN"
version: "1"
data:
originSelectors:
rules:
- name: route-to-edge-function
when:
allOf:
...
- { reqHeader: x-edgefunction-request, exists: false } # skip loopback requests to prevent infinite loops
...
请参阅AEM Edge函数示例,了解如何在CDN配置中排除已包含Sentinel标头的请求。
完整示例
AEM Edge函数示例存储库包含具有完整config/cdn.yaml规则的侦听器函数: