EDGE Functionsを使用したHTTP リクエストフィルター

IMPORTANT
AEM Edge Functionsは現在ベータ版です。 機能やドキュメントは変更される可能性があります。 フィードバックについては、aemcs-edgecompute-feedback@adobe.comまでお問い合わせください。

AEM Edge Functionsを使用してHTTP リクエストフィルタリングを実装し、リクエストを書き換えたりリダイレクトしたり、HTTP レスポンスを変更したりする方法を説明します。

Edge FunctionsでHTTP リクエストをフィルタリングするタイミング

AEM Edge関数は、リバースプロキシとして機能し、リクエストとレスポンスがオリジンまたはクライアントに到達する前に、リクエストとレスポンスに対して高度な処理を実行できます。

2つのシナリオは次のとおりです。

リクエストがオリジンに到達する前に調整します

発信元に向かう途中のトラフィックをインターセプトして、以下を行います。

  • リクエストのオリジン、パス、またはクエリを書き換えます。 リクエストを別のバックエンド、パス、またはクエリ文字列にルーティングして、オリジンが正しいリソースを受け取るようにします。
  • リクエストヘッダーを書き換えます。 リクエストがオリジンに到達する前に、ヘッダーを追加、削除、または変更します。 例えば、オリジンが期待する地域ヘッダーまたはアクセスヘッダーを追加したり、オリジンが無視するヘッダーを削除したり、ヘッダー値を変更してリクエスト動作を変更したりします。

クライアントに到達する前に応答を変更

クライアント(訪問者)に戻る途中のトラフィックを傍受して、次のことを行います。

  • HTMLの応答を変更します。 JavaScriptを挿入するか、パーソナライズされたコンテンツを読み込み、リンクを書き換えるか、ページがブラウザーに到達する前にヘッダーとフッターを合成します。
  • エッジでリダイレクトします。 特に、リダイレクトルールにプログラマティックロジックが必要な場合は、CDNから従来のURLから新しいURLへのリダイレクトを大量に配信します。
  • 要求コンテキストによるパーソナライズ。 公開訪問者に対して、位置情報、デバイス、オーディエンスごとにページやセクションを変更します。 これは、オーサートラフィックではなく、サイトドメイン上の公開トラフィックに適用されます。

HTTP リクエストフィルタリングの実装

HTTP リクエストフィルタリングでは、2つのファイルを使用します。 config/cdn.yaml ファイルは、AEM Edge関数をインターセプトしてルーティングするトラフィックを決定します。 src/index.js ファイルは、リクエストまたは応答の調整を実行します。

オリジン セレクターと関数名は整列する必要があります。 edgeFunctions.yamlmy-edge-functionを宣言した場合、オリジン セレクターはcdn.yamledgefunction-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 フィルター
cdn.yamlwhen + allOfは、AEM Edge関数に一致するトラフィックのみをルーティングします
リクエストのフィルタリング
新しいRequestを作成します。作成元fetch()の前に、別のオリジン、パス、クエリ、またはヘッダーを使用します
応答フィルタリング
変換されたHTML、リダイレクト、またはパーソナライズされたコンテンツで新しいResponseを返します
CDN ループバック
内部fetch()呼び出しに対してセンチネルヘッダーを設定し、CDNがループバックトラフィックをオリジンにルーティングするようにします

デザインガイドライン

  • フィルターは実用的な範囲に絞ります。
  • ナビゲーション関数をGETおよびHEADに制限します。
  • 内部fetch()呼び出しにセンチネルヘッダーを設定することで、CDN ループバックでの無限ループを回避します。
  • 更新されたcdn.yamlをCloud Manager設定パイプラインからデプロイします。

CDN ループバックでの無限ループの防止

無限ループは、AEM Edge関数がCDNを介してオリジンからコンテンツを取得する場合に発生する可能性があります。 そのフェッチはCDNに再入力され、同じオリジン セレクタ ルールに一致し、関数に戻されます。

これを防ぐには、センチネル(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関数の例を参照してください。

# 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設定でセンチネルヘッダーを既に含んでいるリクエストを除外する方法を確認してください。

完全な例

AEM Edge Functionsの例 リポジトリには、完全なconfig/cdn.yaml ルールを持つインターセプター関数が含まれています。

リポジトリ
事例を見る
HTML変圧器
publish-delivery-transformer
HTML レスポンスにJavaScript スニペットを挿入し、CDN ループバックを防ぐ​AEM as a Cloud Service サイトのページフィルターを公開します
HTML変圧器
edge-delivery-transformer
HTML レスポンスにサイトヘッダーとフッターを挿入する​Edge Delivery Services サイトのページフィルターを公開
リダイレクトマップ検索
publish-delivery-redirect-maps
レガシーURLにリダイレクトし、CDN ループバックを防ぐGET/HEAD ページフィルター

その他のリソース

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