具有Edge函式的HTTP要求篩選器
瞭解如何使用AEM Edge函式實作HTTP請求篩選,以重寫或重新導向請求,或修改HTTP回應。
何時透過Edge函式篩選HTTP請求
AEM Edge函式可作為反向Proxy,在請求和回應抵達來源或使用者端之前對其執行進階處理。
這兩種情況包括:
在請求到達來源之前調整請求
在前往來源的途中攔截流量以:
- 重寫要求來源、路徑或查詢。 將請求路由到不同的後端、路徑或查詢字串,讓來源接收正確的資源。
- 重寫要求標頭。 在請求到達來源之前新增、移除或修改標頭。 例如,新增來源期望的地理或存取標題、移除來源忽略的標題,或變更標題值以改變請求行為。
在回應到達使用者端之前修改回應
在回訪使用者端(訪客)的途中攔截流量,以:
- 變更回應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()到來源之前建置具有不同來源、路徑、查詢或標題的新RequestResponsefetch()呼叫上設定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函式範例,以瞭解如何在AEM Edge函式處理常式中設定Sentinel標頭。
# 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規則的攔截器函式: