

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 使用函數在邊緣自訂
<a name="edge-functions"></a>

使用 Amazon CloudFront，您可以撰寫自己的程式碼，以自訂 CloudFront 分發處理 HTTP 請求和回應的方式。程式碼會靠近檢視器 (使用者) 執行，以將延遲降至最低，而且您不必管理伺服器或其他基礎設施。您可以撰寫程式碼可以操作流經 CloudFront 的請求和回應，執行基本身分驗證和授權，在邊緣產生 HTTP 回應等。

您撰寫並附加至 CloudFront 分發的程式碼就稱為*邊緣函數*。CloudFront 提供兩種撰寫和管理邊緣函數的方式：

**CloudFront Functions**  
您可以在 JavaScript 中撰寫輕量型函數，以進行高度擴展及延遲敏感的 CDN 自訂。CloudFront Functions 執行時間環境提供低於一毫秒的啟動時間，可立即擴展以每秒處理數百萬個要求，並且非常安全。CloudFront Functions 是 CloudFront 的原生功能，這表示您可以在 CloudFront 中完全建置、測試和部署程式碼。

**Lambda@Edge**  
Lambda@Edge 是 [AWS Lambda](https://aws.amazon.com/lambda/) 的延伸，為複雜函數提供強大且靈活的運算，同時提供更靠近檢視器的完整應用程式邏輯，並且具有高度安全性。Lambda@Edge 函數在 Node.js 或 Python 執行時期環境中執行。您可以將它們發佈到單一 AWS 區域，但當您將函數與 CloudFront 分佈建立關聯時，Lambda@Edge 會自動將程式碼複寫到世界各地。

如果您在 CloudFront AWS WAF 上執行 ，則可以對 CloudFront Functions 和 Lambda@Edge 使用 AWS WAF 插入的標頭。這適用於檢視器和原始伺服器請求和回應。

**Topics**
+ [CloudFront Functions 與 Lambda@Edge 之間的差異](edge-functions-choosing.md)
+ [使用 CloudFront Functions 在邊緣進行自訂](cloudfront-functions.md)
+ [使用 CloudFront Connection Functions 自訂](customize-connections-validation-with-connection-functions.md)
+ [使用 Lambda@Edge 在邊緣自訂](lambda-at-the-edge.md)
+ [對邊緣函數的限制](edge-functions-restrictions.md)

# CloudFront Functions 與 Lambda@Edge 之間的差異
<a name="edge-functions-choosing"></a>

CloudFront Functions 和 Lambda@Edge 都提供了一種方法來執行回應 CloudFront 事件的程式碼。

CloudFront Functions 適合在以下使用案例用於輕量型且執行時間較短的函數：
+ **快取金鑰標準化**：轉換 HTTP 請求屬性 (標頭、查詢字串、Cookie，甚至 URL 路徑)，以建立最佳[快取金鑰](understanding-the-cache-key.md)，這可提升您的快取命中率。
+ **標頭操作**：在請求或回應中插入、修改或刪除 HTTP 標頭。例如，您可以為每個請求新增一個 `True-Client-IP` 標頭。
+ **URL 重新導向或重寫**：根據請求中的資訊將檢視器重新導向至其他頁面，或將全部請求從一個路徑重寫至另一個路徑。
+ **請求授權**：透過檢查授權標頭或其他請求中繼資料驗證雜湊的授權記號，例如 JSON Web 記號 (JWT)。

若要開始使用 CloudFront Functions，請參閱[使用 CloudFront Functions 在邊緣進行自訂](cloudfront-functions.md)。

Lambda@Edge 非常適合下列使用案例：
+ 需要數毫秒或更長時間才能完成的函數
+ 需要可調整 CPU 或記憶體的函數
+ 依賴第三方程式庫的函數 （包括 AWS SDK，用於與其他程式庫整合 AWS 服務)
+ 需要網路存取才能使用外部服務進行處理的函數
+ 需要檔案系統存取或存取 HTTP 請求內文的函數

若要開始使用 Lambda@Edge，請參閱[使用 Lambda@Edge 在邊緣自訂](lambda-at-the-edge.md)。

為了協助您選擇適合使用案例的選項，請使用下表瞭解 CloudFront Functions 和 Lambda@Edge 之間的差異。如需更多資訊瞭解適用於原始修改協助程式方法的差異，請參閱 [在 CloudFront Functions 和 Lambda@Edge 之間進行選擇](helper-functions-origin-modification.md#origin-modification-considerations)。


|  | CloudFront Functions | Lambda@Edge | 
| --- | --- | --- | 
| 程式設計語言 | JavaScript (與 ECMAScript 5.1 相容) | Node.js 和 Python | 
| 事件來源 |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/AmazonCloudFront/latest/DeveloperGuide/edge-functions-choosing.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/AmazonCloudFront/latest/DeveloperGuide/edge-functions-choosing.html)  | 
|  支援 [Amazon CloudFront KeyValueStore](kvs-with-functions.md)  |  是 CloudFront KeyValueStore 僅支援 [JavaScript 執行時期 2.0](functions-javascript-runtime-20.md)  |  否  | 
| 擴展 | 每秒最多數百萬個請求 | 每秒每個區域最多 10,000 個請求 | 
| 函數持續時間 | 低於一毫秒 |  最長 30 秒 （檢視器請求和檢視器回應） 最多 30 秒 (原始伺服器請求和原始伺服器回應)  | 
|  最大函數記憶體大小  | 2 MB |  128 MB （檢視器請求和檢視器回應） 10,240 MB (10 GB) (原始伺服器請求和原始伺服器回應) 如需詳細資訊，請參閱[Lambda@Edge 的配額](cloudfront-limits.md#limits-lambda-at-edge)。  | 
| 函數程式碼和包含程式庫的最大規模 | 10 KB |  50 MB （檢視器請求和檢視器回應） 50 MB (原始伺服器請求和原始伺服器回應)  | 
| 網路存取 | 否 | 是 | 
| 檔案系統存取 | 否 | 是 | 
| 請求內文存取 | 否 | 是 | 
| 存取地理位置和裝置資料 | 是 |  否 (檢視器請求和檢視器回應) 是 (原始伺服器請求和原始伺服器回應)  | 
| 可以完全在 CloudFront 內建置和測試 | 是 | 否 | 
| 函數日誌記錄和指標 | 是 | 是 | 

# 使用 CloudFront Functions 在邊緣進行自訂
<a name="cloudfront-functions"></a>

使用 CloudFront Functions，您可以在 JavaScript 中撰寫輕量型函數，以進行高擴展性、延遲敏感的 CDN 自訂。您的函數可以操作流經 CloudFront 的請求和回應，執行基本身分驗證和授權，在邊緣產生 HTTP 回應等。CloudFront Functions 執行時間環境提供低於一毫秒的啟動時間，可立即擴展以每秒處理數百萬個要求，並且非常安全。CloudFront Functions 是 CloudFront 的原生功能，這表示您可以在 CloudFront 中完全建置、測試和部署程式碼。

當您將 CloudFront 函式與 CloudFront 分佈建立關聯時，CloudFront 會在 CloudFront 邊緣位置攔截請求和回應，並將它們傳遞給您的函數。發生下列事件時，您可以調用 CloudFront 函數：
+ 當 CloudFront 接收到來自檢視器的請求 (檢視器請求) 時
+ 在 CloudFront 傳回回應給檢視器 (檢視器回應) 之前
+ 在建立 TLS 連線 （連線請求） 期間 - 目前可用於交互 TLS (mTLS) 連線

如需 CloudFront Functions 的詳細資訊，請參閱下列主題：

**Topics**
+ [教學課程：使用 CloudFront Functions 建立簡單的函數](functions-tutorial.md)
+ [教學課程：建立包含鍵值的 CloudFront 函數](functions-tutorial-kvs.md)
+ [撰寫函數程式碼](writing-function-code.md)
+ [建立函數](create-function.md)
+ [測試函數](test-function.md)
+ [更新函數](update-function.md)
+ [發佈函數](publish-function.md)
+ [將函數與分佈相關聯](associate-function.md)
+ [Amazon CloudFront KeyValueStore](kvs-with-functions.md)

# 教學課程：使用 CloudFront Functions 建立簡單的函數
<a name="functions-tutorial"></a>

本教學課程將協助您開始使用 CloudFront Functions。您可建立簡單函數將檢視器重新導向至不同的 URL，此外也會傳回自訂回應標頭。

**Contents**
+ [先決條件](#functions-tutorial-prerequisites)
+ [建立函數](#functions-tutorial-create)
+ [驗證函數](#functions-tutorial-verify)

## 先決條件
<a name="functions-tutorial-prerequisites"></a>

要使用 CloudFront Functions，您需要一個 CloudFront 分佈。如果您沒有帳戶，請參閱 [開始使用 CloudFront 標準分佈](GettingStarted.SimpleDistribution.md)。

## 建立函數
<a name="functions-tutorial-create"></a>

您可使用 CloudFront 主控台建立簡單函數，該函數將檢視器重新導向至不同的 URL，以及傳回自訂回應標頭。

**建立 CloudFront 函數**

1. 登入 AWS 管理主控台 ，並在 開啟 CloudFront 主控台[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在導覽面板上選擇**函數**，然後選擇**建立函數**。

1. 在**建立函數**頁面對**名稱**輸入函數名稱，例如 *MyFunctionName*。

1. (選用) 對於**說明**，請輸入函數的說明內容，例如 **Simple test function**。

1. 對於**執行時期**，請保留預設選取的 JavaScript 版本。

1. 選擇**建立函數**。

1. 複製下列函數程式碼。此函數程式碼會將檢視者重新導向至不同的 URL，並傳回自訂回應標頭。

   ```
   function handler(event) {
       // NOTE: This example function is for a viewer request event trigger. 
       // Choose viewer request for event trigger when you associate this function with a distribution. 
       var response = {
           statusCode: 302,
           statusDescription: 'Found',
           headers: {
               'cloudfront-functions': { value: 'generated-by-CloudFront-Functions' },
               'location': { value: 'https://aws.amazon.com/cloudfront/' }
           }
       };
       return response;
   }
   ```

1. 對於**函數程式碼**，請將程式碼貼到程式碼編輯器中，以取代預設程式碼。

1. 選擇**儲存變更**。

1. (選用) 您可以在發佈之前測試函數。本教學課程不會介紹如何測試函數。如需詳細資訊，請參閱[測試函數](test-function.md)。

1. 選擇**發佈**索引標籤，然後選擇**發佈函數**。您*必須*先發佈該函數，才能將其與 CloudFront 分佈建立關聯。

1. 接下來您可以將函數與分佈或快取行為建立關聯。在 *MyFunctionName* 頁面選擇**發佈**索引標籤。
**警告**  
在下列步驟中，選擇用於測試的分佈或快取行為。請勿將此測試函數與正式作業環境中使用的分佈或快取行為建立關聯。

1. 選擇 **Add association (建立關聯)**。

1. 在**關聯**對話方塊中，選擇分佈和/或快取行為。對於**事件類型**，請保留預設值。

1. 選擇 **Add association (建立關聯)**。

   **關聯的分佈**資料表中會顯示關聯的分佈。

1. 等待幾分鐘，讓關聯的分佈完成部署。若要檢查分佈狀態，請在**關聯的分佈**資料表中選取分佈，然後選擇**檢視分佈**。

   當分佈的狀態為**已部署**時，您就可以確認該函數正常運作。

## 驗證函數
<a name="functions-tutorial-verify"></a>

您部署函數之後，可以驗證該函數是否為您的分佈發揮作用。

**驗證函數**

1. 在 Web 瀏覽器中，導覽至分佈的網域名稱 (例如 `https://d111111abcdef8.cloudfront.net`)。

   該函數傳回重定向到瀏覽器，因此瀏覽器會自動轉到 `https://aws.amazon.com/cloudfront/`。

1. 您可在命令列視窗中使用 **curl** 之類的工具，將請求傳送至分佈的網域名稱。

   ```
   curl -v https://d111111abcdef8.cloudfront.net/
   ```

   您在回應中會看到重新導向回應 (`302 Found`) 和函數新增的自訂回應標頭。以下範例為可能的回應形式。  
**Example**  

   ```
   curl -v https://d111111abcdef8.cloudfront.net/
   > GET / HTTP/1.1
   > Host: d111111abcdef8.cloudfront.net
   > User-Agent: curl/7.64.1
   > Accept: */*
   >
   < HTTP/1.1 302 Found
   < Server: CloudFront
   < Date: Tue, 16 Mar 2021 18:50:48 GMT
   < Content-Length: 0
   < Connection: keep-alive
   < Location: https://aws.amazon.com/cloudfront/
   < Cloudfront-Functions: generated-by-CloudFront-Functions
   < X-Cache: FunctionGeneratedResponse from cloudfront
   < Via: 1.1 3035b31bddaf14eded329f8d22cf188c.cloudfront.net (CloudFront)
   < X-Amz-Cf-Pop: PHX50-C2
   < X-Amz-Cf-Id: ULZdIz6j43uGBlXyob_JctF9x7CCbwpNniiMlmNbmwzH1YWP9FsEHg==
   ```

# 教學課程：建立包含鍵值的 CloudFront 函數
<a name="functions-tutorial-kvs"></a>

本教學課程說明如何使用 CloudFront 函數納入鍵值。鍵值是鍵值對的一部分。您可以在函數程式碼中包含名稱 (來自鍵值對)。函數執行時，CloudFront 會以值取代該名稱。

鍵值對是存放在鍵值存放區中的變數。當您在函數中使用鍵 (而不是硬式編碼值) 時，您的函數會更靈活。您可以變更鍵的值，而不需要部署程式碼變更。鍵值對也可以減少函數的大小。如需詳細資訊，請參閱[Amazon CloudFront KeyValueStore](kvs-with-functions.md)。

**Contents**
+ [先決條件](#functions-kvs-tutorial-prerequisites)
+ [建立鍵值存放區](#functions-kvs-tutorial-kvs-step)
+ [將鍵值對新增到存放區](#add-key-value-pairs-to-store)
+ [建立鍵值存放區與函數的關聯](#functions-kvs-tutorial-functions-step)
+ [測試並發佈函數程式碼](#test-and-publish-function-code)

## 先決條件
<a name="functions-kvs-tutorial-prerequisites"></a>

如果您是初次使用 CloudFront Functions 函數和鍵值存放區，建議您遵循 [教學課程：使用 CloudFront Functions 建立簡單的函數](functions-tutorial.md) 中的教學課程。

完成該教學課程後，您可以遵循本教學課程來延伸您建立的函數。在本教學課程中，建議您先建立鍵值存放區。

## 建立鍵值存放區
<a name="functions-kvs-tutorial-kvs-step"></a>

首先請建立用於函數的鍵值存放區。

**建立鍵值存放區**

1. 規劃您要包含在函數中的鍵值對。請記下這些鍵的名稱。您要在函數中使用的所有鍵值對都必須位於單一鍵值存放區中。

1. 決定工作的順序。有兩種方式可以繼續：
   + 建立一個鍵值存放區，並將鍵值對新增到存放區中。然後建立 (或修改) 函數並加入鍵的名稱。
   + 或者，建立 (或修改) 函數，並加入您要使用的鍵的名稱。然後建立一個鍵值存放區，並新增鍵值對。

1. 登入 AWS 管理主控台 ，並在 開啟 CloudFront 主控台[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 在導覽視窗中選擇**函數**，然後選擇 **KeyValueStores** 索引標籤。

1. 選擇**建立 KeyValueStore** 並輸入下列欄位：
   + 輸入存放區的名稱及 (選用) 說明。
   + 將 **S3 URI** 保留空白。在本教學課程中，您將手動輸入鍵/值對。

1. 選擇**建立**。此時會顯示新鍵值存放區的詳細資訊頁面。此頁面包含目前空白的**鍵值對**區段。

## 將鍵值對新增到存放區
<a name="add-key-value-pairs-to-store"></a>

接著手動將鍵值對清單新增至您先前建立的鍵值存放區。

**將鍵值對新增到鍵值存放區**

1. 在**鍵值對**區段中選擇**新增鍵值對**。

1. 選擇**新增配對**，然後輸入鍵和值。選擇核取記號以確認您的變更，然後重複此步驟以新增更多鍵值對。

1. 完成後請選擇**儲存變更**，以儲存在鍵值存放區中的所有鍵值對。在確認對話上選擇**完成**。

您現在有一個包含一組鍵值對的鍵值存放區。



## 建立鍵值存放區與函數的關聯
<a name="functions-kvs-tutorial-functions-step"></a>

您現在已建立鍵值存放區。並且您已經建立或修改了一個函數，其中包含鍵值存放區中的鍵名稱。您現在可以建立鍵值存放區與函數的關聯。您從函數內建立該關聯。

**建立函數與鍵值存放區的關聯**

1. 在導覽視窗中，選擇**函數**。依據預設，**函數**索引標籤會顯示在頂端。

1. 選擇函數名稱，並在**關聯的 KeyValueStore** 區段中選擇**關聯現有 KeyValueStore**。

1. 選取鍵值存放區，然後選擇**關聯 KeyValueStore**。

**注意**  
每個函數只能與一個鍵值存放區建立關聯。

## 測試並發佈函數程式碼
<a name="test-and-publish-function-code"></a>

將鍵值存放區與函數建立關聯後，您可以測試和發佈函數程式碼。每次修改函數程式碼時，應一律對其進行測試，包括執行以下操作時：
+ 將鍵值存放區與函數建立關聯。
+ 修改函數及其鍵值存放區，以包含新的鍵值對。
+ 變更鍵值對的值。

**測試並發佈函數程式碼**

1. 如需有關如何測試函數的詳細資訊，請參閱 [測試函數](test-function.md)。確定您選擇在 `DEVELOPMENT` 階段測試函數。

1. 您準備好在 `LIVE` 環境中使用函數 (搭配新的或修訂的鍵值對) 時，請發佈該函數。

   當您發佈時，CloudFront 會將函數的版本從 `DEVELOPMENT` 階段複製到即時階段。該函數具有新程式碼，並與鍵值存放區相關聯。(在即時階段無需再次執行關聯。)

   如需有關如何發佈函數的詳細資訊，請參閱 [發佈函數](publish-function.md)。

# 撰寫函數程式碼
<a name="writing-function-code"></a>

您可以使用 CloudFront Functions 撰寫 JavaScript 的輕量型函數，以進行高擴展性、對延遲敏感的 CDN 自訂。您的函數程式碼可以操作流經 CloudFront 的請求和回應，執行基本身分驗證和授權，在邊緣產生 HTTP 回應等。

為協助您為 CloudFront Functions 撰寫函數程式碼，請參閱下列主題。如需程式碼範例，請參閱 [CloudFront 的 CloudFront Functions 範例](service_code_examples_cloudfront_functions_examples.md) 和 GitHub 上的 [amazon-cloudfront-functions 儲存庫](https://github.com/aws-samples/amazon-cloudfront-functions)。

**Topics**
+ [確定函數的用途](function-code-choose-purpose.md)
+ [事件結構](functions-event-structure.md)
+ [JavaScript 執行時期功能](functions-javascript-runtime-features.md)
+ [鍵值存放區的協助程式方法](functions-custom-methods.md)
+ [原始伺服器修改的 Helper 方法](helper-functions-origin-modification.md)
+ [CloudFront SaaS Manager 屬性的 helper 方法](saas-specific-logic-function-code.md)
+ [使用 async 和 await](async-await-syntax.md)
+ [CloudFront Functions 的 CWT 支援](cwt-support-cloudfront-functions.md)
+ [一般協助程式方法](general-helper-methods.md)

# 確定函數的用途
<a name="function-code-choose-purpose"></a>

在撰寫函數程式碼之前，請先確定函數的用途。CloudFront Functions 中的大多數函數都具有以下用途之一。

**Topics**
+ [修改檢視器請求事件類型中的 HTTP 請求](#function-code-modify-request)
+ [在檢視器請求事件類型中產生 HTTP 回應](#function-code-generate-response)
+ [在檢視者回應事件類型中修改 HTTP 回應](#function-code-modify-response)
+ [驗證連線請求事件類型的 mTLS 連線](#function-code-connection-request)
+ [相關資訊](#related-information-cloudfront-functions-purpose)

無論函數的用途如何，`handler` 都是任何函數的入口點。它採用一個稱為 `event` 的參數，該參數由 CloudFront 傳遞給函數。`event` 是一個 JSON 對象，其中包含 HTTP 請求的表示 (以及回應，前提是您的函數修改了 HTTP 回應)。

## 修改檢視器請求事件類型中的 HTTP 請求
<a name="function-code-modify-request"></a>

您的函數可以修改 CloudFront 從檢視者 (用戶端) 接收的 HTTP 請求，並將修改後的請求返回給 CloudFront 以繼續處理。例如，您的函數程式碼可能會標準化[快取金鑰](understanding-the-cache-key.md)或修改請求標頭。

建立及發布修改 HTTP 請求的函數時，請務必新增*檢視器請求*事件類型的關聯。如需詳細資訊，請參閱[建立函數](functions-tutorial.md#functions-tutorial-create)。這會使 CloudFront 每次收到來自檢視器的請求時執行該函數，然後再檢視請求的物件是否位於 CloudFront 快取中。

**Example 範例**  
下面的虛擬程式碼顯示了修改 HTTP 請求的函數結構。  

```
function handler(event) {
    var request = event.request;

    // Modify the request object here.

    return request;
}
```
該函數將修改後的 `request` 物件返回到 CloudFront。CloudFront 會按如下方式繼續處理返回的請求：檢查 CloudFront 快取以取得快取命中率，並在必要時將請求傳送至來源。

## 在檢視器請求事件類型中產生 HTTP 回應
<a name="function-code-generate-response"></a>

您的函數可以在邊緣產生 HTTP 回應，並直接將其返回給檢視者 (用戶端)，而無需檢查快取的回應，也無需 CloudFront 進一步處理。例如，您的函數程式碼可能會將請求重新導向至新的 URL，或檢查授權並將 `401` 或 `403` 回應返回給未經授權的請求。

建立產生 HTTP 回應的函數時，請務必選擇*檢視者請求*事件類型。這表示 CloudFront 每次收到來自檢視者的請求時，該函數就會執行，然後 CloudFront 才進一步處理請求。

**Example 範例**  
下面的虛擬程式碼顯示了產生 HTTP 回應的函數結構。  

```
function handler(event) {
    var request = event.request;

    var response = ...; // Create the response object here,
                        // using the request properties if needed.

    return response;
}
```
該函數將 `response` 物件返回到 CloudFront，CloudFront 立即將其返回給檢視者，而無需檢查 CloudFront 快取或將請求傳送至來源。

## 在檢視者回應事件類型中修改 HTTP 回應
<a name="function-code-modify-response"></a>

您的函數可以在 CloudFront 將其發送給檢視者 (用戶端) 之前修改 HTTP 回應，無論該回應來自 CloudFront 快取還是來源。例如，您的函數代碼可能會新增或修改回應標頭、狀態碼，與本文內容。

建立可修改 HTTP 回應的函數時，請務必選擇*檢視者回應*事件類型。這表示無論回應是來自 CloudFront 快取還是來源，此函數都會在 CloudFront 將回應返回給檢視者之前執行。

**Example 範例**  
下面的虛擬程式碼顯示了修改 HTTP 回應的函數結構。  

```
function handler(event) {
    var request = event.request;
    var response = event.response;

    // Modify the response object here,
    // using the request properties if needed.

    return response;
}
```
該函數將修改後的 `response` 物件返回到 CloudFront，CloudFront 立即將其返回給檢視者。

## 驗證連線請求事件類型的 mTLS 連線
<a name="function-code-connection-request"></a>

連線函數是一種在 TLS 連線期間執行的 CloudFront 函數類型，可提供自訂驗證和身分驗證邏輯。連線函數目前可用於交互 TLS (mTLS) 連線，您可以在其中驗證用戶端憑證，並實作超出標準憑證驗證的自訂身分驗證邏輯。在 TLS 交握程序期間執行的連線函數，可以根據憑證屬性、用戶端 IP 地址或其他條件允許或拒絕連線。

在您建立和發佈連線函數之後，請務必新增*連線請求*事件類型的關聯與啟用 mTLS 的分佈。這可讓函數在每次用戶端嘗試與 CloudFront 建立 mTLS 連線時執行。

**Example**  
下列虛擬程式碼顯示連線函數的結構：  

```
function connectionHandler(connection) {
    // Validate certificate and connection properties here.
    
    if (/* validation passes */) {
        connection.allow();
    } else {
        connection.deny();
    }
}
```
函數使用協助程式方法來判斷是否允許或拒絕連線。與檢視器請求和檢視器回應函數不同，連線函數無法修改 HTTP 請求或回應。

## 相關資訊
<a name="related-information-cloudfront-functions-purpose"></a>

如需使用 CloudFront Functions 的詳細資訊，請參閱下列主題：
+ [事件結構](functions-event-structure.md)
+ [JavaScript 執行時期功能](functions-javascript-runtime-features.md)
+ [CloudFront Functions 範例 ](service_code_examples_cloudfront_functions_examples.md)
+ [對邊緣函數的限制](edge-functions-restrictions.md)

# CloudFront Functions 事件結構
<a name="functions-event-structure"></a>

CloudFront Functions 在運行該函數時將 `event` 物件作為輸入傳遞給您的函數程式碼。[測試函數](test-function.md)時，建立 `event` 物件並將其傳遞給函數。建立用於測試函數的 `event` 物件時，您可以省略 `distributionDomainName` 物件中的 `distributionId`、`requestId` 和 `context` 欄位。此外，請確保標頭名稱為小寫，CloudFront Functions 在生產期間傳遞給函數的 `event` 物件中也會始終採用此形式。

以下是此事件物件結構的概觀。

```
{
    "version": "1.0",
    "context": {
        <context object>
    },
    "viewer": {
        <viewer object>
    },
    "request": {
        <request object>
    },
    "response": {
        <response object>
    }
}
```

如需詳細資訊，請參閱下列主題：

**Topics**
+ [版本欄位](#functions-event-structure-version)
+ [內容物件](#functions-event-structure-context)
+ [連線事件結構](#functions-event-structure-connection)
+ [檢視者物件](#functions-event-structure-viewer)
+ [請求物件](#functions-event-structure-request)
+ [回應物件](#functions-event-structure-response)
+ [狀態碼和本文](#functions-event-structure-status-body)
+ [查詢字串、標頭和 Cookie 的結構](#functions-event-structure-query-header-cookie)
+ [範例回應物件](#functions-response-structure-example)
+ [範例事件物件](#functions-event-structure-example)

## 版本欄位
<a name="functions-event-structure-version"></a>

`version` 欄位包含指定 CloudFront Functions 事件物件版本的字串。目前版本是 `1.0`。

## 內容物件
<a name="functions-event-structure-context"></a>

`context` 物件包含有關事件的關聯式資訊。它包括以下欄位：

**`distributionDomainName`**  
與事件關聯之標準分佈的 CloudFront 網域名稱 (例如 d111111abcdef8.cloudfront.net)。  
只有在您的函數為標準分佈調用時，`distributionDomainName` 欄位才會出現。

**`endpoint`**  
與事件關聯之連線群組的 CloudFront 網域名稱 (例如 d111111abcdef8.cloudfront.net)。  
只有在您的函數為多租用戶分佈調用時，`endpoint` 欄位才會出現。

**`distributionId`**  
與事件相關聯的發佈的 ID (例如 EDFDVBD6EXAMPLE)。

**`eventType`**  
事件類型，`viewer-request` 或 `viewer-response`。

**`requestId`**  
唯一識別 CloudFront 請求 (及其相關回應) 的字串。

## 連線事件結構
<a name="functions-event-structure-connection"></a>

連線函數會收到與檢視器函數不同的事件結構。如需連線事件結構和回應格式的詳細資訊，請參閱 [關聯 CloudFront 連線函數](connection-functions.md)。

## 檢視者物件
<a name="functions-event-structure-viewer"></a>

`viewer` 物件包含一個 `ip` 欄位，其值是傳送請求的檢視者 (用戶端) 的 IP 位址。如果檢視者的請求來自 HTTP 代理或負載平衡器，此值為代理或負載平衡器的 IP 位址。

## 請求物件
<a name="functions-event-structure-request"></a>

`request` 物件包含檢視者到 CloudFront HTTP 請求的表示。在傳遞給函數的 `event` 物件中，`request` 物件代表 CloudFront 從檢視者接收的實際請求。

如果您的函數程式碼將 `request` 物件返回到 CloudFront，其必須使用相同的結構。

`request` 物件包含下列欄位：

**`method`**  
請求的 HTTP 方法。如果您的函數程式碼傳回 `request`，則無法修改此欄位。這是 `request` 物件中唯一的唯讀欄位。

**`uri`**  
請求物件的相對路徑。  
如果您的函數修改 `uri` 值，就適用下列事項：  
+ 全新的 `uri` 值必須以正斜線 (`/`) 作為開頭。
+ 當函數變更 `uri` 值時，它會變更檢視者請求的物件。
+ 當函數變更 `uri` 值時，它不會變更請求的快取行為或原始伺服器請求傳送的來源。

**`querystring`**  
代表請求中的查詢字串的物件。如果請求不包含查詢字串，`request` 物件仍會包含空白的 `querystring` 物件。  
`querystring` 物件針對請求中的每個查詢字串參數包含一個欄位。

**`headers`**  
代表請求中 HTTP 標頭的物件。如果請求包含任何 `Cookie` 標頭，則這些標頭不是 `headers` 物件的一部分。Cookies 在 `cookies` 物件中單獨表示。  
`headers` 物件針對請求中的每個標頭包含一個欄位。在事件物件中，標頭名稱會轉換為 ASCII 小寫，而在函數程式碼新增標頭名稱時，標頭名稱必須是 ASCII 小寫。CloudFront Functions 將事件物件轉換回 HTTP 請求時，標頭名稱中每個單字的第一個字母會大寫 (若為 ASCII 字母)。CloudFront Functions 不會將任何變更套用至標頭名稱中的非 ASCII 符號。例如 `TÈst-header` 將在函數內部變為 `tÈst-header`。非 ASCII 符號 `È` 保持不變。  
單字以連字號分隔 (`-`)。例如，如果函數程式碼新增名為 `example-header-name` 的標頭，CloudFront 會將其轉換為 HTTP 請求中的 `Example-Header-Name`。

**`cookies`**  
代表請求 (`Cookie` 標頭) 中 Cookie 的物件。  
`cookies` 物件針對請求中的每個 Cookie 包含一個欄位。

如需有關查詢字串、標頭和 Cookie 結構的詳細資訊，請參閱 [查詢字串、標頭和 Cookie 的結構](#functions-event-structure-query-header-cookie)。

如需範例 `event` 物件，請參閱 [範例事件物件](#functions-event-structure-example)。

## 回應物件
<a name="functions-event-structure-response"></a>

`response` 物件包含 CloudFront 到檢視者的 HTTP 回應的表示。在傳遞給函數的 `event` 物件中，`response` 物件代表 CloudFront 對檢視者請求的實際回應。

如果您的函數程式碼返回 `response` 物件，其必須使用相同的結構。

`response` 物件包含下列欄位：

**`statusCode`**  
回應的 HTTP 狀態碼。該值是一個整數，而不是字串。  
您的函數可以產生或修改 `statusCode`。

**`statusDescription`**  
回應的 HTTP 狀態說明。如果您的函數程式碼產生回應，則此欄位為選用。

**`headers`**  
代表回應中 HTTP 標頭的物件。如果回應包含任何 `Set-Cookie` 標頭，則這些標頭不是 `headers` 物件的一部分。Cookies 在 `cookies` 物件中單獨表示。  
`headers` 物件針對回應中的每個標頭包含一個欄位。在事件物件中，標頭名稱會轉換為小寫，而在函數程式碼新增標頭名稱時，標頭名稱必須是小寫。當 CloudFront Functions 將事件物件轉換回 HTTP 回應時，標頭名稱中每個單字的第一個字母會大寫。單字以連字號分隔 (`-`)。例如，如果函數程式碼新增名為 `example-header-name` 的標頭，CloudFront 會將其轉換為 HTTP 回應中的 `Example-Header-Name`。

**`cookies`**  
代表回應 (`Set-Cookie` 標頭) 中 Cookie 的物件。  
`cookies` 物件針對回應中的每個 Cookie 包含一個欄位。

**`body`**  
新增 `body` 欄位是選擇性的，除非您在函數中進行指定，否則它不會出現在 `response` 物件中。您的函數無法存取 CloudFront 快取或原始伺服器傳回的原始本文。如果您未在檢視器回應函數中指定 `body` 欄位，則 CloudFront 快取或原始伺服器回傳的原始本文將傳回給檢視器。  
如果您希望 CloudFront 將自訂本文傳回給檢視器，請在 `data` 欄位中指定本文內容，並在 `encoding` 欄位中指定本文編碼。您可以將編碼指定為純文字 (`"encoding": "text"`) 或 Base64 編碼的內容 (`"encoding": "base64"`)。  
作為捷徑，您也可以直接在 `body` 欄位 (`"body": "<specify the body content here>"`) 中指定本文內容。執行此操作時，請省略 `data` 和 `encoding` 欄位。在這種情況下，CloudFront 會將本文視為純文字。    
`encoding`  
`body` 內容 (`data` 欄位) 的編碼。唯一的有效編碼是 `text` 和 `base64`。  
如果您將 `encoding` 指定為 `base64` 但本文不是有效的 base64，CloudFront 會傳回錯誤。  
`data`  
`body` 內容。

如需有關已修改狀態碼和本文內容的更多資訊，請參閱 [狀態碼和本文](#functions-event-structure-status-body)。

如需有關標頭和 Cookie 結構的詳細資訊，請參閱 [查詢字串、標頭和 Cookie 的結構](#functions-event-structure-query-header-cookie)。

如需範例 `response` 物件，請參閱 [範例回應物件](#functions-response-structure-example)。

## 狀態碼和本文
<a name="functions-event-structure-status-body"></a>

透過 CloudFront Functions，您可以更新檢視者回應本文或將其移除。在評估來自 CloudFront 快取或原始伺服器回應的各個層面後，更新檢視器回應的一些常見案例包括：
+ 變更狀態以設定 HTTP 200 狀態碼並建立靜態本文內容，以傳回給檢視器。
+ 變更狀態以設定 HTTP 301 或 302 狀態碼來重新導向使用者到另一個網站。
+ 決定是否要提供或捨棄檢視者回應的本文。

**注意**  
如果原始伺服器傳回 400 及以上的 HTTP 錯誤，CloudFront Function 將無法執行。如需更多資訊，請參閱[對所有邊緣函數的限制](edge-function-restrictions-all.md)。

在您使用 HTTP 回應時，CloudFront Functions 將無法存取回應本文。您可以透過將本文內容設定為所需的值來進行替換，或設定為空值來移除本文。如果您不更新函數中的本文欄位，CloudFront 快取或原始伺服器回傳的原始本文會傳回給檢視器。

**提示**  
使用 CloudFront Functions 取代本文時，請務必將對應的標頭 (例如 `content-encoding`、`content-type` 或 `content-length`) 對齊新的本文內容。  
例如，如果 CloudFront 原始伺服器或快取傳回 `content-encoding: gzip`，但檢視器回應函數設定了純文字的本文，則該函數也需要相應變更 `content-encoding` 和 `content-type` 標頭。

如果您的 CloudFront Function 設定為傳回 400 或更高的 HTTP 錯誤，您的檢視者將不會看到您為相同狀態碼指定的[自訂錯誤頁面](creating-custom-error-pages.md)。

## 查詢字串、標頭和 Cookie 的結構
<a name="functions-event-structure-query-header-cookie"></a>

查詢字串、標頭和 Cookie 共用相同的結構。查詢字串可能會出現在請求中。標頭會出現在請求和回應中。Cookie 會出現在請求和回應中。

每個查詢字串、標頭或 Cookie 都是父系 `querystring`、`headers` 或 `cookies` 物件中的唯一欄位。欄位名稱是查詢字串、標頭或 Cookie 的名稱。每個欄位都包含具有查詢字串、標頭或 Cookie 值的 `value` 屬性。

**Contents**
+ [查詢字串值或查詢字串物件](#functions-event-structure-query)
+ [標頭的特殊考量](#functions-event-structure-headers)
+ [重複的查詢字串、標頭和 Cookie (`multiValue` 陣列)](#functions-event-structure-multivalue)
+ [Cookie 屬性](#functions-event-structure-cookie-attributes)

### 查詢字串值或查詢字串物件
<a name="functions-event-structure-query"></a>

除了查詢字串物件之外，函數還可以傳回查詢字串值。查詢字串值可用來依任意自訂順序排列查詢字串參數。

**Example 範例**  
若要在函數程式碼中修改查詢字串，請使用如下所示的程式碼。  

```
var request = event.request; 
request.querystring = 'ID=42&Exp=1619740800&TTL=1440&NoValue=&querymv=val1&querymv=val2,val3';
```

### 標頭的特殊考量
<a name="functions-event-structure-headers"></a>

僅針對標頭，標頭名稱會在事件物件中轉換為小寫，而在函數程式碼新增標頭名稱時，標頭名稱則必須是小寫。當 CloudFront Functions 將事件物件轉換回 HTTP 請求或回應時，標頭名稱中每個單字的第一個字母會大寫。單字以連字號分隔 (`-`)。例如，如果函數程式碼新增名為 `example-header-name` 的標題，CloudFront 會將其轉換為 HTTP 請求或回應中的 `Example-Header-Name`。

**Example 範例**  
請考慮 HTTP 請求中的下列 `Host` 標頭。  

```
Host: video.example.com
```
此標頭在 `request` 物件中的表示方式如下：  

```
"headers": {
    "host": {
        "value": "video.example.com"
    }
}
```
若要在函數程式碼中存取 `Host` 標頭，請使用如下所示的程式碼：  

```
var request = event.request;
var host = request.headers.host.value;
```
若要在函數程式碼中新增或修改標頭，請使用如下所示的程式碼 (此程式碼會新增名為 `X-Custom-Header` 且包含值 `example value` 的標頭)：  

```
var request = event.request;
request.headers['x-custom-header'] = {value: 'example value'};
```

### 重複的查詢字串、標頭和 Cookie (`multiValue` 陣列)
<a name="functions-event-structure-multivalue"></a>

HTTP 請求或回應可以包含多個具有相同名稱的查詢字串、標頭或 Cookie。在此情況下，重複的查詢字串、標頭或 Cookie 會折疊成 `request` 或 `response` 物件中的一個欄位，但此欄位包含一個名為 `multiValue` 的額外屬性。`multiValue` 屬性包含一個陣列，其中帶有每個重複查詢字串、標頭或 Cookie 的值。

**Example 範例**  
請考慮包含下列 `Accept` 標頭的 HTTP 請求。  

```
Accept: application/json
Accept: application/xml
Accept: text/html
```
這些標頭在 `request` 物件中的表示方式如下。  

```
"headers": {
    "accept": {
        "value": "application/json",
        "multiValue": [
            {
                "value": "application/json"
            },
            {
                "value": "application/xml"
            },
            {
                "value": "text/html"
            }
        ]
    }
}
```

**注意**  
第一個標頭值 (在此情況為 `application/json`) 會在 `value` 和 `multiValue` 屬性中重複。這可讓您透過在 `multiValue` 陣列中執行迴圈來存取*所有*值。

如果您的函數程式碼修改具有 `multiValue` 陣列的查詢字串、標頭或 Cookie，CloudFront Functions 會使用下列規則來套用變更：

1. 如果 `multiValue` 陣列存在且有任何修改，則套用此修改。`value` 屬性中的第一個元素被忽略。

1. 否則，會套用 `value` 屬性的任何修改，並且後續的值 (如果存在) 保持不變。

只有當 HTTP 請求或回應包含具有相同名稱的重複查詢字串、標頭或 Cookie 時，才會使用 `multiValue` 屬性，如上述範例所示。但是，如果單一查詢字串、標頭或 Cookie 中有多個值，則不會使用該 `multiValue` 屬性。

**Example 範例**  
考慮具有一個 `Accept` 標頭的請求，而標頭中包含三個值。  

```
Accept: application/json, application/xml, text/html
```
此標頭在 `request` 物件中的表示方式如下。  

```
"headers": {
    "accept": {
        "value": "application/json, application/xml, text/html"
    }
}
```

### Cookie 屬性
<a name="functions-event-structure-cookie-attributes"></a>

在 HTTP 回應的 `Set-Cookie` 標頭中，標頭包含 Cookie 的名稱/值對，以及選用的一組屬性 (以分號分隔)。

**Example 範例**  

```
Set-Cookie: cookie1=val1; Secure; Path=/; Domain=example.com; Expires=Wed, 05 Apr 2021 07:28:00 GMT
```
在 `response` 物件中，這些屬性會在 Cookie 欄位的 `attributes` 屬性中表示。例如，前面的 `Set-Cookie` 標頭表示如下：  

```
"cookie1": {
    "value": "val1",
    "attributes": "Secure; Path=/; Domain=example.com; Expires=Wed, 05 Apr 2021 07:28:00 GMT"
}
```

## 範例回應物件
<a name="functions-response-structure-example"></a>

以下範例顯示一個本文已被檢視器回應函數替換的 `response` 物件 (檢視器回應函數的輸出)。

```
{
  "response": {
    "statusCode": 200,
    "statusDescription": "OK",
    "headers": {
      "date": {
        "value": "Mon, 04 Apr 2021 18:57:56 GMT"
      },
      "server": {
        "value": "gunicorn/19.9.0"
      },
      "access-control-allow-origin": {
        "value": "*"
      },
      "access-control-allow-credentials": {
        "value": "true"
      },
      "content-type": {
        "value": "text/html"
      },
      "content-length": {
        "value": "86"
      }
    },
    "cookies": {
      "ID": {
        "value": "id1234",
        "attributes": "Expires=Wed, 05 Apr 2021 07:28:00 GMT"
      },
      "Cookie1": {
        "value": "val1",
        "attributes": "Secure; Path=/; Domain=example.com; Expires=Wed, 05 Apr 2021 07:28:00 GMT",
        "multiValue": [
          {
            "value": "val1",
            "attributes": "Secure; Path=/; Domain=example.com; Expires=Wed, 05 Apr 2021 07:28:00 GMT"
          },
          {
            "value": "val2",
            "attributes": "Path=/cat; Domain=example.com; Expires=Wed, 10 Jan 2021 07:28:00 GMT"
          }
        ]
      }
    },
    
    // Adding the body field is optional and it will not be present in the response object
    // unless you specify it in your function.
    // Your function does not have access to the original body returned by the CloudFront
    // cache or origin.
    // If you don't specify the body field in your viewer response function, the original
    // body returned by the CloudFront cache or origin is returned to viewer.

     "body": {
      "encoding": "text",
      "data": "<!DOCTYPE html><html><body><p>Here is your custom content.</p></body></html>"
    }
  }
}
```

## 範例事件物件
<a name="functions-event-structure-example"></a>

以下範例顯示完整的 `event` 物件。這是標準分佈的範例調用，而不是多租用戶分佈。對於多租用戶分佈，會使用 `endpoint` 欄位而不是 `distributionDomainName`。`endpoint` 的值是與事件相關聯之連線群組的 CloudFront 網域名稱 (例如 d111111abcdef8.cloudfront.net)。

**注意**  
`event` 物件是函數的輸入。您的函數僅返回 `request` 或 `response` 物件，而不是完整的 `event` 物件。

```
{
    "version": "1.0",
    "context": {
        "distributionDomainName": "d111111abcdef8.cloudfront.net",
        "distributionId": "EDFDVBD6EXAMPLE",
        "eventType": "viewer-response",
        "requestId": "EXAMPLEntjQpEXAMPLE_SG5Z-EXAMPLEPmPfEXAMPLEu3EqEXAMPLE=="
    },
    "viewer": {"ip": "198.51.100.11"},
    "request": {
        "method": "GET",
        "uri": "/media/index.mpd",
        "querystring": {
            "ID": {"value": "42"},
            "Exp": {"value": "1619740800"},
            "TTL": {"value": "1440"},
            "NoValue": {"value": ""},
            "querymv": {
                "value": "val1",
                "multiValue": [
                    {"value": "val1"},
                    {"value": "val2,val3"}
                ]
            }
        },
        "headers": {
            "host": {"value": "video.example.com"},
            "user-agent": {"value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0"},
            "accept": {
                "value": "application/json",
                "multiValue": [
                    {"value": "application/json"},
                    {"value": "application/xml"},
                    {"value": "text/html"}
                ]
            },
            "accept-language": {"value": "en-GB,en;q=0.5"},
            "accept-encoding": {"value": "gzip, deflate, br"},
            "origin": {"value": "https://website.example.com"},
            "referer": {"value": "https://website.example.com/videos/12345678?action=play"},
            "cloudfront-viewer-country": {"value": "GB"}
        },
        "cookies": {
            "Cookie1": {"value": "value1"},
            "Cookie2": {"value": "value2"},
            "cookie_consent": {"value": "true"},
            "cookiemv": {
                "value": "value3",
                "multiValue": [
                    {"value": "value3"},
                    {"value": "value4"}
                ]
            }
        }
    },
    "response": {
        "statusCode": 200,
        "statusDescription": "OK",
        "headers": {
            "date": {"value": "Mon, 04 Apr 2021 18:57:56 GMT"},
            "server": {"value": "gunicorn/19.9.0"},
            "access-control-allow-origin": {"value": "*"},
            "access-control-allow-credentials": {"value": "true"},
            "content-type": {"value": "application/json"},
            "content-length": {"value": "701"}
        },
        "cookies": {
            "ID": {
                "value": "id1234",
                "attributes": "Expires=Wed, 05 Apr 2021 07:28:00 GMT"
            },
            "Cookie1": {
                "value": "val1",
                "attributes": "Secure; Path=/; Domain=example.com; Expires=Wed, 05 Apr 2021 07:28:00 GMT",
                "multiValue": [
                    {
                        "value": "val1",
                        "attributes": "Secure; Path=/; Domain=example.com; Expires=Wed, 05 Apr 2021 07:28:00 GMT"
                    },
                    {
                        "value": "val2",
                        "attributes": "Path=/cat; Domain=example.com; Expires=Wed, 10 Jan 2021 07:28:00 GMT"
                    }
                ]
            }
        }
    }
}
```

# 適用於 CloudFront Functions 的 JavaScript 執行時期功能
<a name="functions-javascript-runtime-features"></a>

CloudFront Functions JavaScript 執行時期環境符合 [ECMAScript (ES) 版本 5.1](https://www.ecma-international.org/ecma-262/5.1/)，並且支援 ES 版本 6 到 12 的一些功能。

我們建議您使用 JavaScript 執行時期 2.0，以獲得最新功能。

相較於 1.0，JavaScript 執行時期 2.0 功能有下列變更：
+ 可使用緩衝區模組方法
+ 以下非標準字串原型方法無法使用：
  + `String.prototype.bytesFrom()`
  + `String.prototype.fromBytes()`
  + `String.prototype.fromUTF8()`
  + `String.prototype.toBytes()`
  + `String.prototype.toUTF8()`
+ 密碼編譯模組有以下變更：
  + `hash.digest()`：如果未提供編碼，則傳回類型變更為 `Buffer`
  + `hmac.digest()`：如果未提供編碼，則傳回類型變更為 `Buffer`
+ 如需其他新功能的詳細資訊，請參閱 [適用於 CloudFront Functions 的 JavaScript 執行時期 2.0 功能](functions-javascript-runtime-20.md)。

**Topics**
+ [JavaScript 執行時期 1.0 功能](functions-javascript-runtime-10.md)
+ [JavaScript 執行時期 2.0 功能](functions-javascript-runtime-20.md)

# 適用於 CloudFront Functions 的 JavaScript 執行時期 1.0 功能
<a name="functions-javascript-runtime-10"></a>

CloudFront Functions JavaScript 執行時期環境符合 [ECMAScript (ES) 版本 5.1](https://262.ecma-international.org/5.1/)，並且支援 ES 版本 6 到 9 的一些功能。它還提供了一些不屬於 ES 規格的非標準方法。

下列主題列出所有支援的語言功能。

**Topics**
+ [核心功能](#writing-functions-javascript-features-core)
+ [基本物件](#writing-functions-javascript-features-primitive-objects)
+ [內建物件](#writing-functions-javascript-features-builtin-objects)
+ [錯誤類型](#writing-functions-javascript-features-error-types)
+ [全域變數](#writing-functions-javascript-features-globals)
+ [內建模組](#writing-functions-javascript-features-builtin-modules)
+ [限制功能](#writing-functions-javascript-features-restricted-features)

## 核心功能
<a name="writing-functions-javascript-features-core"></a>

支援 ES 的以下核心功能。

**類型**  
支援所有 ES 5.1 類型。這包括布林值、數字、字串、物件、陣列、函數、函數建構子和常規表達式。

**運算子**  
支援所有 ES 5.1 運算子。  
支援 ES 7 指數運算子 (`**`)。

**聲明**  
不支援 `const` 和 `let` 陳述式。
支援下列 ES 5.1 陳述式：  
+ `break`
+ `catch`
+ `continue`
+ `do-while`
+ `else`
+ `finally`
+ `for`
+ `for-in`
+ `if`
+ `return`
+ `switch`
+ `throw`
+ `try`
+ `var`
+ `while`
+ 標記的陳述式

**文字**  
支援 ES 6 範本文字：多行字串、表達式插補和巢狀範本。

**函數**  
支援所有 ES 5.1 函數功能。  
支援 ES 6 箭頭函數，支援 ES 6 剩餘參數語法。

**Unicode**  
來源文字和字串常值可以包含 Unicode 編碼的字元。也支援由六個字元 (例如 `\uXXXX`) 組成的 Unicode 字碼指標逸出序列。

**嚴格模式**  
函數按預設會在嚴格模式下運作，因此您不需要在函數程式碼中新增 `use strict` 陳述式。無法對此進行變更。

## 基本物件
<a name="writing-functions-javascript-features-primitive-objects"></a>

支援 ES 的以下基本物件。

**物件**  
支援物件上的以下 ES 5.1 方法：  
+ `create`( 不含屬性清單)
+ `defineProperties`
+ `defineProperty`
+ `freeze`
+ `getOwnPropertyDescriptor`
+ `getOwnPropertyNames`
+ `getPrototypeOf`
+ `hasOwnProperty`
+ `isExtensible`
+ `isFrozen`
+ `prototype.isPrototypeOf`
+ `isSealed`
+ `keys`
+ `preventExtensions`
+ `prototype.propertyIsEnumerable`
+ `seal`
+ `prototype.toString`
+ `prototype.valueOf`
支援物件上的以下 ES 6 方法：  
+ `assign`
+ `is`
+ `prototype.setPrototypeOf`
支援物件上的以下 ES 8 方法：  
+ `entries`
+ `values`

**字串**  
支援以下針對字串的 ES 5.1 方法：  
+ `fromCharCode`
+ `prototype.charAt`
+ `prototype.concat`
+ `prototype.indexOf`
+ `prototype.lastIndexOf`
+ `prototype.match`
+ `prototype.replace`
+ `prototype.search`
+ `prototype.slice`
+ `prototype.split`
+ `prototype.substr`
+ `prototype.substring`
+ `prototype.toLowerCase`
+ `prototype.trim`
+ `prototype.toUpperCase`
支援字串上的以下 ES 6 方法：  
+ `fromCodePoint`
+ `prototype.codePointAt`
+ `prototype.endsWith`
+ `prototype.includes`
+ `prototype.repeat`
+ `prototype.startsWith`
支援字串上的以下 ES 8 方法：  
+ `prototype.padStart`
+ `prototype.padEnd`
支援字串上的以下 ES 9 方法：  
+ `prototype.trimStart`
+ `prototype.trimEnd`
支援字串上的以下非標準方法：  
+ `prototype.bytesFrom(array | string, encoding)`

  從八位元陣列或編碼字串建立一個位元組字串。字串編碼選項為 `hex`、`base64` 和 `base64url`。
+ `prototype.fromBytes(start[, end])`

  從位元組字串建立 Unicode 字串，其中每個位元組都會以對應的 Unicode 字碼指標取代。
+ `prototype.fromUTF8(start[, end])`

  從 UTF-8 編碼的位元組字串建立一個 Unicode 字串。如果編碼不正確，則返回 `null`。
+ `prototype.toBytes(start[, end])`

  從 Unicode 字串建立一個位元組字串。所有字元均必須在 [0,255] 範圍內。如果不在此範圍內，則返回 `null`。
+ `prototype.toUTF8(start[, end])`

  從一個 Unicode 字串建立一個 UTF-8 編碼的位元組字串。

**數字**  
支援數字上的所有 ES 5.1 方法。  
支援數字上的以下 ES 6 方法：  
+ `isFinite`
+ `isInteger`
+ `isNaN`
+ `isSafeInteger`
+ `parseFloat`
+ `parseInt`
+ `prototype.toExponential`
+ `prototype.toFixed`
+ `prototype.toPrecision`
+ `EPSILON`
+ `MAX_SAFE_INTEGER`
+ `MAX_VALUE`
+ `MIN_SAFE_INTEGER`
+ `MIN_VALUE`
+ `NEGATIVE_INFINITY`
+ `NaN`
+ `POSITIVE_INFINITY`

## 內建物件
<a name="writing-functions-javascript-features-builtin-objects"></a>

支援 ES 的以下內建物件。

**數學**  
支援所有 ES 5.1 數學方法。  
在 CloudFront Functions 執行時間環境中，`Math.random()` 實作會使用植入函數執行時間戳記的 OpenBSD `arc4random`。
支援以下 ES 6 數學方法：  
+ `acosh`
+ `asinh`
+ `atanh`
+ `cbrt`
+ `clz32`
+ `cosh`
+ `expm1`
+ `fround`
+ `hypot`
+ `imul`
+ `log10`
+ `log1p`
+ `log2`
+ `sign`
+ `sinh`
+ `tanh`
+ `trunc`
+ `E`
+ `LN10`
+ `LN2`
+ `LOG10E`
+ `LOG2E`
+ `PI`
+ `SQRT1_2`
+ `SQRT2`

**日期**  
支援所有 ES 5.1 `Date` 功能。  
基於安全考量，在單一函數執行的生命週期內，`Date` 始終返回相同的值 (函數的開始時間)。如需詳細資訊，請參閱 [限制功能](#writing-functions-javascript-features-restricted-features)。

**函數**  
支援 `apply`、`bind` 和 `call` 方法。  
不支援函數建構子。

**常規表達式**  
支援所有 ES 5.1 常規表達式功能。常規表達式語言與 Perl 相容。支援 ES 9 命名的擷取群組。

**JSON**  
支援所有 ES 5.1 JSON 功能，包括 `parse` 和 `stringify`。

**Array**  
支援陣列上的以下 ES 5.1 方法：  
+ `isArray`
+ `prototype.concat`
+ `prototype.every`
+ `prototype.filter`
+ `prototype.forEach`
+ `prototype.indexOf`
+ `prototype.join`
+ `prototype.lastIndexOf`
+ `prototype.map`
+ `prototype.pop`
+ `prototype.push`
+ `prototype.reduce`
+ `prototype.reduceRight`
+ `prototype.reverse`
+ `prototype.shift`
+ `prototype.slice`
+ `prototype.some`
+ `prototype.sort`
+ `prototype.splice`
+ `prototype.unshift`
支援陣列上的以下 ES 6 方法：  
+ `of`
+ `prototype.copyWithin`
+ `prototype.fill`
+ `prototype.find`
+ `prototype.findIndex`
支援陣列上的以下 ES 7 方法：  
+ `prototype.includes`

**類型陣列**  
支援以下 ES 6 類型陣列：  
+ `Int8Array`
+ `Uint8Array`
+ `Uint8ClampedArray`
+ `Int16Array`
+ `Uint16Array`
+ `Int32Array`
+ `Uint32Array`
+ `Float32Array`
+ `Float64Array`
+ `prototype.copyWithin`
+ `prototype.fill`
+ `prototype.join`
+ `prototype.set`
+ `prototype.slice`
+ `prototype.subarray`
+ `prototype.toString`

**陣列緩衝區**  
支援 `ArrayBuffer` 上的以下方法：  
+ `prototype.isView`
+ `prototype.slice`

**Promise**  
支援 Promise 上的以下方法：  
+ `reject`
+ `resolve`
+ `prototype.catch`
+ `prototype.finally`
+ `prototype.then`

**加密**  
密碼編譯模組提供標準雜湊和雜湊型訊息身分驗證碼 (HMAC) 協助程式。您可以使用 `require('crypto')` 加載模組。此模組會公開下列方法，其行為與 Node.js 對應方法完全相同：  
+ `createHash(algorithm)`
+ `hash.update(data)`
+ `hash.digest([encoding])`
+ `createHmac(algorithm, secret key)`
+ `hmac.update(data)`
+ `hmac.digest([encoding])`
如需詳細資訊，請參閱內建模組一節中的 [加密 (雜湊和 HMAC)](#writing-functions-javascript-features-builtin-modules-crypto)。

**主控台**  
這是一個用於偵錯的協助程式物件。它僅支援 `log()` 方法以記錄日誌訊息。  
CloudFront Functions 不支援逗號語法，例如 `console.log('a', 'b')`。因此請改為使用 `console.log('a' + ' ' + 'b')` 格式。

## 錯誤類型
<a name="writing-functions-javascript-features-error-types"></a>

支援以下錯誤物件：
+ `Error`
+ `EvalError`
+ `InternalError`
+ `MemoryError`
+ `RangeError`
+ `ReferenceError`
+ `SyntaxError`
+ `TypeError`
+ `URIError`

## 全域變數
<a name="writing-functions-javascript-features-globals"></a>

支援 `globalThis` 物件。

支援以下 ES 5.1 全局函數：
+ `decodeURI`
+ `decodeURIComponent`
+ `encodeURI`
+ `encodeURIComponent`
+ `isFinite`
+ `isNaN`
+ `parseFloat`
+ `parseInt`

支援以下全局常數：
+ `NaN`
+ `Infinity`
+ `undefined`

## 內建模組
<a name="writing-functions-javascript-features-builtin-modules"></a>

支援以下內建模組：

**Topics**
+ [加密 (雜湊和 HMAC)](#writing-functions-javascript-features-builtin-modules-crypto)
+ [查詢字串](#writing-functions-javascript-features-builtin-modules-query-string)

### 加密 (雜湊和 HMAC)
<a name="writing-functions-javascript-features-builtin-modules-crypto"></a>

密碼編譯模組 (`crypto`) 提供標準雜湊和雜湊型訊息身分驗證碼 (HMAC) 協助程式。您可以使用 `require('crypto')` 加載模組。此模組提供下列方法，其行為與 Node.js 對應方法完全相同：

**雜湊方法**

`crypto.createHash(algorithm)`  
建立並傳回雜湊物件，藉助此物件，您可以使用給定的演算法產生雜湊摘要：`md5`、`sha1` 或 `sha256`。

`hash.update(data)`  
使用給定的 `data` 更新雜湊內容。

`hash.digest([encoding])`  
計算使用 `hash.update()` 傳遞的所有資料的摘要。編碼可以是 `hex`、`base64` 或 `base64url`。

**HMAC 方法**

`crypto.createHmac(algorithm, secret key)`  
建立並返回使用給定 `algorithm` 和 `secret key` 的 HMAC 物件。演算法可以是 `md5`、`sha1` 或 `sha256`。

`hmac.update(data)`  
使用給定的 `data` 更新 HMAC 內容。

`hmac.digest([encoding])`  
計算使用 `hmac.update()` 傳遞的所有資料的摘要。編碼可以是 `hex`、`base64` 或 `base64url`。

### 查詢字串
<a name="writing-functions-javascript-features-builtin-modules-query-string"></a>

**注意**  
[CloudFront Functions 事件物件](functions-event-structure.md)會自動剖析 URL 查詢字串。這意味著在大多數情況下，您不需要使用此模組。

查詢字串模組 (`querystring`) 提供剖析和格式化 URL 查詢字串的方法。您可以使用 `require('querystring')` 加載模組。此模組提供下列方法。

`querystring.escape(string)`  
URL 編碼給定的 `string`，傳回逸出的查詢字串。該方法由 `querystring.stringify()` 使用，並且不應直接使用。

`querystring.parse(string[, separator[, equal[, options]]])`  
剖析查詢字串 (`string`) 並傳回物件。  
`separator` 參數是用來分隔查詢字串中的鍵/值對的子字串。其在預設情況下為 `&`。  
`equal` 參數是用來分隔查詢字串中的鍵和值的子字串。其在預設情況下為 `=`。  
`options` 參數是具有下列鍵的物件：    
`decodeURIComponent function`  
解碼查詢字串中百分比編碼字元的函數。其在預設情況下為 `querystring.unescape()`。  
`maxKeys number`  
要剖析的鍵的最大數量。其在預設情況下為 `1000`。使用 `0` 的值移除計數鍵的限制。
根據預設，查詢字串中的百分比編碼字元會假設為使用 UTF-8 編碼。無效的 UTF-8 序列會被取代為 `U+FFFD` 取代字元。  
例如，對於下列查詢字串：  

```
'name=value&abc=xyz&abc=123'
```
`querystring.parse()` 的返回值是：  

```
{
name: 'value',
abc: ['xyz', '123']
}
```
`querystring.decode()` 是 的別名。`querystring.parse()`

`querystring.stringify(object[, separator[, equal[, options]]])`  
序列化 `object` 並傳回查詢字串。  
`separator` 參數是用來分隔查詢字串中的鍵/值對的子字串。其在預設情況下為 `&`。  
`equal` 參數是用來分隔查詢字串中的鍵和值的子字串。其在預設情況下為 `=`。  
`options` 參數是具有下列鍵的物件：    
`encodeURIComponent function`  
用於將 URL 不安全字元轉換為查詢字串中的百分比編碼的函數。其在預設情況下為 `querystring.escape()`。
根據預設，在查詢字串中需要百分比編碼的字元會編碼為 UTF-8。若要使用不同的編碼，請指定 `encodeURIComponent` 選項。  
例如，對於以下程式碼：  

```
querystring.stringify({ name: 'value', abc: ['xyz', '123'], anotherName: '' });
```
返回值是：  

```
'name=value&abc=xyz&abc=123&anotherName='
```
`querystring.encode()` 是 `querystring.stringify()` 的別名。

`querystring.unescape(string)`  
解碼給定 `string` 中的 URL 百分比編碼字元，傳回未逸出的查詢字串。此方法由 `querystring.parse()` 使用，並且不應直接使用。

## 限制功能
<a name="writing-functions-javascript-features-restricted-features"></a>

由於安全考量，下列 JavaScript 語言功能不受支援或受到限制。

**動態程式碼評估**  
不支援動態程式碼評估。如果嘗試此評估，`eval()` 和 `Function` 建構子都會丟出錯誤。例如，`const sum = new Function('a', 'b', 'return a + b')` 丟出錯誤。

**計時器**  
不支援 `setTimeout()`、`setImmediate()` 和 `clearTimeout()` 函數。在函數執行時期間未推遲或產生任何佈建。您的函數必須同步執行方可完成。

**日期和時間戳記**  
基於安全考量，無法存取高解析度計時器。查詢當前時間的所有 `Date` 方法始終在單個函數執行的生命週期內返回相同的值。返回的時間戳記是函數開始執行的時間。因此，您無法測量函數中的經過時間。

**檔案系統存取**  
沒有檔案系統存取權。例如，沒有類似 Node.js 中的檔案系統存取 `fs` 模組。

**處理存取**  
沒有處理存取。例如沒有 `process` 全域物件可像 Node.js 一樣處理資訊存取。

**環境變數**  
無法存取環境變數。  
您可以改為使用 CloudFront KeyValueStore 為您的 CloudFront Functions 建立鍵值對的集中式資料儲存。CloudFront KeyValueStore 可動態更新組態資料，不需要部署程式碼變更。您必須使用 [JavaScript 執行時期 2.0](functions-javascript-runtime-20.md)，才能使用 CloudFront KeyValueStore。如需詳細資訊，請參閱[Amazon CloudFront KeyValueStore](kvs-with-functions.md)。

**網路存取**  
不支援網路呼叫。例如，不支援 XHR、HTTP(S) 和通訊端。

# 適用於 CloudFront Functions 的 JavaScript 執行時期 2.0 功能
<a name="functions-javascript-runtime-20"></a>

CloudFront Functions JavaScript 執行時期環境符合 [ECMAScript (ES) 版本 5.1](https://262.ecma-international.org/5.1/)，並且支援 ES 版本 6 到 12 的一些功能。它還提供了一些不屬於 ES 規格的非標準方法。以下主題列出此執行時期支援的所有功能。

**Topics**
+ [核心功能](#writing-functions-javascript-features-core-20)
+ [基本物件](#writing-functions-javascript-features-primitive-objects-20)
+ [內建物件](#writing-functions-javascript-features-builtin-objects-20)
+ [錯誤類型](#writing-functions-javascript-features-error-types-20)
+ [全域變數](#writing-functions-javascript-features-globals-20)
+ [內建模組](#writing-functions-javascript-features-builtin-modules-20)
+ [限制功能](#writing-functions-javascript-features-restricted-features-20)

## 核心功能
<a name="writing-functions-javascript-features-core-20"></a>

支援 ES 的以下核心功能。

**類型**  
支援所有 ES 5.1 類型。這包括布林值、數字、字串、物件、陣列、函數和常規表達式。

**運算子**  
支援所有 ES 5.1 運算子。  
支援 ES 7 指數運算子 (`**`)。

**陳述式**  
支援下列 ES 5.1 陳述式：  
+ `break`
+ `catch`
+ `continue`
+ `do-while`
+ `else`
+ `finally`
+ `for`
+ `for-in`
+ `if`
+ `label`
+ `return`
+ `switch`
+ `throw`
+ `try`
+ `var`
+ `while`
支援以下 ES 6 陳述式：  
+ `const`
+ `let`
支援下列 ES 8 陳述式：  
+ `async`
+ `await`
`async`、`await`、`const` 和 `let` 是 JavaScript 執行時期 2.0 支援的功能。  
`await` 只能在 `async` 函數內使用，不支援 `async` 引數和關閉。

**文字**  
支援 ES 6 範本文字：多行字串、表達式插補和巢狀範本。

**函數**  
支援所有 ES 5.1 函數功能。  
支援 ES 6 箭頭函數，支援 ES 6 剩餘參數語法。

**Unicode**  
來源文字和字串常值可以包含 Unicode 編碼的字元。也支援由六個字元 (例如 `\uXXXX`) 組成的 Unicode 字碼指標逸出序列。

**嚴格模式**  
函數按預設會在嚴格模式下運作，因此您不需要在函數程式碼中新增 `use strict` 陳述式。無法對此進行變更。

## 基本物件
<a name="writing-functions-javascript-features-primitive-objects-20"></a>

支援 ES 的以下基本物件。

**物件**  
支援物件上的以下 ES 5.1 方法：  
+ `Object.create()`( 不含屬性清單)
+ `Object.defineProperties()`
+ `Object.defineProperty()`
+ `Object.freeze()`
+ `Object.getOwnPropertyDescriptor()`
+ `Object.getOwnPropertyDescriptors()`
+ `Object.getOwnPropertyNames()`
+ `Object.getPrototypeOf()`
+ `Object.isExtensible()`
+ `Object.isFrozen()`
+ `Object.isSealed()`
+ `Object.keys()`
+ `Object.preventExtensions()`
+ `Object.seal()`
支援物件上的以下 ES 6 方法：  
+ `Object.assign()`
支援物件上的以下 ES 8 方法：  
+ `Object.entries()`
+ `Object.values()`
支援以下針對物件的 ES 5.1 原型方法：  
+ `Object.prototype.hasOwnProperty()`
+ `Object.prototype.isPrototypeOf()`
+ `Object.prototype.propertyIsEnumerable()`
+ `Object.prototype.toString()`
+ `Object.prototype.valueOf()`
支援以下針對物件的 ES 6 原型方法：  
+ `Object.prototype.is()`
+ `Object.prototype.setPrototypeOf()`

**String**  
支援以下針對字串的 ES 5.1 方法：  
+ `String.fromCharCode()`
支援以下針對字串的 ES 6 方法：  
+ `String.fromCodePoint()`
支援以下針對字串的 ES 5.1 原型方法：  
+ `String.prototype.charAt()`
+ `String.prototype.concat()`
+ `String.prototype.indexOf()`
+ `String.prototype.lastIndexOf()`
+ `String.prototype.match()`
+ `String.prototype.replace()`
+ `String.prototype.search()`
+ `String.prototype.slice()`
+ `String.prototype.split()`
+ `String.prototype.substr()`
+ `String.prototype.substring()`
+ `String.prototype.toLowerCase()`
+ `String.prototype.trim()`
+ `String.prototype.toUpperCase()`
支援以下針對字串的 ES 6 原型方法：  
+ `String.prototype.codePointAt()`
+ `String.prototype.endsWith()`
+ `String.prototype.includes()`
+ `String.prototype.repeat()`
+ `String.prototype.startsWith()`
支援以下針對字串的 ES 8 原型方法：  
+ `String.prototype.padStart()`
+ `String.prototype.padEnd()`
支援以下針對字串的 ES 9 原型方法：  
+ `String.prototype.trimStart()`
+ `String.prototype.trimEnd()`
支援以下針對字串的 ES 12 原型方法：  
+ `String.prototype.replaceAll()`
**注意**  
`String.prototype.replaceAll()` 是 JavaScript 執行時間 2.0 的新功能。

**Number**  
支援所有 ES 5 數字。  
支援以下 ES 6 數字屬性：  
+ `Number.EPSILON`
+ `Number.MAX_SAFE_INTEGER`
+ `Number.MIN_SAFE_INTEGER`
+ `Number.MAX_VALUE`
+ `Number.MIN_VALUE`
+ `Number.NaN`
+ `Number.NEGATIVE_INFINITY`
+ `Number.POSITIVE_INFINITY`
支援數字上的以下 ES 6 方法：  
+ `Number.isFinite()`
+ `Number.isInteger()`
+ `Number.isNaN()`
+ `Number.isSafeInteger()`
+ `Number.parseInt()`
+ `Number.parseFloat()`
支援以下針對數字的 ES 5.1 原型方法：  
+ `Number.prototype.toExponential()`
+ `Number.prototype.toFixed()`
+ `Number.prototype.toPrecision()`
支援 ES 12 數字分隔符號。  
ES 12 數字分隔符號是 JavaScript 執行時間 2.0 的新功能。

## 內建物件
<a name="writing-functions-javascript-features-builtin-objects-20"></a>

支援 ES 的以下內建物件。

**數學**  
支援所有 ES 5.1 數學方法。  
在 CloudFront Functions 執行時間環境中，`Math.random()` 實作會使用植入函數執行時間戳記的 OpenBSD `arc4random`。
支援以下 ES 6 數學屬性：  
+ `Math.E`
+ `Math.LN10`
+ `Math.LN2`
+ `Math.LOG10E`
+ `Math.LOG2E`
+ `Math.PI`
+ `Math.SQRT1_2`
+ `Math.SQRT2`
支援以下 ES 6 數學方法：  
+ `Math.abs()`
+ `Math.acos()`
+ `Math.acosh()`
+ `Math.asin()`
+ `Math.asinh()`
+ `Math.atan()`
+ `Math.atan2()`
+ `Math.atanh()`
+ `Math.cbrt()`
+ `Math.ceil()`
+ `Math.clz32()`
+ `Math.cos()`
+ `Math.cosh()`
+ `Math.exp()`
+ `Math.expm1()`
+ `Math.floor()`
+ `Math.fround()`
+ `Math.hypot()`
+ `Math.imul()`
+ `Math.log()`
+ `Math.log1p()`
+ `Math.log2()`
+ `Math.log10()`
+ `Math.max()`
+ `Math.min()`
+ `Math.pow()`
+ `Math.random()`
+ `Math.round()`
+ `Math.sign()`
+ `Math.sinh()`
+ `Math.sin()`
+ `Math.sqrt()`
+ `Math.tan()`
+ `Math.tanh()`
+ `Math.trunc()`

**日期**  
支援所有 ES 5.1 `Date` 功能。  
基於安全考量，在單一函數執行的生命週期內，`Date` 始終返回相同的值 (函數的開始時間)。如需詳細資訊，請參閱 [限制功能](functions-javascript-runtime-10.md#writing-functions-javascript-features-restricted-features)。

**函數**  
支援以下 ES 5.1 原型方法：  
+ `Function.prototype.apply()`
+ `Function.prototype.bind()`
+ `Function.prototype.call()`
不支援函數建構子。

**常規表達式**  
支援所有 ES 5.1 常規表達式功能。常規表達式語言與 Perl 相容。  
支援以下 ES 5.1 原型存取子屬性：  
+ `RegExp.prototype.global`
+ `RegExp.prototype.ignoreCase`
+ `RegExp.protoype.multiline`
+ `RegExp.protoype.source`
+ `RegExp.prototype.sticky`
+ `RegExp.prototype.flags`
**注意**  
`RegExp.prototype.sticky` 和 `RegExp.prototype.flags` 是 JavaScript 執行時間 2.0 的新功能。
支援以下 ES 5.1 原型方法：  
+ `RegExp.prototype.exec()`
+ `RegExp.prototype.test()`
+ `RegExp.prototype.toString()`
+ `RegExp.prototype[@@replace]()`
+ `RegExp.prototype[@@split]()`
**注意**  
`RegExp.prototype[@@split]()` 是 JavaScript 執行時間 2.0 的新功能。
支援以下 ES 5.1 執行個體屬性：  
+ `lastIndex`
支援 ES 9 命名的擷取群組。

**JSON**  
支援以下 ES 5.1 方法：  
+ `JSON.parse()`
+ `JSON.stringify()`

**陣列**  
支援陣列上的以下 ES 5.1 方法：  
+ `Array.isArray()`
支援陣列上的以下 ES 6 方法：  
+ `Array.of()`
支援以下 ES 5.1 原型方法：  
+ `Array.prototype.concat()`
+ `Array.prototype.every()`
+ `Array.prototype.filter()`
+ `Array.prototype.forEach()`
+ `Array.prototype.indexOf()`
+ `Array.prototype.join()`
+ `Array.prototype.lastIndexOf()`
+ `Array.prototype.map()`
+ `Array.prototype.pop()`
+ `Array.prototype.push()`
+ `Array.prototype.reduce()`
+ `Array.prototype.reduceRight()`
+ `Array.prototype.reverse()`
+ `Array.prototype.shift()`
+ `Array.prototype.slice()`
+ `Array.prototype.some()`
+ `Array.prototype.sort()`
+ `Array.prototype.splice()`
+ `Array.prototype.unshift()`
支援以下 ES 6 原型方法  
+ `Array.prototype.copyWithin()`
+ `Array.prototype.fill()`
+ `Array.prototype.find()`
+ `Array.prototype.findIndex()`
支援以下 ES 7 原型方法：  
+ `Array.prototype.includes()`

**類型陣列**  
支援以下 ES 6 類型陣列建構子：  
+ `Float32Array`
+ `Float64Array`
+ `Int8Array`
+ `Int16Array`
+ `Int32Array`
+ `Uint8Array`
+ `Uint8ClampedArray`
+ `Uint16Array`
+ `Uint32Array`
支援以下 ES 6 方法：  
+ `TypedArray.from()`
+ `TypedArray.of()`
**注意**  
`TypedArray.from()` 和 `TypedArray.of()` 是 JavaScript 執行時間 2.0 的新功能。
支援以下 ES 6 原型方法：  
+ `TypedArray.prototype.copyWithin()`
+ `TypedArray.prototype.every()`
+ `TypedArray.prototype.fill()`
+ `TypedArray.prototype.filter()`
+ `TypedArray.prototype.find()`
+ `TypedArray.prototype.findIndex()`
+ `TypedArray.prototype.forEach()`
+ `TypedArray.prototype.includes()`
+ `TypedArray.prototype.indexOf()`
+ `TypedArray.prototype.join()`
+ `TypedArray.prototype.lastIndexOf()`
+ `TypedArray.prototype.map()`
+ `TypedArray.prototype.reduce()`
+ `TypedArray.prototype.reduceRight()`
+ `TypedArray.prototype.reverse()`
+ `TypedArray.prototype.some()`
+ `TypedArray.prototype.set()`
+ `TypedArray.prototype.slice()`
+ `TypedArray.prototype.sort()`
+ `TypedArray.prototype.subarray()`
+ `TypedArray.prototype.toString()`
**注意**  
`TypedArray.prototype.every()`、`TypedArray.prototype.fill()`、`TypedArray.prototype.filter()`、`TypedArray.prototype.find()`、`TypedArray.prototype.findIndex()`、`TypedArray.prototype.forEach()`、`TypedArray.prototype.includes()`、`TypedArray.prototype.indexOf()`、`TypedArray.prototype.join()`、`TypedArray.prototype.lastIndexOf()`、`TypedArray.prototype.map()`、`TypedArray.prototype.reduce()`、`TypedArray.prototype.reduceRight()`、`TypedArray.prototype.reverse()` 和 `TypedArray.prototype.some()` 是 JavaScript 執行時間 2.0 的新功能。

**ArrayBuffer**  
支援以下針對 ArrayBuffer 的 ES 6 方法：  
+ `isView()`
支援以下針對 ArrayBuffer 的 ES 6 原型方法：  
+ `ArrayBuffer.prototype.slice()`

**Promise**  
支援以下針對 Promise 的 ES 6 方法：  
+ `Promise.all()`
+ `Promise.allSettled()`
+ `Promise.any()`
+ `Promise.reject()`
+ `Promise.resolve()`
+ `Promise.race()`
**注意**  
`Promise.all()`、`Promise.allSettled()`、`Promise.any()` 和 `Promise.race()` 是 JavaScript 執行時間 2.0 的新功能。
支援以下針對 Promise 的 ES 6 原型方法：  
+ `Promise.prototype.catch()`
+ `Promise.prototype.finally()`
+ `Promise.prototype.then()`

**DataView**  
支援以下 ES 6 原型方法：  
+ `DataView.prototype.getFloat32()`
+ `DataView.prototype.getFloat64()`
+ `DataView.prototype.getInt16()`
+ `DataView.prototype.getInt32()`
+ `DataView.prototype.getInt8()`
+ `DataView.prototype.getUint16()`
+ `DataView.prototype.getUint32()`
+ `DataView.prototype.getUint8()`
+ `DataView.prototype.setFloat32()`
+ `DataView.prototype.setFloat64()`
+ `DataView.prototype.setInt16()`
+ `DataView.prototype.setInt32()`
+ `DataView.prototype.setInt8()`
+ `DataView.prototype.setUint16()`
+ `DataView.prototype.setUint32()`
+ `DataView.prototype.setUint8()`
**注意**  
所有 DataView ES 6 原型方法都是 JavaScript 執行時間 2.0 的新功能。

**Symbol**  
支援以下 ES 6 方法：  
+ `Symbol.for()`
+ `Symbol.keyfor()`
**注意**  
所有符號 ES 6 方法都是 JavaScript 執行時間 2.0 的新功能。

**文字解碼器**  
支援以下原型方法：  
+ `TextDecoder.prototype.decode()`
支援以下原型存取子屬性：  
+ `TextDecoder.prototype.encoding`
+ `TextDecoder.prototype.fatal`
+ `TextDecoder.prototype.ignoreBOM`

**文字編碼器**  
支援以下原型方法：  
+ `TextEncoder.prototype.encode()`
+ `TextEncoder.prototype.encodeInto()`

**主控台**  
這是一個用於偵錯的協助程式物件。它僅支援 `log()` 方法以記錄日誌訊息。  
CloudFront Functions 不支援逗號語法，例如 `console.log('a', 'b')`。因此請改為使用 `console.log('a' + ' ' + 'b')` 格式。

## 錯誤類型
<a name="writing-functions-javascript-features-error-types-20"></a>

支援以下錯誤物件：
+ `Error`
+ `EvalError`
+ `InternalError`
+ `RangeError`
+ `ReferenceError`
+ `SyntaxError`
+ `TypeError`
+ `URIError`

## 全域變數
<a name="writing-functions-javascript-features-globals-20"></a>

支援 `globalThis` 物件。

支援以下 ES 5.1 全局函數：
+ `decodeURI()`
+ `decodeURIComponent()`
+ `encodeURI()`
+ `encodeURIComponent()`
+ `isFinite()`
+ `isNaN()`
+ `parseFloat()`
+ `parseInt()`

支援以下 ES 6 全域函數：
+ `atob()`
+ `btoa()`
**注意**  
`atob()` 和 `btoa()` 是 JavaScript 執行時間 2.0 的新功能。

支援以下全局常數：
+ `NaN`
+ `Infinity`
+ `undefined`
+ `arguments`

## 內建模組
<a name="writing-functions-javascript-features-builtin-modules-20"></a>

支援以下內建模組：

**Topics**
+ [緩衝區](#writing-functions-javascript-features-builtin-modules-buffer-20)
+ [查詢字串](#writing-functions-javascript-features-builtin-modules-query-string-20)
+ [加密](#writing-functions-javascript-features-builtin-modules-crypto-20)

### 緩衝區
<a name="writing-functions-javascript-features-builtin-modules-buffer-20"></a>

此模組提供以下方法：
+ `Buffer.alloc(size[, fill[, encoding]])`

  配置 `Buffer`。
  + `size`：緩衝區大小。輸入整數。
  + `fill`：選用。輸入字串、`Buffer`、Uint8Array 或整數。預設值為 `0`。
  + `encoding`：選用。當 `fill` 為字串，請輸入以下其中一項：`utf8`、`hex`、`base64`、`base64url`。預設值為 `utf8`。
+ `Buffer.allocUnsafe(size)`

  配置一個未初始化的 `Buffer`。
  + `size`：輸入整數。
+ `Buffer.byteLength(value[, encoding])`

  返回值的長度，以位元組為單位。
  + `value`: 字串、`Buffer`、TypedArray、DataView 或 ArrayBuffer。
  + `encoding`：選用。當 `value` 為字串，請輸入以下其中一項：`utf8`、`hex`、`base64`、`base64url`。預設值為 `utf8`。
+ `Buffer.compare(buffer1, buffer2)`

  比較兩個 `Buffer` 以協助對陣列進行排序。如果兩者相同則傳回 `0`，如果 `buffer1` 在前面則傳回 `-1`，或如果 `buffer2` 在前面則傳回 `1`。
  + `buffer1`：輸入 `Buffer`。
  + `buffer2`：輸入不同的 `Buffer`。
+ `Buffer.concat(list[, totalLength])`

  連接多個 `Buffer`。如果沒有則傳回 `0`。最多傳回 `totalLength`。
  + `list`：輸入 `Buffer` 的清單。請注意，這將被截斷為 `totalLength`。
  + `totalLength`：選用。輸入不帶正負號的整數。如果清單為空白，則使用清單中 `Buffer` 執行個體的總和。
+ `Buffer.from(array)`

  從陣列建立 `Buffer`。
  + `array`: 輸入從 `0` 到 `255` 的位元組陣列。
+ `Buffer.from(arrayBuffer, byteOffset[, length]))`

  從 `arrayBuffer` 建立檢視，從偏移值 `byteOffset` 開始，長度為 `length`。
  + `arrayBuffer`：輸入 `Buffer` 陣列。
  + `byteOffset`：輸入整數。
  + `length`：選用。輸入整數。
+ `Buffer.from(buffer)`

  建立 `Buffer` 的複本。
  + `buffer`：輸入 `Buffer`。
+ `Buffer.from(object[, offsetOrEncoding[, length]])`

  從物件建立 `Buffer`。如果 `valueOf()` 不等於物件，則傳回 `Buffer.from(object.valueOf(), offsetOrEncoding, length)`。
  + `object`：輸入物件。
  + `offsetOrEncoding`：選用。輸入整數或編碼字串。
  + `length`：選用。輸入整數。
+ `Buffer.from(string[, encoding])`

  從字串建立一個 `Buffer`。
  + `string`：輸入字串。
  + `encoding`：選用。輸入以下其中之一：`utf8`、`hex`、`base64`、`base64url`。預設值為 `utf8`。
+ `Buffer.isBuffer(object)`

  檢查 `object` 是否為緩衝區。傳回 `true` 或 `false`。
  + `object`：輸入物件。
+ `Buffer.isEncoding(encoding)`

  檢查是否支援 `encoding`。傳回 `true` 或 `false`。
  + `encoding`：選用。輸入以下其中之一：`utf8`、`hex`、`base64`、`base64url`。預設值為 `utf8`。

此模組提供以下緩衝區原型方法：
+ `Buffer.prototype.compare(target[, targetStart[, targetEnd[, sourceStart[, sourceEnd]]]])`

  將 `Buffer` 與目標比較。如果兩者相同則傳回 `0`，如果 `buffer` 在前面則傳回 `1`，或如果 `target` 在前面則傳回 `-1`。
  + `target`：輸入 `Buffer`。
  + `targetStart`：選用。輸入整數。預設值為 0。
  + `targetEnd`：選用。輸入整數。預設值為 `target` 長度。
  + `sourceStart`：選用。輸入整數。預設值為 0。
  + `sourceEnd`：選用。輸入整數。預設值為 `Buffer` 長度。
+ `Buffer.prototype.copy(target[, targetStart[, sourceStart[, sourceEnd]]])`

  將緩衝區複製到 `target`。
  + `target`：輸入 `Buffer` 或 `Uint8Array`。
  + `targetStart`：選用。輸入整數。預設值為 0。
  + `sourceStart`：選用。輸入整數。預設值為 0。
  + `sourceEnd`：選用。輸入整數。預設值為 `Buffer` 長度。
+ `Buffer.prototype.equals(otherBuffer)`

  將 `Buffer` 與 `otherBuffer` 比較。傳回 `true` 或 `false`。
  + `otherBuffer`：輸入字串。
+ `Buffer.prototype.fill(value[, offset[, end][, encoding])`

  以 `value` 填入 `Buffer`。
  + `value`：輸入字串、`Buffer` 或整數。
  + `offset`：選用。輸入整數。
  + `end`：選用。輸入整數。
  + `encoding`：選用。輸入以下其中之一：`utf8`、`hex`、`base64`、`base64url`。預設值為 `utf8`。
+ `Buffer.prototype.includes(value[, byteOffset][, encoding])`

  搜尋 `Buffer` 中的 `value`。傳回 `true` 或 `false`。
  + `value`：輸入字串、`Buffer`、`Uint8Array`、或整數。
  + `byteOffset`：選用。輸入整數。
  + `encoding`：選用。輸入以下其中之一：`utf8`、`hex`、`base64`、`base64url`。預設值為 `utf8`。
+ `Buffer.prototype.indexOf(value[, byteOffset][, encoding])`

  首先搜尋 `Buffer` 中的第一個 `value`。如果找到了，則傳回 `index`；如果找不到，則傳回 `-1`。
  + `value`: 輸入字串、`Buffer`、Unit8Array 或 0 到 255 之間的整數。
  + `byteOffset`：選用。輸入整數。
  + `encoding`：選用。如果 `value` 是字串，請輸入以下其中一項：`utf8`、`hex`、`base64`、`base64url`。預設值為 `utf8`。
+ `Buffer.prototype.lastIndexOf(value[, byteOffset][, encoding])`

  搜尋 `Buffer` 中的最後一個 `value`。如果找到了，則傳回 `index`；如果找不到，則傳回 `-1`。
  + `value`: 輸入字串、`Buffer`、Unit8Array 或 0 到 255 之間的整數。
  + `byteOffset`：選用。輸入整數。
  + `encoding`：選用。如果 `value` 是字串，請輸入以下其中一項：`utf8`、`hex`、`base64`、`base64url`。預設值為 `utf8`。
+ `Buffer.prototype.readInt8(offset)`

  從 `Buffer` 的偏移值 `offset` 讀取 `Int8`。
  + `offset`：輸入整數。
+ `Buffer.prototype.readIntBE(offset, byteLength)`

  從 `Buffer` 的偏移值 `offset` 讀取大端序 `Int`。
  + `offset`：輸入整數。
  + `byteLength`：選用。輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.readInt16BE(offset)`

  從 `Buffer` 的偏移值 `offset` 讀取大端序 `Int16`。
  + `offset`：輸入整數。
+ `Buffer.prototype.readInt32BE(offset)`

  從 `Buffer` 的偏移值 `offset` 讀取大端序 `Int32`。
  + `offset`：輸入整數。
+ `Buffer.prototype.readIntLE(offset, byteLength)`

  從 `Buffer` 的偏移值 `offset` 讀取小端序 `Int`。
  + `offset`：輸入整數。
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.readInt16LE(offset)`

  從 `Buffer` 的偏移值 `offset` 讀取小端序 `Int16`。
  + `offset`：輸入整數。
+ `Buffer.prototype.readInt32LE(offset)`

  從 `Buffer` 的偏移值 `offset` 讀取小端序 `Int32`。
  + `offset`：輸入整數。
+ `Buffer.prototype.readUInt8(offset)`

  從 `Buffer` 的偏移值 `offset` 讀取 `UInt8`。
  + `offset`：輸入整數。
+ `Buffer.prototype.readUIntBE(offset, byteLength)`

  從 `Buffer` 的偏移值 `offset` 讀取大端序 `UInt`。
  + `offset`：輸入整數。
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.readUInt16BE(offset)`

  從 `Buffer` 的偏移值 `offset` 讀取大端序 `UInt16`。
+ 
  + `offset`：輸入整數。
+ `Buffer.prototype.readUInt32BE(offset)`

  從 `Buffer` 的偏移值 `offset` 讀取大端序 `UInt32`。
  + `offset`：輸入整數。
+ `Buffer.prototype.readUIntLE(offset, byteLength)`

  從 `Buffer` 的偏移值 `offset` 讀取小端序 `UInt`。
  + `offset`：輸入整數。
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.readUInt16LE(offset)`

  從 `Buffer` 的偏移值 `offset` 讀取小端序 `UInt16`。
  + `offset`：輸入整數。
+ `Buffer.prototype.readUInt32LE(offset)`

  從 `Buffer` 的偏移值 `offset` 讀取小端序 `UInt32`。
  + `offset`：輸入整數。
+ `Buffer.prototype.readDoubleBE([offset])`

  從 `Buffer` 的偏移值 `offset` 讀取 64 位元大端序雙精度浮點數。
  + `offset`：選用。輸入整數。
+ `Buffer.prototype.readDoubleLE([offset])`

  從 `Buffer` 的偏移值 `offset` 讀取 64 位元小端序雙精度浮點數。
  + `offset`：選用。輸入整數。
+ `Buffer.prototype.readFloatBE([offset])`

  從 `Buffer` 的偏移值 `offset` 讀取 32 位元大端序浮點數。
  + `offset`：選用。輸入整數。
+ `Buffer.prototype.readFloatLE([offset])`

  從 `Buffer` 的偏移值 `offset` 讀取 32 位元小端序浮點數。
  + `offset`：選用。輸入整數。
+ `Buffer.prototype.subarray([start[, end]])`

  傳回 `Buffer` 的副本，並使用新的 `start` 和 `end` 偏移和裁剪。
  + `start`：選用。輸入整數。預設值為 0。
  + `end`：選用。輸入整數。預設值為緩衝區長度。
+ `Buffer.prototype.swap16()`

  交換 `Buffer` 陣列的位元組順序，將其視為 16 位元數字的陣列。`Buffer` 的長度必須是 2 的倍數，否則您將收到錯誤訊息。
+ `Buffer.prototype.swap32()`

  交換 `Buffer` 陣列的位元組順序，將其視為 32 位元數字的陣列。`Buffer` 的長度必須是 4 的倍數，否則您將收到錯誤訊息。
+ `Buffer.prototype.swap64()`

  交換 `Buffer` 陣列的位元組順序，將其視為 64 位元數字的陣列。`Buffer` 的長度必須是 8 的倍數，否則您將收到錯誤訊息。
+ `Buffer.prototype.toJSON()`

  以 JSON 格式傳回 `Buffer`。
+ `Buffer.prototype.toString([encoding[, start[, end]]])`

  將 `Buffer` 從 `start` 到 `end` 轉換為編碼字串。
  + `encoding`：選用。輸入以下其中之一：`utf8`、`hex`、`base64` 或 `base64url`。預設值為 `utf8`。
  + `start`：選用。輸入整數。預設值為 0。
  + `end`：選用。輸入整數。預設值為緩衝區長度。
+ `Buffer.prototype.write(string[, offset[, length]][, encoding])`

  如果空間足夠，則將編碼 `string` 寫入 `Buffer`，如果空間不足，則寫入被截斷的 `string`。
  + `string`：輸入字串。
  + `offset`：選用。輸入整數。預設值為 0。
  + `length`：選用。輸入整數。預設值是字串的長度。
  + `encoding`：選用。選擇性地輸入以下其中一項：`utf8`、`hex`、`base64` 或 `base64url`。預設值為 `utf8`。
+ `Buffer.prototype.writeInt8(value, offset, byteLength)`

  將 `Int8` `value` (長度為 `byteLength`) 寫入 `Buffer` 的偏移值 `offset`。
  + `value`：輸入整數。
  + `offset`：輸入整數
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.writeIntBE(value, offset, byteLength)`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用大端序。
  + `value`：輸入整數。
  + `offset`：輸入整數
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.writeInt16BE(value, offset, byteLength)`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用大端序。
  + `value`：輸入整數。
  + `offset`：輸入整數
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.writeInt32BE(value, offset, byteLength)`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用大端序。
  + `value`：輸入整數。
  + `offset`：輸入整數
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.writeIntLE(offset, byteLength)`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用小端序。
  + `offset`：輸入整數。
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.writeInt16LE(offset, byteLength)`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用小端序。
  + `offset`：輸入整數。
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.writeInt32LE(offset, byteLength)`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用小端序。
  + `offset`：輸入整數。
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.writeUInt8(value, offset, byteLength)`

  將 `UInt8` `value` (長度為 `byteLength`) 寫入 `Buffer` 的偏移值 `offset`。
  + `value`：輸入整數。
  + `offset`：輸入整數
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.writeUIntBE(value, offset, byteLength)`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用大端序。
  + `value`：輸入整數。
  + `offset`：輸入整數
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.writeUInt16BE(value, offset, byteLength)`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用大端序。
  + `value`：輸入整數。
  + `offset`：輸入整數
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.writeUInt32BE(value, offset, byteLength)`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用大端序。
  + `value`：輸入整數。
  + `offset`：輸入整數
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.writeUIntLE(value, offset, byteLength)`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用小端序。
  + `value`：輸入整數。
  + `offset`：輸入整數
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.writeUInt16LE(value, offset, byteLength)`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用小端序。
  + `value`：輸入整數。
  + `offset`：輸入整數
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.writeUInt32LE(value, offset, byteLength)`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用小端序。
  + `value`：輸入整數。
  + `offset`：輸入整數
  + `byteLength`：輸入從 `1` 到 `6` 的整數。
+ `Buffer.prototype.writeDoubleBE(value, [offset])`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用大端序。
  + `value`：輸入整數。
  + `offset`：選用。輸入整數。預設值為 0。
+ `Buffer.prototype.writeDoubleLE(value, [offset])`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用小端序。
  + `value`：輸入整數。
  + `offset`：選用。輸入整數。預設值為 0。
+ `Buffer.prototype.writeFloatBE(value, [offset])`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用大端序。
  + `value`：輸入整數。
  + `offset`：選用。輸入整數。預設值為 0。
+ `Buffer.prototype.writeFloatLE(value, [offset])`

  將 `value` 寫入 `Buffer` 的偏移值 `offset`，使用小端序。
  + `value`：輸入整數。
  + `offset`：選用。輸入整數。預設值為 0。

支援以下執行個體方法：
+ `buffer[index]`

  取得和設定 `Buffer` 中偏移值 `index` 的八位元組 (位元組)。
  + 取得從 `0` 到 `255` 的數字。或設定一個從 `0` 到 `255` 的數字。

支援以下執行個體屬性：
+ `buffer`

  取得緩衝區的 `ArrayBuffer` 物件。
+ `byteOffset`

  取得緩衝區 `Arraybuffer` 物件的 `byteOffset`。
+ `length`

  取得緩衝區位元組計數。

**注意**  
所有緩衝區模組方法都是 JavaScript 執行時間 2.0 的新功能。

### 查詢字串
<a name="writing-functions-javascript-features-builtin-modules-query-string-20"></a>

**注意**  
[CloudFront Functions 事件物件](functions-event-structure.md)會自動剖析 URL 查詢字串。這意味著在大多數情況下，您不需要使用此模組。

查詢字串模組 (`querystring`) 提供剖析和格式化 URL 查詢字串的方法。您可以使用 `require('querystring')` 加載模組。此模組提供下列方法。

`querystring.escape(string)`  
URL 編碼給定的 `string`，傳回逸出的查詢字串。該方法由 `querystring.stringify()` 使用，並且不應直接使用。

`querystring.parse(string[, separator[, equal[, options]]])`  
剖析查詢字串 (`string`) 並傳回物件。  
`separator` 參數是用來分隔查詢字串中的鍵/值對的子字串。其在預設情況下為 `&`。  
`equal` 參數是用來分隔查詢字串中的鍵和值的子字串。其在預設情況下為 `=`。  
`options` 參數是具有下列鍵的物件：    
`decodeURIComponent function`  
解碼查詢字串中百分比編碼字元的函數。其在預設情況下為 `querystring.unescape()`。  
`maxKeys number`  
要剖析的鍵的最大數量。其在預設情況下為 `1000`。使用 `0` 的值移除計數鍵的限制。
根據預設，查詢字串中的百分比編碼字元會假設為使用 UTF-8 編碼。無效的 UTF-8 序列會被取代為 `U+FFFD` 取代字元。  
例如，對於下列查詢字串：  

```
'name=value&abc=xyz&abc=123'
```
`querystring.parse()` 的返回值是：  

```
{
name: 'value',
abc: ['xyz', '123']
}
```
`querystring.decode()` 是 的別名。`querystring.parse()`

`querystring.stringify(object[, separator[, equal[, options]]])`  
序列化 `object` 並傳回查詢字串。  
`separator` 參數是用來分隔查詢字串中的鍵/值對的子字串。其在預設情況下為 `&`。  
`equal` 參數是用來分隔查詢字串中的鍵和值的子字串。其在預設情況下為 `=`。  
`options` 參數是具有下列鍵的物件：    
`encodeURIComponent function`  
用於將 URL 不安全字元轉換為查詢字串中的百分比編碼的函數。其在預設情況下為 `querystring.escape()`。
根據預設，在查詢字串中需要百分比編碼的字元會編碼為 UTF-8。若要使用不同的編碼，請指定 `encodeURIComponent` 選項。  
例如，對於以下程式碼：  

```
querystring.stringify({ name: 'value', abc: ['xyz', '123'], anotherName: '' });
```
返回值是：  

```
'name=value&abc=xyz&abc=123&anotherName='
```
`querystring.encode()` 是 `querystring.stringify()` 的別名。

`querystring.unescape(string)`  
解碼給定 `string` 中的 URL 百分比編碼字元，傳回未逸出的查詢字串。此方法由 `querystring.parse()` 使用，並且不應直接使用。

### 加密
<a name="writing-functions-javascript-features-builtin-modules-crypto-20"></a>

密碼編譯模組 (`crypto`) 提供標準雜湊和雜湊型訊息身分驗證碼 (HMAC) 協助程式。您可以使用 `require('crypto')` 加載模組。

**雜湊方法**

`crypto.createHash(algorithm)`  
建立並傳回雜湊物件，藉助此物件，您可以使用給定的演算法產生雜湊摘要：`md5`、`sha1` 或 `sha256`。

`hash.update(data)`  
使用給定的 `data` 更新雜湊內容。

`hash.digest([encoding])`  
計算使用 `hash.update()` 傳遞的所有資料的摘要。編碼可以是 `hex`、`base64` 或 `base64url`。

**HMAC 方法**

`crypto.createHmac(algorithm, secret key)`  
建立並返回使用給定 `algorithm` 和 `secret key` 的 HMAC 物件。演算法可以是 `md5`、`sha1` 或 `sha256`。

`hmac.update(data)`  
使用給定的 `data` 更新 HMAC 內容。

`hmac.digest([encoding])`  
計算使用 `hmac.update()` 傳遞的所有資料的摘要。編碼可以是 `hex`、`base64` 或 `base64url`。

## 限制功能
<a name="writing-functions-javascript-features-restricted-features-20"></a>

由於安全考量，下列 JavaScript 語言功能不受支援或受到限制。

**動態程式碼評估**  
不支援動態程式碼評估。如果嘗試此評估，`eval()` 和 `Function` 建構子都會丟出錯誤。例如，`const sum = new Function('a', 'b', 'return a + b')` 丟出錯誤。

**計時器**  
不支援 `setTimeout()`、`setImmediate()` 和 `clearTimeout()` 函數。在函數執行時期間未推遲或產生任何佈建。您的函數必須同步執行方可完成。

**日期和時間戳記**  
基於安全考量，無法存取高解析度計時器。查詢當前時間的所有 `Date` 方法始終在單個函數執行的生命週期內返回相同的值。返回的時間戳記是函數開始執行的時間。因此，您無法測量函數中的經過時間。

**檔案系統存取**  
沒有檔案系統存取權。例如，沒有類似 Node.js 中的檔案系統存取 `fs` 模組。

**處理存取**  
沒有處理存取。例如沒有 `process` 全域物件可像 Node.js 一樣處理資訊存取。

**環境變數**  
無法存取環境變數。您可以改為使用 CloudFront KeyValueStore 為您的 CloudFront Functions 建立鍵值對的集中式資料儲存。CloudFront KeyValueStore 可動態更新組態資料，不需要部署程式碼變更。如需詳細資訊，請參閱[Amazon CloudFront KeyValueStore](kvs-with-functions.md)。

**網路存取**  
不支援網路呼叫。例如，不支援 XHR、HTTP(S) 和通訊端。

# 鍵值存放區的協助程式方法
<a name="functions-custom-methods"></a>

**注意**  
CloudFront Functions 的鍵值存放區 helper 方法呼叫不會觸發 AWS CloudTrail 資料事件。這些事件不會記錄在 CloudTrail 事件歷史記錄中。如需詳細資訊，請參閱[使用 記錄 Amazon CloudFront API 呼叫 AWS CloudTrail](logging_using_cloudtrail.md)。

如果您使用 [CloudFront 鍵值存放區](kvs-with-functions.md)在您建立的函數中包含鍵值，則本節內容適用。CloudFront Functions 具有的模組可提供三種協助程式方法來讀取鍵值存放區中的值。

若要在函數程式碼中使用此模組，請確定您已[將索引鍵值存放區與函數建立關聯](kvs-with-functions-associate.md)。

接著，在函數程式碼的第一行中包含下列陳述式：

```
import cf from 'cloudfront';
const kvsHandle = cf.kvs();
```



## `get()` 方法
<a name="functions-custom-methods-get"></a>

使用此方法傳回您指定的鍵名稱的鍵值。

**請求**

```
get("key", options);
```
+ `key`：需要擷取其值的鍵名稱
+ `options`：有一個選項，`format`。它可以確保函數正確解析資料。可能的值如下：
  + `string`：(預設值) 以 UTF8 編碼
  + `json` 
  + `bytes`：原始二進位資料緩衝區

**請求範例**

```
const value = await kvsHandle.get("myFunctionKey", { format: "string"});
```

**回應**

回應是 `promise`，會以使用 `options` 請求的格式解析為值。根據預設，值會以字串傳回。

### 錯誤處理
<a name="error-handling-exists-method"></a>

當您請求的鍵不存在於關聯的鍵值存放區時，`get()` 方法會傳回錯誤。若要管理此使用案例，您可以將 `try` 和 `catch` 區塊新增至程式碼。

**警告**  
使用 promise 組合器 (例如 `Promise.all`、`Promise.any`) 和 promise 鏈接方法 (例如 `then` 和 `catch`) 可能需要高函數記憶體用量。如果您的函數超過[函數記憶體配額上限](cloudfront-limits.md#limits-functions)，則無法執行。為了避免此錯誤，建議您依序或在迴圈中使用 `await` 語法來請求多重值。  
**範例**  

```
var value1 = await kvs.get('key1');
var value2 = await kvs.get('key2');
```
目前，使用 promise 組合器取得多重值並不會改善效能，範例如下。  

```
var values = await Promise.all([kvs.get('key1'), kvs.get('key2'),]);
```

## `exists()` 方法
<a name="functions-custom-methods-exists"></a>

使用此方法來識別鍵是否存在於鍵值存放區中。

**請求**

```
exists("key");
```

**請求範例**

```
const exist = await kvsHandle.exists("myFunctionkey");
```

**回應**

回應是傳回布林值 (`true` 或 `false`) 的 `promise`。此值指定鍵是否存在於鍵值存放區中。

## `meta()` 方法
<a name="functions-custom-methods-meta"></a>

此方法會傳回有關鍵值存放區的中繼資料。

**請求**

```
meta();
```

**請求範例**

```
const meta = await kvsHandle.meta();
```

**回應**

回應是一個 `promise`，可解析為具有以下屬性的物件：
+ `creationDateTime`：鍵值存放區建立的日期和時間，以 ISO 8601 格式表示。
+ `lastUpdatedDateTime`：上次從來源同步的鍵值存放區的日期和時間，以 ISO 8601 格式表示。該值不包括到邊緣的傳播時間。
+ `keyCount`：上次從來源同步後 KVS 中的總鍵數。

**回應範例**

```
{keyCount:3,creationDateTime:2023-11-30T23:07:55.765Z,lastUpdatedDateTime:2023-12-15T03:57:52.411Z}
```

# 原始伺服器修改的 Helper 方法
<a name="helper-functions-origin-modification"></a>

如果您動態更新或變更 CloudFront Functions 程式碼內請求所使用的原始伺服器，則本節適用。您只能在*檢視器請求* CloudFront Functions 時更新原始伺服器。CloudFront Functions 有一個模組，提供 helper 方法來動態更新或變更原始伺服器。

若要使用此模組，請使用 JavaScript 執行時期 2.0 建立 CloudFront 函數，並在函數程式碼的第一行中包含下列陳述式：

```
import cf from 'cloudfront';
```

如需詳細資訊，請參閱[適用於 CloudFront Functions 的 JavaScript 執行時期 2.0 功能](functions-javascript-runtime-20.md)。

**注意**  
測試 API 和測試主控台頁面不會測試是否已發生原始伺服器修改。不過，測試可確保函數程式碼執行時不會發生錯誤。

## 在 CloudFront Functions 和 Lambda@Edge 之間進行選擇
<a name="origin-modification-considerations"></a>

您可以使用 CloudFront Functions 或 Lambda@Edge 來更新原始伺服器。

使用 CloudFront Functions 更新原始伺服器時，您可以使用*檢視器請求*事件觸發程序，這表示使用此函數時，每個請求都會執行此邏輯。使用 Lambda@Edge 時，原始伺服器更新功能位於*原始伺服器請求*事件觸發程序上，這表示此邏輯僅在快取遺漏時執行。

您的選擇主要取決於您的工作負載，以及分佈上 CloudFront Functions 和 Lambda@Edge 的任何現有用量。下列考量可協助您決定是否使用 CloudFront Functions 或 Lambda@Edge 來更新您的原始伺服器。

CloudFront Functions 在下列情況下最有用：
+ 當您的請求是動態的 (表示無法快取) 且一律會前往原始伺服器時。CloudFront Functions 可提供更好的效能並降低整體成本。
+ 當您已有現有的檢視器請求 CloudFront 函數會在每個請求上執行時，您可以將原始伺服器更新邏輯新增至現有的函數。

若要使用 CloudFront Functions 更新原始伺服器，請參閱下列主題中的 helper 方法。

Lambda@Edge 在下列情況下最有用：
+ 當您有高度可快取的內容時，Lambda@Edge 可能更具成本效益，因為它只會在快取遺漏時執行，而 CloudFront Functions 會在每次請求時執行。
+ 當您已有現有的原始伺服器請求 Lambda@Edge 函數時，您可以將原始伺服器更新邏輯新增至現有的函數。
+ 當您的原始伺服器更新邏輯需要從第三方資料來源擷取資料時，例如 Amazon DynamoDB 或 Amazon S3。

如需 Lambda@Edge 的詳細資訊，請參閱「[使用 Lambda@Edge 在邊緣自訂](lambda-at-the-edge.md)」。

## updateRequestOrigin() 方法
<a name="update-request-origin-helper-function"></a>

使用 `updateRequestOrigin()` 方法更新請求的原始伺服器設定。您可以使用此方法更新已在分佈中定義的原始伺服器現有的原始伺服器屬性，或定義請求的新原始伺服器。若要這樣做，請指定您要變更的屬性。

**重要**  
您在 `updateRequestOrigin()` 中未指定的任何設定都會繼承現有原始伺服器的組態中的*相同設定*。

`updateRequestOrigin()` 方法設定的原始伺服器可以是任何 HTTP 端點，而且不需要是 CloudFront 分佈中的現有原始伺服器。

**備註**  
如果您要更新屬於原始伺服器群組的原始伺服器，則只會更新原始伺服器群組的*主要原始伺服器*。次要原始伺服器保持不變。來自修改原始伺服器且符合容錯移轉準則的任何回應代碼都會觸發容錯移轉至次要原始伺服器。
如果您要變更原始伺服器類型並啟用 OAC，請確定 `originAccessControlConfig` 中的原始伺服器類型符合新的原始伺服器類型。
您無法使用 `updateRequestOrigin()` 方法更新 [VPC 原始伺服器](private-content-vpc-origins.md)。請求將會失敗。

**請求**

```
updateRequestOrigin({origin properties})
```

`origin properties` 可以包含以下：

**domainName (選用)**  
原始伺服器的網域名稱。如果未提供，則會改用來自指派原始伺服器的網域名稱。    
**對於自訂原始伺服器**  
指定 DNS 網域名稱，例如 `www.example.com`。網域名稱不能包含冒號 (`:`)，也不能是 IP 地址。網域名稱長度上限為 253 個字元。  
**對於 S3 原始伺服器**  
指定 Amazon S3 儲存貯體的 DNS 網域名稱，例如 `amzn-s3-demo-bucket.s3.eu-west-1.amazonaws.com`。名稱可以高達 128 個字元，而且必須全部小寫。

**hostHeader （選用，適用於non-S3 自訂原始伺服器）**  
向原始伺服器提出請求時要使用的主機標頭。如果未提供，則會使用 domainName 參數的值。如果未提供主機標頭或網域名稱參數，則會使用指派原始伺服器的網域名稱，如果轉送至原始伺服器 (FTO) 政策包含主機，則會使用來自傳入請求的主機標頭。主機標頭不能包含冒號 (`:`)，也不能是 IP 地址。主機標頭最多可達 253 個字元。

**originPath (選用)**  
目錄路徑位於需定位內容請求的原始伺服器中。路徑的開頭應為正斜線 (/)，但結尾不應為正斜線。例如，不應以 `example-path/` 結尾。如果未提供，則會使用指派原始伺服器的原始路徑。    
**對於自訂原始伺服器**  
此路徑應以 URL 編碼且最多 255 個字元。

**customHeaders (選用)**  
您可以透過指定的標頭名稱與每個自訂標頭的值對，於請求中包含自訂標頭。格式與事件結構中請求和回應標頭的格式不同。使用下列索引鍵/值對語法：  

```
{"key1": "value1", "key2": "value2", ...}
```
您無法新增已列入不允許的標頭，且具有相同名稱的標頭也無法出現在傳入的請求 `headers` 中。函數程式碼中的標頭名稱必須為小寫。當 CloudFront Functions 將事件物件轉換回 HTTP 請求時，標頭名稱中每個單字的第一個字母會大寫，且單字之間用連字符分隔。  
例如，如果函數程式碼新增名為 `example-header-name` 的標頭，CloudFront 會將其轉換為 HTTP 請求中的 `Example-Header-Name`。如需詳細資訊，請參閱[CloudFront 無法新增到原始伺服器請求的自訂標頭](add-origin-custom-headers.md#add-origin-custom-headers-denylist)及[對邊緣函數的限制](edge-functions-restrictions.md)。  
如果未提供，則會使用來自指派原始伺服器的任何自訂標頭。

**connectionAttempts (選用)**  
CloudFront 會嘗試連線至原始伺服器的次數。最小值為 1，最大值為 3。如果未提供，則會使用來自指派原始伺服器的連線嘗試。

**originShield (選用)**  
這會啟用或更新 CloudFront Origin Shield。使用 Origin Shield 有利於降低原始伺服器的負載。如需詳細資訊，請參閱[使用 Amazon CloudFront Origin Shield](origin-shield.md)。如果未提供，則會使用來自指派原始伺服器的 Origin Shield 設定。    
**enabled (必要)**  
啟用或停用 Origin Shield 的布林值表達式。接受值：`true` 或 `false`。  
**region (啟用時需要)**  
 AWS 區域 適用於 Origin Shield 的 。指定對原始伺服器延遲最低的 AWS 區域 。使用區域代碼，而非區域名稱。例如，使用 `us-east-2` 指定美國東部 (俄亥俄) 區域。  
啟用 CloudFront Origin Shield 時，您必須為其指定 AWS 區域 。如需可用 AWS 區域 的清單並協助您選擇最適合原始伺服器的區域，請參閱 [選擇 Origin Shield AWS 的區域](origin-shield.md#choose-origin-shield-region)。

**originAccessControlConfig (選用)**  
此原始伺服器的原始存取控制 (OAC) 唯一識別碼。這只會在原始伺服器支援 CloudFront OAC 時使用，例如 Amazon S3、Lambda 函數 URL、MediaStore 和 MediaPackage V2。如果未提供，則會使用來自指派原始伺服器的 OAC 設定。  
這不支援舊版原始存取身分 (OAI)。如需詳細資訊，請參閱[限制對 AWS 原始伺服器的存取](private-content-restricting-access-to-origin.md)。    
**enabled (必要)**  
啟用或停用 OAC 的布林值表達式。接受值：`true` 或 `false`。  
**signingBehavior (啟用時需要)**  
指定 CloudFront 會簽署哪些請求 (將身分驗證資訊新增至其中)。針對最常見的使用案例指定 `always`。如需詳細資訊，請參閱[原始存取控制的進階設定](private-content-restricting-access-to-s3.md#oac-advanced-settings-s3)。  
此欄位可以有下列其中一個值：  
+ `always` – CloudFront 會簽署所有原始伺服器請求，並覆寫來自檢視器請求的 `Authorization` 標頭 (如果存在)。
+ `never` – CloudFront 不簽署任何原始伺服器請求。此值會關閉原始伺服器的原始存取控制。
+ `no-override` – 如果檢視器請求不包含 `Authorization` 標頭，則 CloudFront 會簽署原始伺服器請求。如果檢視器請求包含 `Authorization` 標頭，則 CloudFront 不會簽署原始伺服器請求，而是傳遞來自檢視器請求的 `Authorization` 標頭。
**警告**  
如要從檢視器請求傳遞 `Authorization` 標題，您必須將標題新增至原始伺服器請求政策中，適用於使用與此原始存取控制相關聯之原始伺服器的所有快取行為。如需詳細資訊，請參閱[使用政策控制原始伺服器請求](controlling-origin-requests.md)。  
**signingProtocol (啟用時需要)**  
OAC 的簽署通訊協定，將決定 CloudFront 簽署 (驗證) 請求的方式。唯一有效的值為 `sigv4`。  
**originType (啟用時需要)**  
此 OAC 的原始伺服器類型。有效值包括 `s3`、`mediapackagev2`、`mediastore` 與 `lambda`。

**timeout (選用)**  
您可以指定 CloudFront 應嘗試等待原始伺服器回應或傳送資料的逾時時間。如果未提供，則會使用來自指派原始伺服器的逾時設定。  
除非另行指定，否則這些逾時支援自訂原始伺服器和 Amazon S3 原始伺服器。  
**readTimeout (選用)**  
`readTimeout` 適用於下列兩個值：  
+ 在將請求轉送到自訂原始伺服器之後，CloudFront 等待回應的時間 (以秒為單位)。
+ CloudFront 在收到來自原始伺服器的回應封包後，並在接收下一個封包前，等待的時間長短 (以秒為單位) 
最短的逾時時間是 1 秒，最長是 120 秒。如需詳細資訊，請參閱[回應逾時](DownloadDistValuesOrigin.md#DownloadDistValuesOriginResponseTimeout)。  
**responseCompletionTimeout (選用)**  
從 CloudFront 到原始伺服器的請求可以保持開啟並等待回應的時間 (以秒為單位)。如果此時未從原始伺服器收到完整回應，CloudFront 會結束連線。  
`responseCompletionTimeout` 值必須大於或等於 `readTimeout` 的值。如需詳細資訊，請參閱[回應完成逾時](DownloadDistValuesOrigin.md#response-completion-timeout)。  
**keepAliveTimeout (選用)**  
此逾時僅適用於自訂原始伺服器，不適用於 Amazon S3 原始伺服器。(S3 原始伺服器組態會忽略這些設定。)   
`keepAliveTimeout` 指定 CloudFront 在收到上次封包的回應後維持與原始伺服器的連線所需的時間長度。最短的逾時時間是 1 秒，最長是 120 秒。如需詳細資訊，請參閱[保持連線逾時 (僅限自訂與 VPC 原始伺服器)](DownloadDistValuesOrigin.md#DownloadDistValuesOriginKeepaliveTimeout)。  
**connectionTimeout (選用)**  
嘗試建立與原始伺服器的連線時，CloudFront 所等待的秒數。最短的逾時時間是 1 秒，最長是 10 秒。如需詳細資訊，請參閱[連線逾時。](DownloadDistValuesOrigin.md#origin-connection-timeout)。

**customOriginConfig (選用)**  
使用 `customOriginConfig` 指定*非* Amazon S3 儲存貯體之原始伺服器的連線設定。有一個例外：如果 S3 儲存貯體設定為靜態網站託管，您可以指定這些設定。(其他類型的 S3 儲存貯體組態會忽略這些設定。) 如果未提供 `customOriginConfig`，則會使用來自指派原始伺服器的設定。    
**port (必要)。**  
CloudFront 用來連線至來源的 HTTP 連接埠。指定來源接聽使用的 HTTP 連接埠。  
**protocol (必要)**  
指定 CloudFront 用來連線至來源的通訊協定 (HTTP 或 HTTPS)。有效值如下：  
+ `http` — CloudFront 一律使用 HTTP 來連線至原始伺服器
+ `https` — CloudFront 一律使用 HTTPS 來連線至原始伺服器  
**sslProtocols (必要)**  
指定 CloudFront 透過 HTTPS 連線至原始伺服器時所使用的最低 SSL/TLS 通訊協定的清單。有效值包括 `SSLv3`、`TLSv1`、`TLSv1.1` 與 `TLSv1.2`。如需詳細資訊，請參閱[最低來源 SSL 通訊協定](DownloadDistValuesOrigin.md#DownloadDistValuesOriginSSLProtocols)。  
**ipAddressType (選用)**  
指定 CloudFront 用來連線至原始伺服器的 IP 位址類型。有效值包括 `ipv4`、`ipv6` 與 `dualstack`。只有在同時變更 `domainName` 屬性時，才支援變更 `ipAddressType`。

**sni （選用，適用於non-S3 自訂原始伺服器）**  
伺服器名稱指示 (SNI) 是 Transport Layer Security (TLS) 通訊協定的延伸，其中用戶端會指出其嘗試在 TLS 交握程序開始時連線到哪個主機名稱。此值應與原始伺服器上 TLS 憑證上的通用名稱相符。否則，您的原始伺服器可能會擲回錯誤。  
如果未提供，則會使用 `hostHeader` 參數中的 值。如果未提供主機標頭，則會使用 `domainName` 參數的值。  
如果未提供主機標頭或網域名稱參數，則會使用指派原始伺服器的網域名稱，如果轉送至原始伺服器 (FTO) 政策包含主機，則會使用來自傳入請求的主機標頭。SNI 不能包含冒號 (`:`)，也不能是 IP 地址。SNI 最多可達 253 個字元。

**allowedCertificateNames （選用，適用於non-S3 自訂原始伺服器）**  
您可以包含 CloudFront 在 TLS 與您的原始伺服器交握期間，用來驗證原始伺服器 TLS 憑證之網域相符的有效憑證名稱清單。此欄位預期有一組有效的網域名稱，並且可以包含萬用字元網域，例如 `*.example.com`。  
您最多可以指定 20 個允許的憑證名稱。每個憑證名稱最多可有 64 個字元。

**Example – 更新至 Amazon S3 請求原始伺服器**  
下列範例會將檢視器請求的原始伺服器變更為 S3 儲存貯體、啟用 OAC，以及重設傳送至原始伺服器的自訂標頭。  

```
cf.updateRequestOrigin({
    "domainName" : "amzn-s3-demo-bucket-in-us-east-1.s3.us-east-1.amazonaws.com",
    "originAccessControlConfig": {
        "enabled": true,
        "signingBehavior": "always",
        "signingProtocol": "sigv4",
        "originType": "s3"
    },
    // Empty object resets any header configured on the assigned origin
    "customHeaders": {}
});
```

**Example – 更新 Application Load Balancer 請求原始伺服器**  
下列範例會將檢視器請求的原始伺服器變更為 Application Load Balancer 原始伺服器，並設定自訂標頭和逾時。  

```
cf.updateRequestOrigin({
    "domainName" : "example-1234567890.us-east-1.elb.amazonaws.com",
    "timeouts": {
        "readTimeout": 30,
        "connectionTimeout": 5
    },
    "customHeaders": {
        "x-stage": "production",
        "x-region": "us-east-1"
    }
});
```

**Example – 在啟用 Origin Shield 的情況下更新原始伺服器**  
在下列範例中，分佈中的原始伺服器已啟用 Origin Shield。函數程式碼只會更新用於原始伺服器的網域名稱，並省略所有其他選用參數。在此情況下，Origin Shield 仍會與修改後的原始網域名稱搭配使用，因為 Origin Shield 參數未更新。  

```
cf.updateRequestOrigin({
    "domainName" : "www.example.com"
});
```

**Example – 更新主機標頭、SNI 和允許的憑證名稱**  
對於大多數使用案例，您不需要對前往原始伺服器的請求使用此類型的修改。除非您了解變更這些值的影響，否則不應使用這些參數。
下列範例會在對原始伺服器的請求上變更網域名稱、主機標頭、SNI 和允許的憑證。  

```
cf.updateRequestOrigin({ 
    "domainName": "www.example.com", 
    "hostHeader": "test.example.com", 
    "sni": "test.example.net", 
    "allowedCertificateNames": ["*.example.com", "*.example.net"],
});
```

## selectRequestOriginById() 方法
<a name="select-request-origin-id-helper-function"></a>

使用 `selectRequestOriginById()` 來更新現有的原始伺服器，方法是選取已在分佈中設定的不同原始伺服器。此方法使用更新原始伺服器所定義的所有相同設定。

此方法僅接受已在執行函數時所用的相同分佈中定義的原始伺服器。原始伺服器由原始伺服器 ID 參考，這是您在設定原始伺服器時定義的原始伺服器名稱。

如果您的分佈中已設定 VPC 原始伺服器，您可以使用此方法將原始伺服器更新為 VPC 原始伺服器。如需詳細資訊，請參閱[使用 VPC 原始伺服器限制存取](private-content-vpc-origins.md)。

**備註**  
`selectRequestOriginById()` 函數無法選取已啟用交互 TLS （原始伺服器） 的原始伺服器。嘗試使用此函數選取已啟用交互 TLS （原始伺服器） 的原始伺服器會導致驗證錯誤。
如果您的使用案例需要使用交互 TLS （原始伺服器） 來選擇動態原始伺服器，請`updateRequestOrigin()`改用 ，確保所有目標原始伺服器都使用相同的用戶端憑證。

**請求**

```
cf.selectRequestOriginById(origin_id, {origin_overrides})
```

在先前的範例中， `origin_id` 是一個字串，指向執行 函數之 分佈中原始伺服器的原始伺服器名稱。`origin_overrides `參數可以包含下列項目：

**hostHeader （選用，適用於non-S3 自訂原始伺服器）**  
向原始伺服器提出請求時要使用的主機標頭。如果未提供，則會使用 `domainName` 參數中的 值。  
如果未提供主機標頭或網域名稱參數，則會使用指派原始伺服器的網域名稱，如果轉送至原始伺服器 (FTO) 政策包含主機，則會使用來自傳入請求的主機標頭。主機標頭不能包含冒號 (`:`)，也不能是 IP 地址。主機標頭最多可達 253 個字元。

**sni （選用，適用於non-S3 自訂原始伺服器）**  
伺服器名稱指示 (SNI) 是 Transport Layer Security (TLS) 通訊協定的延伸，其中用戶端會指出其嘗試在 TLS 交握程序開始時連線到哪個主機名稱。此值應與原始伺服器上 TLS 憑證上的通用名稱相符。否則，您的原始伺服器可能會擲回錯誤。  
如果未提供，則會使用 `hostHeader` 參數中的 值。如果未提供主機標頭，則會使用 `domainName` 參數的值。  
如果未提供主機標頭或網域名稱參數，則會使用指派原始伺服器的網域名稱，如果轉送至原始伺服器 (FTO) 政策包含主機，則會使用來自傳入請求的主機標頭。SNI 不能包含冒號 (`:`)，也不能是 IP 地址。SNI 最多可達 253 個字元。

**allowedCertificateNames （選用，適用於non-S3 自訂原始伺服器）**  
您可以包含 CloudFront 在 TLS 與您的原始伺服器交握期間，用來驗證原始伺服器 TLS 憑證之網域相符的有效憑證名稱清單。此欄位預期有一組有效的網域名稱，並且可以包含萬用字元網域，例如 `*.example.com`。  
您最多可以指定 20 個允許的憑證名稱。每個憑證名稱最多可有 64 個字元。

**請求**

```
selectRequestOriginById(origin_id)
```

在上述範例中，`origin_id` 是指向執行函數之分佈中原始伺服器的原始伺服器名稱的字串。

**Example – 選取 Amazon S3 請求原始伺服器**  
下列範例會從與分佈相關聯的原始伺服器清單中選擇名為 `amzn-s3-demo-bucket-in-us-east-1` 的原始伺服器，並將 `amzn-s3-demo-bucket-in-us-east-1` 原始伺服器的組態設定套用至請求。  

```
cf.selectRequestOriginById("amzn-s3-demo-bucket-in-us-east-1");
```

**Example – 選取 Application Load Balancer 請求原始伺服器**  
下列範例會從與分佈相關聯的原始伺服器清單中選擇名為 `myALB-prod` 的 Application Load Balancer 原始伺服器，並將 `myALB-prod` 的組態設定套用至請求。  

```
cf.selectRequestOriginById("myALB-prod");
```

**Example – 選取 Application Load Balancer 請求原始伺服器並覆寫主機標頭**  
如同上述範例，下列範例`myALB-prod`會從與分佈相關聯的原始伺服器清單中選擇名為 的 Application Load Balancer 原始伺服器，並將 的組態設定套用至`myALB-prod`請求。不過，此範例會使用 覆寫主機標頭值`origin_overrides`。  

```
cf.overrideRequestOrigin("myALB-prod",{ 
        "hostHeader" : "test.example.com"
});
```

## createRequestOriginGroup() 方法
<a name="create-request-origin-group-helper-function"></a>

使用 `createRequestOriginGroup()` 定義兩個原始伺服器，以在需要高可用性的情況下用作容錯移轉的[原始伺服器群組](high_availability_origin_failover.md#concept_origin_groups.creating)。

原始伺服器群組包括兩個原始伺服器 (主要原始伺服器和次要原始伺服器)，以及您指定的容錯移轉準則。您建立原始伺服器群組以支援 CloudFront 中的原始伺服器容錯移轉。當您使用此方法建立或更新原始伺服器群組時，您可以指定原始伺服器群組，而不是單一原始伺服器。CloudFront 將使用容錯移轉準則，從主要原始伺服器容錯移轉至次要原始伺服器。

如果您的分佈中已設定 VPC 原始伺服器，您可以使用此方法來使用 VPC 原始伺服器建立原始伺服器群組。如需詳細資訊，請參閱[使用 VPC 原始伺服器限制存取](private-content-vpc-origins.md)。

**備註**  
`createRequestOriginGroup()` 函數不支援建立包含啟用互斥 TLS （原始伺服器） 的原始伺服器群組。無法透過 CloudFront Functions 動態建立具有互斥 TLS （原始） 原始伺服器的原始伺服器群組。
如果您需要具有相互 TLS （原始伺服器） 的原始伺服器容錯移轉功能，請直接在 CloudFront 分佈設定中設定原始伺服器群組，而不是在函數中動態建立原始伺服器群組。

### 請求
<a name="create-origin-group-request"></a>

```
createRequestOriginGroup({origin_group_properties})
```

在前面的範例中，`origin_group_properties` 包含以下：

**originIds (必要)**  
`origin_ids` 陣列，其中 `origin_id` 是指向執行函數之分佈中原始伺服器的原始伺服器名稱的字串。您必須提供兩個原始伺服器做為陣列的一部分。清單中的第一個原始伺服器是主要原始伺服器，第二個則做為容錯移轉用途的次要原始伺服器。

**originOverrides （選用）**  
 使用 `{origin_overrides}` 參數可覆寫一些進階設定。`origin overrides` 可以包含以下：    
**hostHeader （選用，適用於non-S3 自訂原始伺服器）**  
向原始伺服器提出請求時要使用的主機標頭。如果未提供，則會使用 `domainName` 參數中的 值。  
如果未提供主機標頭或網域名稱參數，則會使用指派原始伺服器的網域名稱，如果轉送至原始伺服器 (FTO) 政策包含主機，則會使用來自傳入請求的主機標頭。主機標頭不能包含冒號 (`:`)，也不能是 IP 地址。主機標頭最多可達 253 個字元。  
**sni （選用，適用於non-S3 自訂原始伺服器）**  
伺服器名稱指示 (SNI) 是 Transport Layer Security (TLS) 通訊協定的延伸，用戶端會在 TLS 交握程序開始時，指出其嘗試連線到哪個主機名稱。此值應與原始伺服器上 TLS 憑證上的通用名稱相符，否則原始伺服器可能會擲回錯誤。  
如果未提供，則會使用 `hostHeader` 參數中的 值。如果未提供主機標頭，則會使用 `domainName` 參數的值。  
如果未提供主機標頭或網域名稱參數，則會使用指派原始伺服器的網域名稱，如果轉送至原始伺服器 (FTO) 政策包含主機，則會使用來自傳入請求的主機標頭。SNI 不能包含冒號 (`:`)，也不能是 IP 地址。SNI 最多可達 253 個字元。  
**allowedCertificateNames （選用，適用於non-S3 自訂原始伺服器）**  
您可以包含 CloudFront 在 TLS 與您的原始伺服器交握期間，用來驗證原始伺服器 TLS 憑證之網域相符的有效憑證名稱清單。此欄位預期有一組有效的網域名稱，並且可以包含萬用字元網域，例如 `*.example.com`。  
您最多可以指定 20 個允許的憑證名稱。每個憑證名稱最多可有 64 個字元。

**selectionCriteria (選用)**  
選取要使用 `default` 原始伺服器容錯移轉準則或使用 `media-quality-score` 型容錯移轉邏輯。有效值如下：  
+ `default` 根據 `failoverCriteria` 中指定的狀態碼使用容錯移轉準則。如果您未在函數中設定 `selectionCriteria`，則會使用 `default`。
+ 使用媒體感知路由功能時，會使用 `media-quality-score`。

**failoverCriteria (必要)**  
從主要原始伺服器傳回的狀態碼陣列，將觸發 CloudFront 容錯移轉至次要原始伺服器。如果您覆寫現有的原始伺服器群組，此陣列會覆寫原始伺服器群組原始組態中設定的所有容錯移轉狀態碼。  
當您使用 `media-quality-score` `selectionCriteria` 時，CloudFront 會根據媒體品質分數嘗試路由請求。如果選取的原始伺服器傳回此陣列中設定的錯誤碼，CloudFront 會容錯移轉至另一個原始伺服器。

**Example – 建立請求原始伺服器群組**  
下列範例會使用原始伺服器 ID 為請求建立原始伺服器群組。這些原始伺服器 ID 來自用於執行此函數之分佈的原始伺服器群組組態。  
或者，您可以使用 `originOverrides` 覆寫 `sni`、 `hostHeader`和 的原始伺服器群組組態`allowedCertificateNames`。  

```
import cf from 'cloudfront';

function handler(event) {
    cf.createRequestOriginGroup({
        "originIds": [
            {
                "originId": "origin-1",
                "originOverrides": {
                    "hostHeader": "hostHeader.example.com",
                    "sni": "sni.example.com",
                    "allowedCertificateNames": ["cert1.example.com", "cert2.example.com", "cert3.example.com"]
                }
            },
            {
                "originId": "origin-2",
                "originOverrides": {
                    "hostHeader": "hostHeader2.example.com",
                    "sni": "sni2.example.com",
                    "allowedCertificateNames": ["cert4.example.com", "cert5.example.com"]
                }
            }
        ],
        "failoverCriteria": {
            "statusCodes": [500]
        }
    });
    
    event.request.headers['x-hookx'] = { value: 'origin-overrides' };
    return event.request;
}
```

# CloudFront SaaS Manager 屬性的 helper 方法
<a name="saas-specific-logic-function-code"></a>

使用 CloudFront SaaS Manager 的下列 helper 函數，在您建立的函數中擷取多租用戶分佈的值。若要在此頁面上使用範例，您必須先使用 JavaScript 執行時期 2.0 建立 CloudFront 函數。如需詳細資訊，[適用於 CloudFront Functions 的 JavaScript 執行時期 2.0 功能](functions-javascript-runtime-20.md)。

**Topics**
+ [連線群組](#connection-groups-helper-function)
+ [分佈租用戶](#distribution-tenants-helper-functions)

## 連線群組
<a name="connection-groups-helper-function"></a>

與分佈租用戶相關聯的連線群組具有網域名稱。

若要取得此值，請使用事件物件 `context` 子物件的 `endpoint` 欄位。

**請求**

```
const value = event.context.endpoint;
```

**回應**

回應是包含連線群組網域名稱的 `string`，例如 d111111abcdef8.cloudfront.net。只有當您的函數被調用具有相關聯連線群組的多租用戶分佈時，`endpoint` 欄位才會出現。如需詳細資訊，請參閱[內容物件](functions-event-structure.md#functions-event-structure-context)。

## 分佈租用戶
<a name="distribution-tenants-helper-functions"></a>

CloudFront Functions 有一個模組，可讓您存取特定的分佈租用戶值。

若要使用此模組，請在函數程式碼的第一行中包含下列陳述式：

```
import cf from 'cloudfront';
```

您只能在 `handler` 函數中直接或透過任何巢狀呼叫函數使用下列範例。

### `distributionTenant.id` 欄位
<a name="distribution-tenants-field"></a>

使用此欄位來取得分佈租用戶 ID 的值。

**請求**

```
const value = cf.distributionTenant.id;
```

**回應**

回應是包含分佈租用戶 ID 的 `string`，例如 `dt_1a2b3c4d5e6f7`。

**錯誤處理**

如果您的函數是針對標準分佈調用，則指定 `distributionTenant.id` 欄位將傳回 `distributionTenant module is not available` 類型錯誤。若要處理此使用案例，您可以將 `try` 和 `catch` 區塊新增至程式碼。

### `distributionTenant.parameters.get()` 方法
<a name="distribution-tenant-parameters-get-method"></a>

使用此方法可傳回您指定之分佈租用戶參數名稱的值。

```
distributionTenant.parameters.get("key");
```

`key`：您要為其擷取值的分佈租用戶參數名稱。

**請求 **

```
const value = distributionTenant.parameters.get("key");
```

**回應**

回應是 `string`，其中包含分佈租用戶參數的值。例如，如果您的鍵名稱為 `TenantPath`，則此參數的值可能是 `tenant1`。

**錯誤處理**

您可能會收到下列錯誤：
+ 如果您的函數是針對標準分佈調用，則 `distributionTenant.parameters.get()` 方法將傳回 `distributionTenant module is not available` 類型錯誤。
+ 當您指定的分佈租用戶參數不存在時，會傳回 `DistributionTenantParameterKeyNotFound` 錯誤。

若要管理這些使用案例，您可以將 `try` 和 `catch` 區塊新增至程式碼。

# 使用 async 和 await
<a name="async-await-syntax"></a>

CloudFront Functions JavaScript 執行時期函數 2.0 提供處理 `Promise` 物件的 `async` 和 `await` 語法。Promises 代表可以透過函數中標記為 `async` 的關鍵字 `await` 存取延遲結果。各種新的 WebCrypto 函數使用 Promises。

如需 `Promise` 物件的詳細資訊，請參閱 [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)。

**注意**  
您必須針對下列程式碼範例使用 JavaScript 執行時期 2.0。  
`await` 只能在 `async` 函數內使用，不支援 `async` 引數和關閉。

```
async function answer() {
    return 42;
}

// Note: async, await can be used only inside an async function. async arguments and closures are not supported.

async function handler(event) {
    // var answer_value = answer(); // returns Promise, not a 42 value
    let answer_value = await answer(); // resolves Promise, 42
    console.log("Answer"+answer_value);
    event.request.headers['answer'] = { value : ""+answer_value };
    return event.request;
}
```

下面的範例 JavaScript 程式碼顯示如何使用 `then` 鏈接方法查看 Promises。您可以使用 `catch` 來檢視錯誤。

**警告**  
使用 promise 組合器 (例如 `Promise.all`、`Promise.any`) 和 promise 鏈接方法 (例如 `then` 和 `catch`) 可能需要高函數記憶體用量。如果您的函數超過[函數記憶體配額上限](cloudfront-limits.md#limits-functions)，則無法執行。為了避免此錯誤，建議您使用 `await` 語法，而非 `promise` 方法。

```
async function answer() {
    return 42;
}

async function squared_answer() {
   return answer().then(value => value * value)
} 
// Note: async, await can be used only inside an async function. async arguments and closures are not supported.
async function handler(event) {
    // var answer_value = answer(); // returns Promise, not a 42 value
    let answer_value = await squared_answer(); // resolves Promise, 42
    console.log("Answer"+answer_value);
    event.request.headers['answer'] = { value : ""+answer_value };
    return event.request;
}
```

# CloudFront Functions 的 CWT 支援
<a name="cwt-support-cloudfront-functions"></a>

本節提供 CloudFront Functions 中 CBOR Web Token (CWT) 支援的詳細資訊，可在 CloudFront Edge Locations 啟用安全字符型身分驗證和授權。此支援是以模組形式提供，可在 CloudFront Function 中存取。

若要使用此模組，請使用 JavaScript 執行期 2.0 建立 CloudFront 函數，並在函數程式碼的第一行中包含下列陳述式：

```
import cf from 'cloudfront';
```

與此模組相關聯的方法可透過 存取 （其中 \$1 是萬用字元，代表模組中存在的不同函數）：

```
cf.cwt.*
```

如需詳細資訊，請參閱[適用於 CloudFront Functions 的 JavaScript 執行時期 2.0 功能](functions-javascript-runtime-20.md)。

目前，模組僅支援具有 HS256 (HMAC-SHA256) 演算法的 MAC0 結構，字符大小上限為 1KB。

## 字符結構
<a name="token-structure"></a>

本節涵蓋 CWT 模組預期的字符結構。模組預期字符會正確標記和識別 （例如 COSE MAC0)。此外，對於字符的結構，模組遵循 [CBOR 物件簽署和加密 (COSE) 【RFC 8152】](https://datatracker.ietf.org/doc/html/rfc8152) 設定的標準。

```
( // CWT Tag (Tag value: 61) --- optional    
    ( // COSE MAC0 Structure Tag (Tag value: 17) --- required        
        [            
            protectedHeaders,            
            unprotectedHeaders,            
            payload,            
            tag,        
        ]    
    )
)
```

**Example ：使用 COSE MAC0 結構的 CWT**  

```
61( // CWT tag     
    17( // COSE_MAC0 tag       
        [         
            { // Protected Headers           
                1: 4  // algorithm : HMAC-256-64         
            },         
            { // Unprotected Headers           
                4: h'53796d6d6574726963323536' // kid : Symmetric key id          
            },         
            { // Payload           
                1: "https://iss.example.com", // iss           
                2: "exampleUser", // sub           
                3: "https://aud.example.com", // aud           
                4: 1444064944, // exp           
                5: 1443944944, // nbf           
                6: 1443944944, // iat         
            },         
            h'093101ef6d789200' // tag       
        ]     
    )   
)
```
產生字符時，CWT 標籤是選用的。不過，需要 COSE 結構標籤。

## validateToken() 方法
<a name="validatetoken-method"></a>

函數會使用指定的金鑰解碼和驗證 CWT 字符。如果驗證成功，則會傳回解碼的 CWT 字符。否則，它會擲回錯誤。請注意，此函數不會對宣告集進行驗證。

### 請求
<a name="validatetoken-request"></a>

```
cf.cwt.validateToken(token, handlerContext{key})
```Parameters

**字符 （必要）**  
用於驗證的編碼字符。這必須是 JavaScript 緩衝區。

**handlerContext （必要）**  
存放 validateToken 呼叫內容的 JavaScript 物件。目前僅支援 金鑰屬性。

**金鑰 （必要）**  
訊息摘要運算的私密金鑰。可做為字串或 JavaScript 緩衝區提供。

### 回應
<a name="validatetoken-response"></a>

當 `validateToken()`方法傳回成功驗證的字符時，函數的回應會以下列格式`CWTObject`顯示 。解碼後，所有宣告金鑰都會以字串表示。

```
CWTObject {    
    protectedHeaders,    
    unprotectedHeaders,    
    payload
}
```

### 範例 - 使用作為字符一部分傳送的 kid 驗證字符
<a name="validatetoken-example"></a>

此範例示範 CWT 字符驗證，其中會從 標頭擷取 kid。接著 kid 會傳遞至 CloudFront Functions KeyValueStore，以擷取用於驗證權杖的私密金鑰。

```
import cf from 'cloudfront'

const CwtClaims = {
   iss: 1,
   aud: 3,
   exp: 4
}

async function handler(event) {
    try {
        let request = event.request;
        let encodedToken = request.headers['x-cwt-token'].value;
        let kid = request.headers['x-cwt-kid'].value;
                
        // Retrieve the secret key from the kvs
        let secretKey = await cf.kvs().get(kid);
                 
        // Now you can use the secretKey to decode & validate the token.
        let tokenBuffer = Buffer.from(encodedToken, 'base64url');
                
        let handlerContext = {
           key: secretKey,
        }
                
        try {
            let cwtObj = cf.cwt.validateToken(tokenBuffer, handlerContext);
                        
            // Check if token is expired
            const currentTime = Math.floor(Date.now() / 1000); // Current time in seconds
            if (cwtObj[CwtClaims.exp] && cwtObj[CwtClaims.exp] < currentTime) {
                return {
                    statusCode: 401,
                    statusDescription: 'Token expired'
                };
            }
        } catch (error) {
            return {
               statusCode: 401,
               statusDescription: 'Invalid token'
            };
         }
    } catch (error) {
        return {
            statusCode: 402,
            statusDescription: 'Token processing failed'
        };
     }
    return request;
}
```

## generateToken() 方法
<a name="generatetoken-method"></a>

此函數會使用提供的承載和內容設定產生新的 CWT 字符。

### 請求
<a name="generatetoken-request"></a>

```
cf.cwt.generateToken(generatorContext, payload)
```Parameters

**generatorContext （必要）**  
這是 JavaScript 物件，可做為產生字符的內容，並包含下列索引鍵值對：    
**cwtTag （選用）**  
此值是布林值，如果 `true`指定`cwtTag`應新增 。  
**coseTag （必要）**  
指定 COSE 標籤類型。目前僅支援 `MAC0`。  
**金鑰 （必要）**  
用於運算訊息摘要的私密金鑰。此值可以是字串或 JavaScript `Buffer`。

**承載 （必要）**  
用於編碼的字符承載。承載必須是 `CWTObject` 格式。

### 回應
<a name="generatetoken-response"></a>

傳回包含編碼字符的 JavaScript 緩衝區。

**Example ：產生 CWT 字符**  

```
import cf from 'cloudfront';

const CwtClaims = {
    iss: 1,
    sub: 2,
    exp: 4
};

const CatClaims = {
    catu: 401,
    catnip: 402,
    catm: 403,
    catr: 404
};

const Catu = {
    host: 1,
    path: 2,
    ext: 3
};

const CatuMatchTypes = {
    prefix_match: 1,
    suffix_match: 2,
    exact_match: 3
};

const Catr = {
    renewal_method: 1,
    next_renewal_time: 2,
    max_uses: 3
};

async function handler(event) {
    try {
        const response = {
            statusCode: 200,
            statusDescription: 'OK',
            headers: {}
        };
        
        const commonAccessToken = {
            protected: {
                1: "5",
            },
            unprotected: {},
            payload: {
                [CwtClaims.iss]: "cloudfront-documentation",
                [CwtClaims.sub]: "cwt-support-on-cloudfront-functions",
                [CwtClaims.exp]: 1740000000,
                [CatClaims.catu]: {
                    [Catu.host]: {
                        [CatuMatchTypes.suffix_match]: ".cloudfront.net"
                    },
                    [Catu.path]: {
                        [CatuMatchTypes.prefix_match]: "/media/live-stream/cf-4k/"
                    },
                    [Catu.ext]: {
                        [CatuMatchTypes.exact_match]: [
                            ".m3u8",
                            ".ts",
                            ".mpd"
                        ]
                    }
                },
                [CatClaims.catnip]: [
                    "[IP_ADDRESS]",
                    "[IP_ADDRESS]"
                ],
                [CatClaims.catm]: [
                    "GET",
                    "HEAD"
                ],
                [CatClaims.catr]: {
                    [Catr.renewal_method]: "header_renewal",
                    [Catr.next_renewal_time]: 1750000000,
                    [Catr.max_uses]: 5
                }
            }
        };
        
        if (!request.headers['x-cwt-kid']) {
            throw new Error('Missing x-cwt-kid header');
        }
        
        const kid = request.headers['x-cwt-kid'].value;
        const secretKey = await cf.kvs().get(kid);
        
        if (!secretKey) {
            throw new Error('Secret key not found for provided kid');
        }
        
        try {
            const genContext = {
                cwtTag: true,
                coseTag: "MAC0",
                key: secretKey
            };
            
            const tokenBuffer = cf.cwt.generateToken(commonAccessToken, genContext);
            response.headers['x-generated-cwt-token'] = { value: tokenBuffer.toString('base64url') };
                        
            return response;
        } catch (tokenError) {
            return {
                statusCode: 401,
                statusDescription: 'Could not generate the token'
            };
        }
    } catch (error) {
        return {
            statusCode: 402,
            statusDescription: 'Token processing failed'
        };
    }
}
```

**Example ：根據某些邏輯重新整理權杖**  

```
import cf from 'cloudfront'

const CwtClaims = {
   iss: 1,
   aud: 3,
   exp: 4
}

async function handler(event) {
    try {
        let request = event.request;
        let encodedToken = request.headers['x-cwt-token'].value;
        let kid = request.headers['x-cwt-kid'].value;
        let secretKey = await cf.kvs().get(kid); // Retrieve the secret key from the kvs
                
        // Now you can use the secretKey to decode & validate the token.
        let tokenBuffer = Buffer.from(encodedToken, 'base64url');
                
        let handlerContext = {
           key: secretKey,
        }
                
        try {
            let cwtJSON = cf.cwt.validateToken(tokenBuffer, handlerContext);
                        
            // Check if token is expired
            const currentTime = Math.floor(Date.now() / 1000); // Current time in seconds
            if (cwtJSON[CwtClaims.exp] && cwtJSON[CwtClaims.exp] < currentTime) {
                // We can regnerate the token and add 8 hours to the expiry time
                cwtJSON[CwtClaims.exp] = Math.floor(Date.now() / 1000) + (8 * 60 * 60);
                                
                let genContext = {
                  coseTag: "MAC0",
                  key: secretKey
                }
                                
                let newTokenBuffer = cf.cwt.generateToken(cwtJSON, genContext);
                 request.headers['x-cwt-regenerated-token'] = newTokenBuffer.toString('base64url');
            }
        } catch (error) {
            return {
               statusCode: 401,
               statusDescription: 'Invalid token'
            };
         }
    }
    catch (error) {
        return {
            statusCode: 402,
            statusDescription: 'Token processing failed'
        };
     }
    return request;
}
```

# 一般協助程式方法
<a name="general-helper-methods"></a>

此頁面提供 CloudFront Functions 內的其他協助程式方法。若要使用這些方法，請使用 JavaScript 執行期 2.0 建立 CloudFront 函數。

```
import cf from 'cloudfront';
```

如需詳細資訊，請參閱[適用於 CloudFront Functions 的 JavaScript 執行時期 2.0 功能](functions-javascript-runtime-20.md)。

## `edgeLocation` 中繼資料
<a name="edge-location-metadata"></a>

此方法需要使用 `cloudfront`模組。

**注意**  
您只能將此方法用於檢視器請求函數。對於檢視器-回應函數，此方法為空。

使用此 JavaScript 物件來取得節點機場代碼、預期的[區域節點快取](HowCloudFrontWorks.md#CloudFrontRegionaledgecaches)區域或用於處理請求的 CloudFront 伺服器 IP 地址。此中繼資料僅適用於檢視器請求事件觸發。

```
cf.edgeLocation = {
    name: SEA
    serverIp: 1.2.3.4
    region: us-west-2
}
```

`cf.edgeLocation` 物件可以包含下列項目：

**name**  
處理請求之節點的三個字母 [IATA 代碼](https://en.wikipedia.org/wiki/IATA_airport_code)。

**serverIp**  
處理請求之伺服器的 IPv4 或 IPv6 地址。

**region**  
如果發生快取遺漏，*預期*請求使用的 CloudFront Regional Edge Cache (REC)。如果預期的 REC 無法使用，且請求使用備份 REC，則不會更新此值。這不包括正在使用的 Origin Shield 位置，除非主要 REC 和 Origin Shield 是相同的位置。

**注意**  
當 CloudFront 設定為使用原始伺服器容錯移轉時，不會再次叫用 CloudFront Functions。如需詳細資訊，請參閱[透過 CloudFront 原始伺服器容錯移轉最佳化高可用性](high_availability_origin_failover.md)。

## `rawQueryString()` 方法
<a name="raw-query-string-method"></a>

此方法不需要 `cloudFront`模組。

使用 `rawQueryString()`方法將未剖析和未變更的查詢字串擷取為字串。

**請求**

```
function handler(event) {
    var request = event.request;
    const qs = request.rawQueryString();
}
```

**回應**

將傳入請求的完整查詢字串傳回為字串值，而不含前置 `?`。
+ 如果沒有查詢字串，但 `?` 存在，則函數會傳回空字串。
+ 如果沒有查詢字串且 `?` 不存在，則函數會傳回 `undefined`。

**案例 1：傳回完整查詢字串 （沒有前置 `?`)**  
傳入請求 URL： `https://example.com/page?name=John&age=25&city=Boston`  
`rawQueryString()` 傳回： `"name=John&age=25&city=Boston"`

**案例 2：傳回空字串 （當 `?` 存在但沒有參數時）**  
傳入請求 URL： `https://example.com/page?`  
`rawQueryString()` 傳回： `""`

**案例 3：`undefined`傳回 （沒有查詢字串，也沒有 `?`)**  
傳入請求 URL： `https://example.com/page`  
`rawQueryString()` 傳回： `undefined`

# 建立函數
<a name="create-function"></a>

您可以分兩個階段建立函數：

1. 建立函數程式碼做為 JavaScript。您可以從 CloudFront 主控台使用預設範例或自行撰寫。如需詳細資訊，請參閱下列主題：
   + [撰寫函數程式碼](writing-function-code.md)
   + [CloudFront Functions 事件結構](functions-event-structure.md)
   + [CloudFront 的 CloudFront Functions 範例](service_code_examples_cloudfront_functions_examples.md)

1. 然後，您可以使用 CloudFront 建立函數並包含程式碼。程式碼存在於函數內部 (而不是引用形式)。

------
#### [ Console ]

**建立 函數**

1. 在 [https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions) 登入 CloudFront 主控台，並選擇**函數**頁面。

1. 選擇 **Create function (建立函數)**。

1. 輸入在 AWS 帳戶 內唯一的函數名稱，然後選擇 JavaScript 版本，接著選擇**繼續**。新函數的詳細資訊頁面會隨即顯示。
**注意**  
如要在函數中使用[鍵值對](kvs-with-functions.md)，您必須選擇 JavaScript 執行時期 2.0。

1. 在**函數程式碼**區段中，選擇**建置**索引標籤，然後輸入您的函數程式碼。**建置**索引標籤中包含的範例程式碼會說明函數程式碼的基本語法。

1. 選擇**儲存變更**。

1. 如果函數程式碼使用鍵值對，則必須關聯鍵值存放區。

   在首次建立函數時，您可以關聯鍵值存放區。或者，您可以稍後透過[更新函數](update-function.md)來關聯它。

   若要立即關聯鍵值存放區，請依照下列步驟執行：
   + 前往**關聯 KeyValueStore** 區段，然後選擇**關聯現有的 KeyValueStore**。
   + 選取函數中包含鍵值對的鍵值存放區，然後選擇**關聯 KeyValueStore**。

   CloudFront 會立即將存放區與該函數建立關聯。您無需儲存函數。

------
#### [ CLI ]

如果您使用 CLI，通常會先在檔案中建立函數程式碼，然後使用 AWS CLI 來建立函數。

**建立 函數**

1. 在檔案中建立函數程式碼，並將其儲存於電腦可以連線的目錄中。

1. 執行命令，如範例所示。此範例會使用 `fileb://` 標記法來傳入檔案。它還會包括換行符號，讓命令更易於讀取。

   ```
   aws cloudfront create-function \
       --name MaxAge \
       --function-config '{"Comment":"Max Age 2 years","Runtime":"cloudfront-js-2.0","KeyValueStoreAssociations":{"Quantity":1,"Items":[{"KeyValueStoreARN":"arn:aws:cloudfront::111122223333:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"}]}}' \
       --function-code fileb://function-max-age-v1.js
   ```
**備註**  
`Runtime` – JavaScript 版本。如要在函數中使用[鍵值對](kvs-with-functions.md)，您必須指定版本 2.0。
`KeyValueStoreAssociations` – 如果您的函數使用鍵值對，則可以在初次建立函數時關連鍵值存放區。或者，您可以稍後使用 `update-function` 來關聯它。`Quantity` 永遠等於 `1`，因為每個函數只能有一個與其關聯的鍵值存放區。

   如果命令成功執行，您會看到如下所示的輸出。

   ```
   ETag: ETVABCEXAMPLE
   FunctionSummary:
     FunctionConfig:
       Comment: Max Age 2 years
       Runtime: cloudfront-js-2.0
       KeyValueStoreAssociations= \
         {Quantity=1, \
         Items=[{KeyValueStoreARN='arn:aws:cloudfront::111122223333:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111'}]} \
     FunctionMetadata:
       CreatedTime: '2021-04-18T20:38:56.915000+00:00'
       FunctionARN: arn:aws:cloudfront::111122223333:function/MaxAge
       LastModifiedTime: '2023-11-19T20:38:56.915000+00:00'
       Stage: DEVELOPMENT
     Name: MaxAge
     Status: UNPUBLISHED
   Location: https://cloudfront.amazonaws.com/2020-05-31/function/arn:aws:cloudfront:::function/MaxAge
   ```

   大多數資訊都是從請求中複製的。其他資訊會由 CloudFront 新增。
**備註**  
`ETag` – 每次修改鍵值存放區時，這個值都會變更。您可以使用此值和函數名稱，以在未來參考函數。確保始終使用目前的 `ETag`。
`FunctionARN` – CloudFront 函數的 ARN。
111122223333 – AWS 帳戶。
`Stage` – 函數的階段 (`LIVE` 或 `DEVELOPMENT`)。
`Status` – 函數的狀態 (`PUBLISHED` 或 `UNPUBLISHED`)。

------

建立函數之後，該函數會新增至 `DEVELOPMENT` 階段。建議您[在發佈函數](publish-function.md)之前進行[測試](test-function.md)。發佈函數之後，函數會變更為 `LIVE` 階段。

# 測試函數
<a name="test-function"></a>

在將函數部署到即時階段 (生產環境) 之前，可以測試該函數以確保其運作正常。若要測試函數，您可以指定一個*事件物件*，代表您的 CloudFront 分佈可在生產環境中接收的 HTTP 請求或回應。

CloudFront Functions 會執行下列作業：

1. 執行該函數，使用提供的事件物件作為函數的輸入。

1. 返回函數的結果 (修改後的事件物件)，同時返回任何函數日誌或錯誤訊息以及函數的*運算利用率*。如需運算使用率的詳細資訊，請參閱 [了解運算利用率](#compute-utilization)。

**注意**  
當您測試函數時，CloudFront 只會驗證函數執行錯誤。CloudFront 不會驗證發佈後請求是否成功通過。例如，如果您的函數刪除必要的標頭，測試將會成功，因為程式碼沒有問題。不過，如果您發佈函數並將其與分佈建立關聯，則函數會在透過 CloudFront 提出請求時失敗。

**Contents**
+ [設定事件物件](#test-function-create-event)
+ [測試函數](#test-function-step-test)
+ [了解運算利用率](#compute-utilization)

## 設定事件物件
<a name="test-function-create-event"></a>

在測試函數之前，您必須建立事件物件以進行測試。有幾種選項。

**選項 1：設定事件物件而不儲存**  
您可以在 CloudFront 主控台的視覺化編輯器中設定事件物件，而不儲存該事件物件。  
您可以使用此事件物件從 CloudFront 主控台測試函數，即使該函數尚未儲存也是如此。

**選項 2：在視覺化編輯器中建立事件物件**  
您可以在 CloudFront 主控台的視覺化編輯器中設定事件物件，而不儲存該事件物件。您可以針對每個函數建立 10 個事件物件，例如，可測試不同的可能輸入。  
以這種方式建立事件物件時，您可以使用事件物件，在 CloudFront 主控台中測試函數。您無法使用它來測試使用 AWS API 或 SDK 的函數。

**選項 3：使用文字編輯器建立事件物件**  
您可以使用文字編輯器，以 JSON 格式建立事件物件。如需有關事件物件結構的詳細資訊，請參閱 [事件結構](functions-event-structure.md)。  
您可以使用此事件物件來測試使用 CLI 的函數。但是您無法使用它來測試 CloudFront 主控台中的函數。

**建立事件物件 (選項 1 或 2)**

1. 在 [https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions) 登入 CloudFront 主控台，並選擇**函數**頁面。

   選擇您要測試的函數。

1. 在函數詳細資訊頁面上，選擇**測試**索引標籤。

1. 針對**事件類型**，選擇以下其中一個選項：
   + 如果函數會根據請求修改 HTTP 請求或產生回應，請選擇**檢視者請求**。**請求**區段隨即出現。
   + 選擇**檢視器回應**。**請求**和**回應**區段隨即出現。

1. 完成您想要包含在事件中的所有欄位。您可選擇**編輯 JSON** 來檢視原始 JSON。

1. (選用) 若要儲存事件，請選擇**儲存**，然後在**儲存測試事件**中輸入名稱，然後選擇**儲存**。

   您也可以選擇**編輯 JSON** 並複製原始 JSON，並將其儲存在 CloudFront 之外您自己的檔案中。

**建立事件物件 (選項 3)**

使用文字編輯器建立事件物件。將檔案儲存於電腦可以連線的目錄中。

請確定您遵循這些準則：
+ 省略 `distributionDomainName`、`distributionId` 和 `requestId` 欄位。
+ 標頭、Cookie 和查詢字串的名稱必須為小寫。

以這種方式建立事件物件的一個選項是使用視覺化編輯器建立範例。您可以確定範例格式正確。然後您可以複製原始 JSON 並將其貼到文字編輯器中並儲存檔案。

如需有關事件結構的詳細資訊，請參閱 [事件結構](functions-event-structure.md)。

## 測試函數
<a name="test-function-step-test"></a>

您可以在 CloudFront 主控台或使用 AWS Command Line Interface () 測試函數AWS CLI。

------
#### [ Console ]

**若要測試函數**

1. 在 [https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions) 登入 CloudFront 主控台，並選擇**函數**頁面。

1. 選擇您要測試的函數。

1. 選擇**測試**標籤。

1. 確定已顯示正確的事件。若要從目前顯示的事件切換，請在**選取測試事件**欄位中選擇另一個事件。

1. 選擇**測試函數**。控制台顯示函數的輸出，包括函數日誌和運算使用率。

------
#### [ CLI ]

您可以使用 **aws cloudfront test-function** 命令來測試函數。

**若要測試函數**

1. 開啟命令列視窗。

1. 從包含指定檔案的目錄執行下列命令。

   此範例會使用 `fileb://` 標記法來傳入事件物件檔案。它還會包括換行符號，讓命令更易於讀取。

   ```
   aws cloudfront test-function \
       --name MaxAge \
       --if-match ETVABCEXAMPLE \
       --event-object fileb://event-maxage-test01.json \
       --stage DEVELOPMENT
   ```
**備註**  
您可以透過其名稱和 ETag (在 `if-match` 參數中) 引用該函數。您可以依照事件物件在檔案系統中的位置來參照事件物件。
此階段可以是 `DEVELOPMENT` 或 `LIVE`。

   如果命令成功執行，您會看到如下所示的輸出。

   ```
   TestResult:
     ComputeUtilization: '21'
     FunctionErrorMessage: ''
     FunctionExecutionLogs: []
     FunctionOutput: '{"response":{"headers":{"cloudfront-functions":{"value":"generated-by-CloudFront-Functions"},"location":{"value":"https://aws.amazon.com/cloudfront/"}},"statusDescription":"Found","cookies":{},"statusCode":302}}'
     FunctionSummary:
       FunctionConfig:
         Comment: MaxAge function
         Runtime: cloudfront-js-2.0
         KeyValueStoreAssociations= \
         {Quantity=1, \
         Items=[{KeyValueStoreARN='arn:aws:cloudfront::111122223333:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111'}]} \
       FunctionMetadata:
         CreatedTime: '2021-04-18T20:38:56.915000+00:00'
         FunctionARN: arn:aws:cloudfront::111122223333:function/MaxAge
         LastModifiedTime: '2023-17-20T10:38:57.057000+00:00'
         Stage: DEVELOPMENT
       Name: MaxAge
       Status: UNPUBLISHED
   ```

------

**備註**  
`FunctionExecutionLogs` 包含函數在 `console.log()` 語句中撰寫的日誌行清單 (如果有的話)。
`ComputeUtilization` 包含執行函數的相關資訊。請參閱 [了解運算利用率](#compute-utilization)。
`FunctionOutput` 包含該函數返回的事件物件。

## 了解運算利用率
<a name="compute-utilization"></a>

**運用利用率**是指執行函數所花費的時間，以所允許時間上限的百分比表示。例如，值為 35 表示函數在最大允許時間的 35% 內完成。

如果某個函數持續時間，超過允許時間上限，則 CloudFront 會將該函數限流。下列清單說明，根據運算利用率的值，函數限流的可能性。

**運算利用率值：**
+ **1 — 50**— 函數遠低於允許時間上限，應不會受到限流。
+ **51 — 70**— 函數接近允許時間上限。考慮將函數程式碼最佳化。
+ **71 — 100**— 函數非常接近或超過允許時間上限。如果您將此函數與分佈產生關聯，CloudFront 很有可能會將此函數限流。

# 更新函數
<a name="update-function"></a>

您隨時都可以更新。這些變更只會對 `DEVELOPMENT` 階段中的函數版本進行。若要將更新從 `DEVELOPMENT` 階段複製到 `LIVE`，您必須[發佈函數](publish-function.md)。

您可以在 CloudFront 主控台中或使用 AWS Command Line Interface (AWS CLI) 更新函數的程式碼。

------
#### [ Console ]

**更新函數程式碼**

1. 在 [https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions) 登入 CloudFront 主控台，並選擇**函數**頁面。

   選擇要更新的函數。

1. 選擇**編輯**，然後進行您想要的變更：
   + 更新**詳細資訊**區段中的任何欄位。
   + 變更或移除關聯的鍵值存放區。如需鍵值存放區的詳細資訊，請參閱 [Amazon CloudFront KeyValueStore](kvs-with-functions.md)。
   + 變更函數程式碼。選擇**建置**索引標籤，進行變更，然後選擇**儲存變更**，儲存對程式碼的變更。

------
#### [ CLI ]

**若要更新函數程式碼**

1. 開啟命令列視窗。

1. 執行下列命令。

   此範例會使用 `fileb://` 標記法來傳入檔案。它還會包括換行符號，讓命令更易於讀取。

   ```
   aws cloudfront update-function \
       --name MaxAge \
       --function-config '{"Comment":"Max Age 2 years","Runtime":"cloudfront-js-2.0","KeyValueStoreAssociations":{"Quantity":1,"Items":[{"KeyValueStoreARN":"arn:aws:cloudfront::111122223333:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"}]}}' \
       --function-code fileb://function-max-age-v1.js \
       --if-match ETVABCEXAMPLE
   ```
**備註**  
您可以透過名稱和 ETag (在 `if-match` 參數中) 來識別函數。請確定您使用目前的 ETag。您可以從 [DescribeFunction](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_DescribeFunction.html) API 作業取得此值。
即使您不想要變更，您也必須包含 `function-code`。
要小心 `function-config`。您應該傳遞您想要在組態中保留的所有內容。具體而言，請依下列方式處理鍵值存放區：  
若要保留現有的鍵值存放區關聯 (如果有的話)，請指定*現有*存放區的名稱。
若要變更關聯，請指定*新*鍵值存放區的名稱。
若要移除關聯，請省略 `KeyValueStoreAssociations` 參數。

   如果命令成功執行，您會看到如下所示的輸出。

   ```
   ETag: ETVXYZEXAMPLE
   FunctionSummary:
     FunctionConfig:
       Comment: Max Age 2 years \
       Runtime: cloudfront-js-2.0 \
       KeyValueStoreAssociations= \
         {Quantity=1, \
         Items=[{KeyValueStoreARN='arn:aws:cloudfront::111122223333:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111'}]} \
     FunctionMetadata: \
       CreatedTime: '2021-04-18T20:38:56.915000+00:00' \
       FunctionARN: arn:aws:cloudfront::111122223333:function/MaxAge \
       LastModifiedTime: '2023-12-19T23:41:15.389000+00:00' \
       Stage: DEVELOPMENT \
     Name: MaxAge \
     Status: UNPUBLISHED
   ```

------

大多數資訊都是從請求中複製的。其他資訊會由 CloudFront 新增。

**備註**  
`ETag` – 每次修改鍵值存放區時，這個值都會變更。
`FunctionARN` – CloudFront 函數的 ARN。
`Stage` – 函數的階段 (`LIVE` 或 `DEVELOPMENT`)。
`Status` – 函數的狀態 (`PUBLISHED` 或 `UNPUBLISHED`)。

# 發佈函數
<a name="publish-function"></a>

您發佈函數時，這會將函數從 `DEVELOPMENT` 階段複製到 `LIVE` 階段。

如果沒有與函數相關聯的快取行為，發佈該函數可讓您將其與快取行為產生關聯。您只能將快取行為與 `LIVE` 階段中的函數產生關聯。

**重要**  
建議您在發佈之前先[測試函數](test-function.md)。
發佈函數後，與該函數相關聯的所有快取行為會在分佈完成部署後立即開始使用新發佈的副本。

您可以在 CloudFront 主控台中或使用 AWS CLI 發佈函數。

------
#### [ Console ]

**發佈函數**

1. 在 [https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions) 登入 CloudFront 主控台，並選擇**函數**頁面。

1. 選擇要更新的函數。

1. 選擇**發佈**索引標籤，然後選擇**發佈**。如果您的函數已附加至一或多個快取行為，請選擇**發佈並更新**。

1. (選用) 若要檢視與函數相關聯的分佈，請選擇**關聯的 CloudFront 分佈**以展開該區段。

成功後，您會在頁面頂端看到一個橫幅，顯示**已成功發佈*函數名稱***。您也可以選擇**建置**索引標籤，然後選擇**即時**以檢視函數程式碼的即時版本。

------
#### [ CLI ]

**發佈函數**

1. 開啟命令列視窗。

1. 執行下列 **aws cloudfront publish-function** 命令。在此範例中，提供分行符號以使範例更具可讀性。

   ```
   aws cloudfront publish-function \
       --name MaxAge \
       --if-match ETVXYZEXAMPLE
   ```

   如果命令成功執行，您會看到如下所示的輸出。

   ```
   FunctionSummary:
     FunctionConfig:
       Comment: Max Age 2 years
       Runtime: cloudfront-js-2.0
     FunctionMetadata:
       CreatedTime: '2021-04-18T21:24:21.314000+00:00'
       FunctionARN: arn:aws:cloudfront::111122223333:function/ExampleFunction
       LastModifiedTime: '2023-12-19T23:41:15.389000+00:00'
       Stage: LIVE
     Name: MaxAge
     Status: UNASSOCIATED
   ```

------

# 將函數與分佈相關聯
<a name="associate-function"></a>

若要將函數與分佈搭配使用，請將該函數與分佈中的一或多個快取行為建立關聯。您可以將函數與多個分佈中的多個快取行為相關聯。

您可以將函數與下列任何行為建立關聯：
+ 現有的快取行為
+ 現有分佈中新的新快取行為
+ 新分佈中的新快取行為

將函數與快取行為建立關聯時，您必須選擇*事件類型*。事件類型決定 CloudFront 何時執行該函數。

您可以選擇下列事件類型：
+ **檢視者請求** – 當 CloudFront 收到來自檢視者的請求時，該函數會執行。
+ **檢視者回應** – 此函數會在 CloudFront 傳回檢視者回應之前執行。

您不能將面向原始伺服器的事件類型 (*原始伺服器請求*和*原始伺服器回應*) 與 CloudFront Functions 搭配使用。您可以改為使用 Lambda@Edge。如需更多詳細資訊，請參閱 [可以觸發 Lambda@Edge 函數的 CloudFront 事件](lambda-cloudfront-trigger-events.md)。

**注意**  
在關聯函數之前，您必須將[其發佈](publish-function.md)至 `LIVE` 階段。

您可以將函數與 CloudFront 主控台中的分佈建立關聯，或與 AWS Command Line Interface (AWS CLI) 建立關聯。下列程序顯示如何將函數與現有的快取行為建立關聯。

------
#### [ Console ]

**將函數與現有快取行為產生關聯**

1. 在 [https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions) 登入 CloudFront 主控台，並選擇**函數**頁面。

1. 選擇您要關聯的函數。

1. 在**函數**頁面上，選擇**發佈**索引標籤。

1. 選擇**發佈函數**。

1. 選擇 **Add association (建立關聯)**。在出現的對話方塊中，選擇分佈、事件類型及/或快取行為。

   在事件類型中，選擇您希望此函數執行的時間：
   + **檢視器請求** – 在 CloudFront 每次收到請求時執行該函數。
   + **檢視器回應** – 在 CloudFront 每次傳回回應時執行該函數。

1. 若要儲存組態，請選擇**新增關聯**。

CloudFront 會將分佈與函數建立關聯。等待幾分鐘，讓關聯的分佈完成部署。您可以在函數詳細資訊頁面上選擇**檢視分佈**來檢查進度。

------
#### [ CLI ]

**將函數與現有快取行為產生關聯**

1. 開啟命令列視窗。

1. 使用下列命令儲存分佈的組態，該分佈的快取行為將與函數產生關聯。此命令會將分佈組態儲存到名為 `dist-config.yaml` 的檔案中。若要使用此命令，請執行下列動作：
   + 將 *`DistributionID`* 取代為分佈的 ID。
   + 在一行上執行命令。在此範例中，提供分行符號以使範例更具可讀性。

   ```
   aws cloudfront get-distribution-config \
       --id DistributionID \
       --output yaml > dist-config.yaml
   ```

   命令成功時，AWS CLI 不會返回任何輸出。

1. 開啟您建立且命名為 `dist-config.yaml` 的檔案。編輯檔案以進行下列變更。

   1. 將 `ETag` 欄位重新命名為 `IfMatch`，但不要變更欄位的值。

   1. 在快取行為中，尋找名為 `FunctionAssociations` 的物件。更新此物件以新增函數關聯。如下的範例給出函數關聯的 YAML 語法。
      + 下列範例顯示檢視者請求事件物件 (觸發條件)。若要使用檢視者回應事件類型，請將 `viewer-request` 取代為 `viewer-response`。
      + 將 *`arn:aws:cloudfront::111122223333:function/ExampleFunction`* 取代為與此快取行為相關聯之函數的 Amazon Resource Name (ARN)。要獲取函數 ARN，您可以使用 **aws cloudfront list-functions** 命令。

      ```
      FunctionAssociations:
        Items:
          - EventType: viewer-request
            FunctionARN: arn:aws:cloudfront::111122223333:function/ExampleFunction
        Quantity: 1
      ```

   1. 進行這些變更後，請儲存檔案。

1. 使用以下命令更新分佈，同時新增函數關聯。若要使用此命令，請執行下列動作：
   + 將 *`DistributionID`* 取代為分佈的 ID。
   + 在一行上執行命令。在此範例中，提供分行符號以使範例更具可讀性。

   ```
   aws cloudfront update-distribution \
       --id DistributionID \
       --cli-input-yaml file://dist-config.yaml
   ```

   如果命令成功執行，您會看到如下所示的輸出，其中描述剛使用函數關聯更新的分佈。為便於閱讀，對如下的範例輸出進行了截斷。

   ```
   Distribution:
     ARN: arn:aws:cloudfront::111122223333:distribution/EBEDLT3BGRBBW
     ... truncated ...
     DistributionConfig:
       ... truncated ...
       DefaultCacheBehavior:
         ... truncated ...
         FunctionAssociations:
           Items:
           - EventType: viewer-request
             FunctionARN: arn:aws:cloudfront::111122223333:function/ExampleFunction
           Quantity: 1
         ... truncated ...
     DomainName: d111111abcdef8.cloudfront.net
     Id: EDFDVBD6EXAMPLE
     LastModifiedTime: '2021-04-19T22:39:09.158000+00:00'
     Status: InProgress
   ETag: E2VJGGQEG1JT8S
   ```

------

重新部署分佈時，此分佈的 `Status` 變更為 `InProgress`。一旦新的分佈組態到達 CloudFront 邊緣節點，該邊緣節點就會開始使用關聯的函數。當分佈完全部署時，`Status` 會變更回 `Deployed`。這表示相關聯的 CloudFront 函數已在全球所有 CloudFront 邊緣節點中上線。通常這需要幾分鐘的時間。

# Amazon CloudFront KeyValueStore
<a name="kvs-with-functions"></a>

CloudFront KeyValueStore 是安全、全域、低延遲的鍵值資料儲存，可從 [CloudFront Functions](cloudfront-functions.md) 內進行讀取存取，從而在 CloudFront 邊緣節點啟用進階可自訂邏輯。

使用 CloudFront KeyValueStore，您可以更新函數程式碼，並且彼此獨立地更新與函數相關聯的資料。這種分離簡化了函數程式碼，並且可以輕鬆更新資料，而無需部署程式碼變更。

**注意**  
若要使用 CloudFront KeyValueStore，您的 CloudFront 函數必須使用 [JavaScript 執行時期 2.0](functions-javascript-runtime-20.md)。

使用鍵值對的一般程序如下：
+ 建立鍵值存放區，並填入一組鍵值對。您可以將鍵值存放區新增至 Amazon S3 儲存貯體，或手動輸入。
+ 將鍵值存放區與您的 CloudFront 函數建立關聯。
+ 在函數程式碼中，使用鍵的名稱來擷取與鍵關聯的值或評估鍵是否存在。如需有關在函數程式碼中使用鍵值對的詳細資訊，以及協助程式方法的相關資訊，請參閱 [鍵值存放區的協助程式方法](functions-custom-methods.md)。

## 使用案例
<a name="key-value-store-use-cases"></a>

您可以針對下列範例使用鍵值對：
+ **URL 重寫或重新導向**：鍵值對可以保留重寫 URL 或重新導向 URL。
+ **A/B 測試及功能旗標**：您可指派一定百分比的流量至特定版本網站，即可建立執行實驗的函數。
+ **存取授權**：您可實作存取控制，根據您定義的條件和鍵值存放區中儲存的資料來允許或拒絕請求。

## 支援的值格式
<a name="key-value-store-supported-formats"></a>

您可採用下列任何一種格式儲存鍵值對中的值：
+ String
+ 位元組編碼字串
+ JSON 

## 安全
<a name="key-value-store-security"></a>

CloudFront 函數及其所有鍵值存放區資料均可安全地處理，如下所示：
+ 您呼叫 [CloudFront KeyValueStore](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_Operations_Amazon_CloudFront_KeyValueStore.html) API 操作時，CloudFront 會在靜態和傳輸期間 (讀取或寫入鍵值存放區時) 將每個鍵值存放區加密。
+ 執行函數時，CloudFront 會解密 CloudFront 邊緣節點記憶體中的每個鍵值對。

若要開始使用 CloudFront KeyValueStore，請參閱下列主題。

**Topics**
+ [使用案例](#key-value-store-use-cases)
+ [支援的值格式](#key-value-store-supported-formats)
+ [安全](#key-value-store-security)
+ [使用鍵值存放區](kvs-with-functions-kvs.md)
+ [使用鍵值資料](kvs-with-functions-kvp.md)
+ 如需 CloudFront KeyValueStore 入門的詳細資訊，請參閱[簡介 Amazon CloudFront KeyValueStore](https://aws.amazon.com/blogs/aws/introducing-amazon-cloudfront-keyvaluestore-a-low-latency-datastore-for-cloudfront-functions/) AWS 部落格文章。

# 使用鍵值存放區
<a name="kvs-with-functions-kvs"></a>

您必須建立鍵值存放區，以保留要在 CloudFront Functions 中使用的鍵值對。

建立鍵值存放區並新增鍵值對之後，您可以在 CloudFront 函數程式碼中使用這些鍵值。

若要開始使用，請參閱下列主題：

**Topics**
+ [建立鍵值存放區](kvs-with-functions-create.md)
+ [將鍵值存放區與函數建立關聯。](kvs-with-functions-associate.md)
+ [更新鍵值存放區](kvs-with-functions-edit.md)
+ [獲取鍵值存放區參考資料](kvs-with-functions-get-reference.md)
+ [刪除鍵值存放區](kvs-with-functions-delete.md)
+ [鍵值對的檔案格式](kvs-with-functions-create-s3-kvp.md)

**注意**  
JavaScript 執行時期 2.0 包含一些協助程式方法，可用來處理函數程式碼中的鍵值。如需詳細資訊，請參閱[鍵值存放區的協助程式方法](functions-custom-methods.md)。

# 建立鍵值存放區
<a name="kvs-with-functions-create"></a>



您可以同時建立一個鍵值存放區及其鍵值對。您也可以建立一個空的鍵值存放區，並於稍後在其中新增鍵值對。

**注意**  
如果您從 Amazon S3 儲存貯體指定資料來源，就必須擁有該儲存貯體的 `s3:GetObject` 和 `s3:GetBucketLocation` 許可權限。如果您沒有這些許可權限，CloudFront 就無法成功建立您的鍵值存放區。

請決定是否要在建立鍵值存放區的同時新增鍵值對。您可以使用 CloudFront 主控台、CloudFront API 或 AWS SDKs匯入金鑰/值對。不過您只能在*最初*建立鍵值存放區時匯入鍵值對檔案。

若要建立鍵值對檔案，請參閱 [鍵值對的檔案格式](kvs-with-functions-create-s3-kvp.md)。

------
#### [ Console ]

**建立鍵值存放區**

1. 登入 ， AWS 管理主控台 並在位於 的 CloudFront 主控台中開啟**函數**頁面[https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions)。

1. 選擇 **KeyValueStores** 索引標籤，然後選擇**建立 KeyValueStore**。

1. 輸入鍵值存放區的名稱，以及選擇性的描述。

1. 完成 **S3 URI**：
   + 如果您有鍵值對檔案，請輸入您存放該檔案的 Amazon S3 儲存貯體路徑。
   + 如果您打算手動輸入鍵值對，請將此欄位保留空白。

1. 選擇**建立**。鍵值存放區現已建立。

   此時會顯示新鍵值存放區的詳細資訊頁面。頁面上的資訊包括鍵值存放區的 ID 和 ARN。
   + ID 是在您 中唯一的隨機字元字串 AWS 帳戶。
   + ARN 具有以下語法：

     *AWS 帳戶*`:key-value-store/`* 鍵值存放區 ID*

1. 請查看**鍵值對**區段。如果您匯入檔案，此區段會顯示一些鍵值對。您可以執行下列動作：
   + 如果您匯入了檔案，也可以手動新增更多值。
   + 如果您沒有從 Amazon S3 儲存貯體匯入檔案，而且現在想要新增鍵值對，則可以完成下個步驟。
   + 您可以略過此步驟，稍後再新增鍵值對。

1. 立即新增鍵值對：

   1. 選擇**新增鍵值對**。

   1. 選擇**新增配對**，然後輸入名稱和值。若要新增更多配對，請重複此步驟。

   1. 完成後，選擇**儲存變更**以儲存鍵值存放區中的所有鍵值對。在出現的對話方塊中選擇**完成**。

1. 如果您要立即將鍵值存放區與函數相關聯，請完成**關聯函數**區段。如需詳細資訊，請參閱 [建立函數](create-function.md) 或 [更新函數](update-function.md) 。

   您也可以稍後從此鍵值存放區詳細資訊頁面或從函數詳細資訊頁面與函數建立關聯。

------
#### [ AWS CLI ]

**建立鍵值存放區**
+ 您可執行下列命令來建立鍵值存放區，並從 Amazon S3 儲存貯體匯入鍵值對。

  ```
  aws cloudfront create-key-value-store \
      --name=keyvaluestore1 \
      --comment="This is my key value store file" \
      --import-source=SourceType=S3,SourceARN=arn:aws:s3:::amzn-s3-demo-bucket1/kvs-input.json
  ```

  **回應**

  ```
  {
      "ETag": "ETVABCEXAMPLE",
      "Location": "https://cloudfront.amazonaws.com/2020-05-31/key-value-store/arn:aws:cloudfront::123456789012:key-value-store/8aa76c93-3198-462c-aaf6-example",
      "KeyValueStore": {
          "Name": "keyvaluestore1",
          "Id": "8aa76c93-3198-462c-aaf6-example",
          "Comment": "This is my key value store file",
          "ARN": "arn:aws:cloudfront::123456789012:key-value-store/8aa76c93-3198-462c-aaf6-example",
          "Status": "PROVISIONING",
          "LastModifiedTime": "2024-08-06T22:19:10.813000+00:00"
      }
  }
  ```

------
#### [ API ]

**建立鍵值存放區**

1. 使用 [CloudFront CreateKeyValueStore](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateKeyValueStore.html) 操作。該操作需要幾個參數：
   + 鍵值存放區的 `name`。
   + 包含註解的 `comment` 參數。
   + 可讓您從存放在 Amazon S3 儲存貯體的檔案匯入鍵值對的 `import-source` 參數。您只能在第一次建立鍵值存放區時從檔案匯入。如需檔案結構的相關資訊，請參閱 [鍵值對的檔案格式](kvs-with-functions-create-s3-kvp.md)。

操作回應包含下列資訊：
+ 請求中傳遞的值，包括您指派的名稱。
+ 建立時間等資料。
+ `ETag` (例如 `ETVABCEXAMPLE`) 為包含鍵值存放區名稱的 ARN (例如 `arn:aws:cloudfront::123456789012:key-value-store/keyvaluestore1`)。

  您將使用 `ETag`、ARN 和名稱的某些組合，以程式設計方式處理鍵值存放區。

------

## 鍵值存放區狀態
<a name="key-value-store-status"></a>

您建立鍵值存放區時，資料存放區可能有下列狀態值。


****  

| Value | Description | 
| --- | --- | 
|  **佈建**  |  鍵值存放區已建立，CloudFront 正在處理您指定的資料來源。  | 
|  **備妥**  |  鍵值存放區已建立，CloudFront 已成功處理您指定的資料來源。  | 
|  **匯入失敗**  |  CloudFront 無法處理您指定的資料來源。如果您的檔案格式無效或超過大小限制，就會出現此狀態。如需詳細資訊，請參閱[鍵值對的檔案格式](kvs-with-functions-create-s3-kvp.md)。  | 

# 將鍵值存放區與函數建立關聯。
<a name="kvs-with-functions-associate"></a>

您建立鍵值存放區之後，就可以更新函數，將其與鍵值存放區建立關聯。您必須建立此關聯，才能在該函數中使用該存放區中的鍵值對。適用的規定如下：
+ 一個函數只能有一個鍵值存放區
+ 同一個鍵值存放區可以與多個函數建立關聯

------
#### [ Console ]

**將鍵值存放區與函數建立關聯**

1. 在 [https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions) 登入 CloudFront 主控台，並選擇**函數**頁面。

1. 選擇函數名稱。

1. 前往**關聯 KeyValueStore** 區段，然後選擇**關聯現有的 KeyValueStore**。

1. 選取函數中包含鍵值對的鍵值存放區，然後選擇**關聯 KeyValueStore**。

   CloudFront 會立即將存放區與該函數建立關聯。您無需儲存函數。

1. 若要指定不同的鍵值存放區，請選擇**更新關聯的 KeyValueStore**，選擇另一個鍵值存放區名稱，然後選擇**關聯 KeyValueStore**。

如需詳細資訊，請參閱[更新函數](update-function.md)。

------
#### [ AWS CLI ]

**將鍵值存放區與函數建立關聯**
+ 您可執行下列命令來更新 `MaxAge` 函數，並與鍵值存放區資源建立關聯。

  ```
  aws cloudfront update-function \
      --name MaxAge \
      --function-config '{"Comment":"Max Age 2 years","Runtime":"cloudfront-js-2.0","KeyValueStoreAssociations":{"Quantity":1,"Items":[{"KeyValueStoreARN":"arn:aws:cloudfront::123456789012:key-value-store/8aa76c93-3198-462c-aaf6-example"}]}}' \
      --function-code fileb://function-max-age-v1.js \
      --if-match ETVABCEXAMPLE
  ```
+ 若要將鍵值存放區與函數建立關聯，請指定 `KeyValueStoreAssociations` 參數和鍵值存放區 ARN。
+ 若要變更關聯，請指定另一個鍵值存放區 ARN。
+ 若要移除關聯，請移除 `KeyValueStoreAssociations` 參數。

如需詳細資訊，請參閱[更新函數](update-function.md)。

------
#### [ API ]

**將鍵值存放區與函數建立關聯**
+ 使用 [UpdateFunction](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateFunction.html) API 操作。如需詳細資訊，請參閱[更新函數](update-function.md)。

------

**備註**  
如果您修改鍵值存放區而不變更鍵值對，或者只修改鍵值存放區中的鍵值對，則不需要再次關聯鍵值存放區。您也不需要重新發佈函數。  
不過我們建議您測試函數，以確認函數是否如預期般運作。如需詳細資訊，請參閱[測試函數](test-function.md)。
您可以檢視使用特定鍵值存放區的所有函數。在 CloudFront 主控台上，選擇鍵值存放區詳細資訊頁面。

# 更新鍵值存放區
<a name="kvs-with-functions-edit"></a>

您更新鍵值存放區時可以變更鍵值對，或變更鍵值存放區和函數之間的關聯。

------
#### [ Console ]

**更新鍵值存放區**

1. 登入 ， AWS 管理主控台 並在位於 的 CloudFront 主控台中開啟**函數**頁面[https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions)。

1. 選擇 **KeyValueStores** 索引標籤。

1.  選擇您想要更新的鍵值存放區。
   + 若要更新鍵值對，請選擇**鍵值對**區段中的**編輯**。您可以新增或刪除任何鍵值對。您也可以變更現有鍵值對的值。完成時，請選擇**儲存變更**。
   + 若要更新此鍵值存放區的關聯，請選擇**前往函數**。如需詳細資訊，請參閱[將鍵值存放區與函數建立關聯。](kvs-with-functions-associate.md)。

------
#### [ AWS CLI ]

**更新鍵值存放區**

1. **變更鍵值對**：您可以新增更多鍵值對、刪除一或多個鍵值對，也可以變更現有鍵值對的值。如需詳細資訊，請參閱[使用鍵值資料](kvs-with-functions-kvp.md)。

1. **變更鍵值存放區的函數關聯**：若要更新鍵值存放區的關聯函數，請參閱 [將鍵值存放區與函數建立關聯。](kvs-with-functions-associate.md)。
**提示**  
您將需要鍵值存放區的 ARN。如需詳細資訊，請參閱[獲取鍵值存放區參考資料](kvs-with-functions-get-reference.md)。

------
#### [ API ]

**更新鍵值存放區**

1. **變更鍵值對**：您可以新增更多鍵值對、刪除一或多個鍵值對，也可以變更現有鍵值對的值。如需詳細資訊，請參閱[使用鍵值資料](kvs-with-functions-kvp.md)。

1. **變更鍵值存放區的函數關聯**：若要更新鍵值存放區的關聯函數，請使用 [UpdateFunction](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateFunction.html) API 操作。如需詳細資訊，請參閱[更新函數](update-function.md)。
**提示**  
您將需要鍵值存放區的 ARN。如需詳細資訊，請參閱[獲取鍵值存放區參考資料](kvs-with-functions-get-reference.md)。

------

# 獲取鍵值存放區參考資料
<a name="kvs-with-functions-get-reference"></a>

為了以程式設計方式使用鍵值存放區，您需要 `ETag` 和鍵值存放區的名稱。

若要取得這兩個值，您可以使用 AWS Command Line Interface (AWS CLI) 或 CloudFront API。

------
#### [ AWS CLI ]

**取得鍵值存放區參考資料**

1. 若要傳回鍵值存放區清單，請執行下列命令。尋找您要變更的鍵值存放區名稱。

   ```
   aws cloudfront list-key-value-stores
   ```

1. 從回應中尋找您想要的鍵值存放區名稱。

   **回應**

   ```
   {
       "KeyValueStoreList": {
           "Items": [
               {
                   "Name": "keyvaluestore3",
                   "Id": "37435e19-c205-4271-9e5c-example3",
                   "ARN": "arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example3",
                   "Status": "READY",
                   "LastModifiedTime": "2024-05-08T14:50:18.876000+00:00"
               },
               {
                   "Name": "keyvaluestore2",
                   "Id": "47970d59-6408-474d-b850-example2",
                   "ARN": "arn:aws:cloudfront::123456789012:key-value-store/47970d59-6408-474d-b850-example2",
                   "Status": "READY",
                   "LastModifiedTime": "2024-05-30T21:06:22.113000+00:00"
               },
               {
                   "Name": "keyvaluestore1",
                   "Id": "8aa76c93-3198-462c-aaf6-example",
                   "ARN": "arn:aws:cloudfront::123456789012:key-value-store/8aa76c93-3198-462c-aaf6-example",
                   "Status": "READY",
                   "LastModifiedTime": "2024-08-06T22:19:30.510000+00:00"
               }
           ]
       }
   }
   ```

1. 執行下列命令，以傳回指定鍵值存放區的 `ETag`。

   ```
   aws cloudfront describe-key-value-store \
       --name=keyvaluestore1
   ```

   **回應**

   ```
   {
       "ETag": "E3UN6WX5RRO2AG",
       "KeyValueStore": {
           "Name": "keyvaluestore1",
           "Id": "8aa76c93-3198-462c-aaf6-example",
           "Comment": "This is an example KVS",
           "ARN": "arn:aws:cloudfront::123456789012:key-value-store/8aa76c93-3198-462c-aaf6-example",
           "Status": "READY",
           "LastModifiedTime": "2024-08-06T22:19:30.510000+00:00"
       }
   }
   ```

------
#### [ API ]

**取得鍵值存放區參考資料**

1. 使用 [https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_ListKeyValueStores.html](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_ListKeyValueStores.html) API 操作傳回鍵值存放區清單。尋找您想要變更的鍵值存放區名稱。

1. 使用 [https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_DescribeKeyValueStore.html](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_DescribeKeyValueStore.html) API 操作，並指定您從上一個步驟傳回的鍵值存放區名稱。

------

回應包括 UUID、鍵值存放區的 ARN 以及鍵值存放區的 `ETag`。
+ `ETag` (例如 `E3UN6WX5RRO2AG`)
+ UUID 為 128 位元 (例如 `8aa76c93-3198-462c-aaf6-example`)
+ ARN 包含 AWS 帳戶 數字、常數 `key-value-store`和 UUID，如下列範例所示：

  `arn:aws:cloudfront::123456789012:key-value-store/8aa76c93-3198-462c-aaf6-example`

如需 `DescribeKeyValueStore` 操作的詳細資訊，請參閱 [關於 CloudFront KeyValueStore](kvs-with-functions-kvp.md#kvs-with-functions-api-describe)。

# 刪除鍵值存放區
<a name="kvs-with-functions-delete"></a>

您可以使用 Amazon CloudFront 主控台或 API 來刪除鍵值存放區。

------
#### [ Console ]

**刪除鍵值存放區**

1. 登入 ， AWS 管理主控台 並在位於 的 CloudFront 主控台中開啟**函數**頁面[https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions)。

1. 選擇函數名稱。

1. 在**關聯的 KeyValueStore** 區段下，驗證鍵值存放區是否與函數相關聯。如果是，請選擇**取消關聯 KeyValueStore** 以移除關聯，然後選擇**移除關聯**。

1. 在導覽面板中選擇**函數**頁面，然後選擇 **KeyValueStores** 索引標籤。

1. 選取您要刪除的鍵值存放區，然後選擇**刪除**。

------
#### [ AWS CLI ]

**刪除鍵值存放區**

1. 獲取 `ETag` 和鍵值存放區的名稱。如需詳細資訊，請參閱[獲取鍵值存放區參考資料](kvs-with-functions-get-reference.md)。

1. 驗證鍵值存放區是否與函數相關聯。如果是，將移除關聯。如需這些步驟的詳細資訊，請參閱 [更新函數](update-function.md)。

1. 如果您已取得鍵值存放區的名稱和 `ETag`，且鍵值存放區不再與函數相關聯，就可以將其刪除。

   請執行下列命令來刪除指定的鍵值存放區。

   ```
   aws cloudfront delete-key-value-store \
       --name=keyvaluestore1 \
       --if-match=E3UN6WX5RRO2AG
   ```

------
#### [ API ]

**刪除鍵值存放區**

1. 獲取 `ETag` 和鍵值存放區的名稱。如需詳細資訊，請參閱[獲取鍵值存放區參考資料](kvs-with-functions-get-reference.md)。

1. 驗證鍵值存放區是否與函數相關聯。如果是，將移除關聯。如需這些步驟的詳細資訊，請參閱 [更新函數](update-function.md)。

1. 若要刪除鍵值存放區，請使用 CloudFront [https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_DeleteKeyValueStore.html](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_DeleteKeyValueStore.html) API 操作。

------

# 鍵值對的檔案格式
<a name="kvs-with-functions-create-s3-kvp"></a>

建立 UTF-8 編碼的檔案時，請使用下列 JSON 格式：

```
{
  "data":[
    {
      "key":"key1",
      "value":"value"
    },
    {
      "key":"key2",
      "value":"value"
    }
  ]
}
```

您的檔案不能包含重複的鍵。如果您在 Amazon S3 儲存貯體中指定了無效檔案，您可以更新檔案以移除任何重複項目，然後再次嘗試建立您的鍵值存放區。

如需詳細資訊，請參閱[建立鍵值存放區](kvs-with-functions-create.md)。

**注意**  
資料來源及其鍵值組的檔案具有以下限制：  
檔案大小 - 5 MB
鍵大小 — 512 個字元
鍵大小 — 1024 個字元

# 使用鍵值資料
<a name="kvs-with-functions-kvp"></a>

本節說明如何將鍵值對新增至現有的鍵值存放區。若要在最初建立鍵值存放區時包含鍵值對，請參閱 [建立鍵值存放區](kvs-with-functions-create.md)。

**Topics**
+ [使用鍵值對 (主控台)](#kvs-with-functions-kvp-using-console)
+ [關於 CloudFront KeyValueStore](#kvs-with-functions-api-describe)
+ [使用鍵值對 (AWS CLI)](#work-with-kvs-cli-keys)
+ [使用鍵值對 (API)](#kvs-with-functions-kvp-using-api)

## 使用鍵值對 (主控台)
<a name="kvs-with-functions-kvp-using-console"></a>

您可使用 CloudFront 主控台處理鍵值對。

**使用鍵值對**

1. 登入 ， AWS 管理主控台 並在位於 的 CloudFront 主控台中開啟**函數**頁面[https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions)。

1. 選擇 **KeyValueStores** 索引標籤。

1. 選擇您想要變更的鍵值存放區。

1. 在**鍵值對**區段中選擇**編輯**。

1. 您可以新增鍵值對、刪除鍵值對，或變更現有鍵值對的值。

1. 完成時，請選擇**儲存變更**。

## 關於 CloudFront KeyValueStore
<a name="kvs-with-functions-api-describe"></a>

**提示**  
CloudFront KeyValueStore API 是使用 Signature 第 4A 版 (SigV4A) 進行身分驗證的全域服務。使用臨時憑證搭配 SigV4A 需要第 2 版工作階段記號。如需詳細資訊，請參閱[搭配 CloudFront KeyValueStore API 使用臨時憑證](cloudfront-function-restrictions.md#regional-endpoint-for-key-value-store)。

如果您使用 AWS Command Line Interface (AWS CLI) 或自己的程式碼來呼叫 CloudFront KeyValueStore API，請參閱下列各節。

您使用鍵值存放區及其鍵值對時，您呼叫的服務取決於您的使用案例：
+ 若要使用*現有*鍵值存放區的鍵值對，請使用 CloudFront KeyValueStore 服務。
+ 若要在*最初*建立鍵值存放區時在其中包含一些鍵值對，請使用 CloudFront 服務。

CloudFront API 和 CloudFront KeyValueStore API 都具有 `DescribeKeyValueStore` 操作。您會依據不同原因呼叫這兩項服務。若要瞭解其中差異，請參閱下表。


|  | CloudFront DescribeKeyValueStore API | CloudFront KeyValueStore DescribeKeyValueStore API | 
| --- | --- | --- | 
| 關於鍵值存放區的資料 |  傳回資料，例如狀態和上次修改鍵值存放區的日期。  |  傳回有關儲存資源*內容*的資料：存放區中的鍵值對，以及內容的大小。  | 
| 識別鍵值存放區的資料 |  傳回鍵值存放區的 `ETag`、UUID 和 ARN。  |  傳回鍵值存放區的 `ETag` 和 ARN。  | 

**備註**  
每個 DescribeKeyValueStore 操作會傳回*不同的* `ETag`。`ETags` 不可互換。
您呼叫 API 操作以完成動作時，必須從適當的 API 指定 `ETag`。例如在 CloudFront KeyValueStore 的 [DeleteKey](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_DeleteKey.html) 操作中，您要指定從 CloudFront KeyValueStore [https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_DescribeKeyValueStore.html](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_DescribeKeyValueStore.html) 操作傳回的 `ETag`。
您使用 CloudFront KeyValueStore 調用 CloudFront Functions 時，鍵值存放區的值在調用函數期間不會更新或變更。更新會在函數調用之間進行處理。

## 使用鍵值對 (AWS CLI)
<a name="work-with-kvs-cli-keys"></a>

您可以針對 CloudFront KeyValueStore 執行下列 AWS Command Line Interface 命令。

**Contents**
+ [列出鍵值對](#kvs-cli-list-keys)
+ [取得鍵值對](#kvs-cli-get-keys)
+ [描述鍵值存放區](#kvs-cli-describe-keys)
+ [建立鍵值對](#kvs-cli-create-keys)
+ [刪除鍵值對](#kvs-cli-delete-keys)
+ [更新鍵值對](#kvs-cli-update-key)

### 列出鍵值對
<a name="kvs-cli-list-keys"></a>

若要列出鍵值存放區中的鍵值對，請執行下列命令。

```
aws cloudfront-keyvaluestore list-keys \
    --kvs-arn=arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example
```

**回應**

```
{
    "Items": [
        {
            "Key": "key1",
            "Value": "value1"
        }
    ]
}
```

### 取得鍵值對
<a name="kvs-cli-get-keys"></a>

若要取得鍵值存放區中的鍵值對，請執行下列命令。

```
aws cloudfront-keyvaluestore get-key \
    --key=key1 \
    --kvs-arn=arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example
```

**回應**

```
{
    "Key": "key1",
    "Value": "value1",
    "ItemCount": 1,
    "TotalSizeInBytes": 11
}
```

### 描述鍵值存放區
<a name="kvs-cli-describe-keys"></a>

若要描述鍵值存放區，請執行下列命令。

```
aws cloudfront-keyvaluestore describe-key-value-store \
    --kvs-arn=arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example
```

**回應**

```
{
    "ETag": "KV1F83G8C2ARO7P",
    "ItemCount": 1,
    "TotalSizeInBytes": 11,
    "KvsARN": "arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example",
    "Created": "2024-05-08T07:48:45.381000-07:00",
    "LastModified": "2024-08-05T13:50:58.843000-07:00",
    "Status": "READY"
}
```

### 建立鍵值對
<a name="kvs-cli-create-keys"></a>

若要在鍵值存放區建立鍵值對，請執行下列命令。

```
aws cloudfront-keyvaluestore put-key \
    --if-match=KV1PA6795UKMFR9 \
    --key=key2 \
    --value=value2 \
    --kvs-arn=arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example
```

**回應**

```
{
    "ETag": "KV13V1IB3VIYZZH",
    "ItemCount": 3,
    "TotalSizeInBytes": 31
}
```

### 刪除鍵值對
<a name="kvs-cli-delete-keys"></a>

若要刪除鍵值對，請執行下列命令。

```
aws cloudfront-keyvaluestore delete-key \
    --if-match=KV13V1IB3VIYZZH \
    --key=key1 \
    --kvs-arn=arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example
```

**輸出**

```
{
    "ETag": "KV1VC38T7YXB528",
    "ItemCount": 2,
    "TotalSizeInBytes": 22
}
```

### 更新鍵值對
<a name="kvs-cli-update-key"></a>

您可以使用 `update-keys` 命令來更新多個鍵值對。例如若要刪除現有鍵值對並建立另一個，請執行下列命令。

```
aws cloudfront-keyvaluestore update-keys \
    --if-match=KV2EUQ1WTGCTBG2 \
    --kvs-arn=arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example \
    --deletes '[{"Key":"key2"}]' \
    --puts '[{"Key":"key3","Value":"value3"}]'
```

**回應**

```
{
    "ETag": "KV3AEGXETSR30VB",
    "ItemCount": 3,
    "TotalSizeInBytes": 28
}
```

## 使用鍵值對 (API)
<a name="kvs-with-functions-kvp-using-api"></a>

您可遵循本節說明以程式設計方式使用鍵值對。

**Contents**
+ [獲取鍵值存放區參考資料](#kvs-with-functions-api-ref)
+ [變更鍵值存放區中的鍵值對](#kvs-with-functions-api-actions)
+ [CloudFront KeyValueStore 的範例程式碼](#example-code-key-value-store)

### 獲取鍵值存放區參考資料
<a name="kvs-with-functions-api-ref"></a>

您使用 CloudFront KeyValueStore API 呼叫寫入操作時，需要指定鍵值存放區的 ARN 和 `ETag`。若要取得此資料，請依下列步驟執行：

**取得鍵值存放區參考資料**

1. 使用 [https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_ListKeyValueStores.html](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_ListKeyValueStores.html) API 操作取得鍵值存放區清單。尋找您想要變更的鍵值存放區。

1. 使用 [CloudFrontKeyValueStore DescribeKeyValueStore API 操作](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_DescribeKeyValueStore.html)，並指定上一個步驟的鍵值存放區。

   回應包括鍵值存放區的 ARN 和 `ETag`。
   + ARN 包含 AWS 帳戶 數字、常數 `key-value-store`和 UUID，例如下列範例：

     `arn:aws:cloudfront::123456789012:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111`
   + `ETag` 如下列範例所示：

     `ETVABCEXAMPLE2`

### 變更鍵值存放區中的鍵值對
<a name="kvs-with-functions-api-actions"></a>

您可以指定包含所需更新鍵值對的鍵值存放區。

請參閱下列 CloudFront KeyValueStore API 操作：
+ [CloudFrontKeyValueStore DeleteKey](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_DeleteKey.html)：刪除鍵值對
+ [CloudFrontKeyValueStore GetKey](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_GetKey.html)：傳回鍵值對
+ [CloudFrontKeyValueStore ListKeys](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_ListKeys.html)：傳回鍵值對清單 
+ [CloudFrontKeyValueStore PutKey](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_PutKey.html)：您可以執行下列任務：
  + 指定新的鍵名稱及值，以在鍵值存放區中建立新的鍵值對。
  + 指定現有鍵名稱及新的鍵值，以在現有鍵值對中設定不同的值。
+ [CloudFrontKeyValueStore UpdateKeys](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_UpdateKeys.html)：您可以在一個「全有或全無」操作中執行下列其中一個或多個動作：
  + 刪除一或多個鍵值對
  + 建立一或多個新的鍵值對
  + 在一個或多個現有鍵值對中設定不同的值

### CloudFront KeyValueStore 的範例程式碼
<a name="example-code-key-value-store"></a>

**Example**  
下列程式碼示範如何呼叫鍵值存放區的 `DescribeKeyValueStore` API 操作。  

```
const {
  CloudFrontKeyValueStoreClient,
  DescribeKeyValueStoreCommand,
} = require("@aws-sdk/client-cloudfront-keyvaluestore");

require("@aws-sdk/signature-v4-crt");

(async () => {
  try {
    const client = new CloudFrontKeyValueStoreClient({
      region: "us-east-1"
    });
    const input = {
      KvsARN: "arn:aws:cloudfront::123456789012:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111",
    };
    const command = new DescribeKeyValueStoreCommand(input);

    const response = await client.send(command);
  } catch (e) {
    console.log(e);
  }
})();
```

# 使用 CloudFront Connection Functions 自訂
<a name="customize-connections-validation-with-connection-functions"></a>

CloudFront Connection Functions 可讓您為 mTLS 憑證驗證和自訂身分驗證邏輯撰寫輕量型 JavaScript 函數。您的連線函數會在建立 mTLS 連線期間執行，以驗證用戶端憑證、實作裝置特定的身分驗證規則，以及處理憑證撤銷案例。Connection Functions 執行時間環境提供低於毫秒的啟動時間、立即擴展以處理每秒數百萬個連線，而且非常安全。Connection Functions 是 CloudFront 的原生功能，這表示您可以完全在 CloudFront 內建置、測試和部署程式碼。

當您將連線函數與啟用 mTLS 的 CloudFront 分佈建立關聯時，CloudFront 會在 CloudFront 節點攔截 TLS 連線請求，並將憑證資訊傳遞給您的函數。當發生下列事件時，您可以叫用連線函數：
+ 在建立 TLS 連線 （連線請求） 期間 - 用於交互 TLS (mTLS) 連線

如需連線函數的詳細資訊，請參閱下列主題。

**Topics**
+ [概觀和工作流程](connection-functions-overview.md)
+ [組態和限制](connection-function-configuration-limits.md)
+ [建立 CloudFront 連線函數以進行交互 TLS （檢視器） 驗證](create-connection-functions.md)
+ [寫入 CloudFront 連線函數程式碼以進行交互 TLS （檢視器） 驗證](write-connection-function-code.md)
+ [在部署之前測試 CloudFront 連線函數](test-connection-functions.md)
+ [將連線函數與分佈建立關聯](associate-connection-functions.md)
+ [使用 CloudFront Functions 和 KeyValueStore 實作交互 TLS （檢視器） 的憑證撤銷](implement-certificate-revocation.md)

# 概觀和工作流程
<a name="connection-functions-overview"></a>

CloudFront Connection Functions 是一種特殊類型的 CloudFront Functions，當用戶端嘗試建立 mTLS 連線時，會在 TLS 交握期間執行。您的 Connection Function 可以存取用戶端憑證資訊、mTLS 組態參數、憑證撤銷檢查結果，以及用戶端 IP 地址。

在 CloudFront 執行標準憑證驗證 （信任鏈、過期、簽章驗證） 後叫用連線函數，但即使憑證撤銷檢查失敗，也可以執行。這可讓您實作自訂邏輯，以處理已撤銷的憑證或新增其他驗證條件。

建立並發佈 Connection Function 之後，請務必為啟用 mTLS 的分佈新增連線請求事件類型的關聯。這可讓函數在每次用戶端嘗試與 CloudFront 建立 mTLS 連線時執行。

CloudFront Connection Functions 遵循兩階段生命週期，可讓您在將函數部署到生產環境之前開發和測試函數。此工作流程可確保連線函數在影響即時流量之前正常運作。

**Topics**
+ [函數階段](#connection-function-stages)
+ [開發工作流程](#connection-function-development-workflow)
+ [與其他函數類型的差異](#connection-function-differences)

## 函數階段
<a name="connection-function-stages"></a>

連線函數存在於兩個階段之一：
+ **開發** – 可修改、測試和更新此階段中的函數。使用此階段來寫入和偵錯函數程式碼。
+ **LIVE** – 此階段中的函數為唯讀並處理生產流量。您無法直接修改 LIVE 階段中的函數。

當您建立新的連線函數時，它會在 **DEVELOPMENT** 階段啟動。測試和驗證之後，您會發佈函數以將其移至 **LIVE** 階段。

## 開發工作流程
<a name="connection-function-development-workflow"></a>

遵循此工作流程來開發和部署 Connection Functions：

1. **建立** – 在 DEVELOPMENT 階段使用初始程式碼和組態建立新的連線函數。

1. **測試** – 使用測試功能，在部署之前使用範例連線事件來驗證您的函數。

1. **更新** – 根據測試結果，視需要修改函數程式碼和組態。

1. **發佈** – 準備進行生產時，發佈函數以將其從 DEVELOPMENT 移至 LIVE 階段。

1. **關聯** – 將已發佈的函數與已啟用 mTLS 的分佈建立關聯，以處理即時連線。

若要變更 LIVE 函數，您必須更新 DEVELOPMENT 版本並重新發佈。這會在 LIVE 階段中建立新的版本。

## 與其他函數類型的差異
<a name="connection-function-differences"></a>

連線函數與檢視器請求和檢視器回應函數在幾個重要方面不同：
+ 在進行任何 HTTP 處理之前，連線函數會在 mTLS 交握之後執行
+ 連線函數可存取 TLS 憑證資訊，而不是 HTTP 請求/回應資料
+ 連線函數只能允許或拒絕連線，不能修改 HTTP 資料
+ 連線函數只會針對新的 TLS 連線叫用，而不是針對連線重複使用叫用
+ mTLS 不支援 TLS 工作階段恢復，以確保憑證驗證在每個連線上發生
+ 除了標準檢視器請求和檢視器回應函數之外，連線函數也會執行
+ 您可以在分佈層級建立 Connection Functions 的關聯，而不是在快取行為層級建立關聯。
+ 連線函數僅支援 JavaScript 執行期 2.0。

# 組態和限制
<a name="connection-function-configuration-limits"></a>

CloudFront Connection Functions 具有特定的組態需求和服務限制，因為其在 TLS 連線驗證和邊緣運算效能方面的專業角色。

**Topics**
+ [函數程式碼需求](#connection-function-code-requirements)
+ [服務限制](#connection-function-service-limits)
+ [函數篩選選項](#connection-function-filtering-options)

## 函數程式碼需求
<a name="connection-function-code-requirements"></a>

連線函數需要處理 TLS 連線事件的 JavaScript 程式碼。函數程式碼必須：
+ 以 JavaScript 撰寫
+ 處理連線事件並做出允許/拒絕決策
+ 在時間限制內完成執行
+ 處理憑證和連線驗證邏輯

## 服務限制
<a name="connection-function-service-limits"></a>

連線函數受到下列限制：
+ **函數大小** – 函數程式碼和組態的大小有限
+ **執行時間** – 函數對於 TLS 連線處理有嚴格的執行時間限制
+ **關聯限制** – 每個分佈只能有一個關聯的連線函數
+ **階段限制** – 只有 LIVE 階段函數可以與分佈建立關聯

## 函數篩選選項
<a name="connection-function-filtering-options"></a>

列出連線函數時，您可以使用下列篩選條件：
+ **階段篩選條件** – 依 DEVELOPMENT 或 LIVE 階段篩選
+ **關聯篩選條件** – 依分佈 ID 或鍵值存放區 ID 關聯進行篩選

這些篩選條件可協助您組織和管理不同環境和使用案例的連線函數。

# 建立 CloudFront 連線函數以進行交互 TLS （檢視器） 驗證
<a name="create-connection-functions"></a>

您會分兩個階段建立 CloudFront Connection Function：

1. 建立函數程式碼做為 JavaScript。您可以從 CloudFront 主控台使用預設範例或自行撰寫。如需詳細資訊，請參閱下列主題：
   + 寫入 mTLS 驗證的 CloudFront 連線函數程式碼
   + CloudFront Connection Function 事件結構和回應格式
   + 連線函數程式碼範例

1. 使用 CloudFront 建立連線函數並包含您的程式碼。程式碼存在於函數內部 (而不是引用形式)。

**Topics**
+ [CloudFront 主控台](#create-connection-function-console)
+ [AWS CLI](#create-connection-function-cli)

## CloudFront 主控台
<a name="create-connection-function-console"></a>

**建立連線函數**

1. 登入 AWS 管理主控台 ，並在 開啟 CloudFront 主控台[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 選擇**建立函數**。

1. 輸入 中唯一的函數名稱 AWS 帳戶，選擇**連線函數**做為函數類型，然後選擇**繼續**。

1. 新連線函數的詳細資訊頁面隨即出現。
**注意**  
連線函數僅支援 JavaScript 執行期 2.0。若要在函數中使用 CloudFront Connection Function KeyValueStore 整合，您必須使用此執行時間版本。

1. 在**函數程式碼**區段中，選擇**建置**索引標籤，然後輸入您的連線函數程式碼。組建索引標籤中包含的範例程式碼說明 Connection Function 程式碼的基本語法。

1. 選擇**儲存變更**。

1. 如果連線函數程式碼使用 KeyValueStore 進行憑證撤銷檢查或裝置驗證，您必須建立 KeyValueStore 的關聯。

   您可以在第一次建立函數時建立 KeyValueStore 的關聯。或者，您可以稍後透過關聯連線函數來建立關聯。

   若要立即建立 KeyValueStore 的關聯，請遵循下列步驟：
   + 前往**關聯 KeyValueStore** 區段，然後選擇**關聯現有的 KeyValueStore**。
   + 選取包含連線函數憑證資料的 KeyValueStore，然後選擇**關聯 KeyValueStore**。

   CloudFront 會立即將存放區與該函數建立關聯。您無需儲存函數。

## AWS CLI
<a name="create-connection-function-cli"></a>

如果您使用 AWS CLI，通常會先在 檔案中建立連線函數程式碼，然後使用 建立函數 AWS CLI。

**建立連線函數**

1. 在 檔案中建立連線函數程式碼，並將其存放在電腦可連線的目錄中。

1. 執行命令，如範例所示。此範例會使用 `fileb://` 標記法來傳入檔案。它還會包括換行符號，讓命令更易於讀取。

   ```
   aws cloudfront create-connection-function \
       --name CertificateValidator \
       --connection-function-config '{
           "Comment":"Device certificate validation",
           "Runtime":"cloudfront-js-2.0",
           "KeyValueStoreAssociations":{
               "Quantity":1,
               "Items":[{
                   "KeyValueStoreARN":"arn:aws:cloudfront::111122223333:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"
               }]
           }
       }' \
       --connection-function-code fileb://certificate-validator.js
   ```
**注意**  
**執行期** – 連線函數僅支援 JavaScript 執行期 2.0 (cloudfront-js-2.0)。
**KeyValueStoreAssociations** – 如果您的連線函數使用 KeyValueStore 進行憑證驗證，您可以在第一次建立函數時關聯 KeyValueStore。或者，您可以稍後使用 update-connection-function 建立關聯。數量一律為 1，因為每個連線函數只能有一個與其相關聯的 KeyValueStore。

1. 如果命令成功執行，您會看到如下所示的輸出。

   ```
   ETag: ETVABCEXAMPLE
   ConnectionFunctionSummary:
     ConnectionFunctionConfig:
       Comment: Device certificate validation
       Runtime: cloudfront-js-2.0
       KeyValueStoreAssociations:
         Quantity: 1
         Items:
           - KeyValueStoreARN: arn:aws:cloudfront::111122223333:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111
     ConnectionFunctionMetadata:
       CreatedTime: '2024-09-04T16:32:54.292000+00:00'
       ConnectionFunctionARN: arn:aws:cloudfront::111122223333:connection-function/CertificateValidator
       LastModifiedTime: '2024-09-04T16:32:54.292000+00:00'
       Stage: DEVELOPMENT
     Name: CertificateValidator
     Status: UNPUBLISHED
   Location: https://cloudfront.amazonaws.com/2020-05-31/connection-function/arn:aws:cloudfront:::connection-function/CertificateValidator
   ```

   大多數資訊都是從請求中複製的。其他資訊會由 CloudFront 新增。
**注意**  
**ETag** – 每次修改連線函數時，此值都會變更。您需要此值才能更新或發佈函數。
**階段** – 在 DEVELOPMENT 階段啟動新的連線函數。您必須先發佈函數以將其移至 LIVE 階段，再將其與分佈建立關聯。
**狀態** – 函數狀態為未發佈，直到您將其發佈至 LIVE 階段為止。

# 寫入 CloudFront 連線函數程式碼以進行交互 TLS （檢視器） 驗證
<a name="write-connection-function-code"></a>

CloudFront Connection Functions 可讓您為 mTLS 憑證驗證和自訂身分驗證邏輯撰寫輕量型 JavaScript 函數。您的 Connection Function 程式碼可以驗證用戶端憑證、實作裝置特定的身分驗證規則、處理憑證撤銷案例，以及針對全球 CloudFront 節點的 TLS 連線做出允許/拒絕決策。

連線函數提供強而有力的方式來使用您自己的商業邏輯擴展 CloudFront 的內建憑證驗證。與處理 HTTP 資料的檢視器請求和檢視器回應函數不同，Connection Functions 會在 TLS 層運作，並可存取憑證資訊、用戶端 IP 地址和 TLS 連線詳細資訊。這使得它們非常適合用於實作零信任安全模型、裝置身分驗證系統和超出標準 PKI 驗證的自訂憑證驗證政策。

您的 Connection Function 程式碼會在安全、隔離且啟動時間低於毫秒的環境中執行，並且可以擴展以處理每秒數百萬個連線。執行時間已針對憑證驗證工作負載進行最佳化，並提供與 CloudFront KeyValueStore 的內建整合，以進行即時資料查詢操作，實現複雜的身分驗證案例，例如憑證撤銷清單檢查和裝置允許清單驗證。

為了協助您撰寫有效的連線函數程式碼，請參閱下列主題。如需完整的程式碼範例和step-by-step教學課程，請參閱本指南中的教學課程章節，並探索 CloudFront 主控台中提供的連線函數範例。

**Topics**
+ [CloudFront Connection Function 使用案例和用途](#connection-function-use-cases)
+ [CloudFront Connection Function 事件結構和回應格式](#connection-function-event-structure)
+ [CloudFront Connection Functions JavaScript 執行期功能](#connection-function-javascript-runtime)
+ [CloudFront Connection Function 協助程式方法和 APIs](#connection-function-helper-methods)
+ [CloudFront Connection Function KeyValueStore 整合](#connection-function-kvs-integration)
+ [使用非同步並等待](#connection-function-async-await)
+ [連線函數程式碼範例](#connection-function-code-examples)

## CloudFront Connection Function 使用案例和用途
<a name="connection-function-use-cases"></a>

寫入 CloudFront Connection Function 之前，請仔細判斷您需要實作的憑證驗證或身分驗證邏輯類型。連線函數是專為需要超出標準 PKI 憑證檢查自訂驗證的特定使用案例而設計。了解您的使用案例可協助您設計符合您安全需求的高效程式碼，同時維持最佳效能。

常見的連線函數使用案例包括：
+ **憑證撤銷處理** – 實作自訂政策來處理撤銷的憑證，包括憑證輪換的寬限期、內部裝置的信任網路例外狀況，或撤銷的憑證可能需要暫時存取的緊急存取案例。
+ **選用的 mTLS 支援** – 使用不同的身分驗證政策處理 mTLS 和非 mTLS 連線，可讓您為支援憑證的用戶端提供增強的安全性，同時保持與舊版用戶端的相容性。
+ **以 IP 為基礎的身分**驗證 – 結合憑證驗證與用戶端 IP 地址檢查，以提高安全性，例如限制來自特定地理區域、公司網路或已知惡意 IP 範圍的存取。
+ **多租戶憑證驗證** – 實作租戶特定的驗證規則，其中根據用戶端憑證發行者或主體屬性，套用不同的憑證授權單位或驗證條件。
+ **以時間為基礎的存取控制** – 強制執行以時間為基礎的限制，其中憑證僅在特定時間、維護時段或商業期間內有效，即使憑證本身尚未過期。

在 CloudFront 執行標準憑證驗證 （信任鏈驗證、過期檢查和簽章驗證） 之後，但在建立 TLS 連線之前執行連線函數。此時間可讓您靈活地新增自訂驗證條件，同時受益於 CloudFront 的內建憑證驗證。您的函數會收到標準驗證的結果，並可以根據標準和自訂條件，做出是否允許或拒絕連線的明智決策。

設計連線函數時，請考慮驗證邏輯的效能影響。函數具有 5 毫秒的執行限制，因此應針對速度最佳化複雜的操作。使用 KeyValueStore 進行快速資料查詢，而非複雜的計算，並建構驗證邏輯以針對無效的憑證快速失敗。

## CloudFront Connection Function 事件結構和回應格式
<a name="connection-function-event-structure"></a>

CloudFront Connection Functions 會收到與檢視器請求和檢視器回應函數不同的事件結構。連線函數會收到憑證和連線資訊，供您用來做出身分驗證決策，而不是 HTTP 請求/回應資料。

**Topics**
+ [Connection Functions 的事件結構](#connection-function-event-structure-details)
+ [Connection Functions 回應格式](#connection-function-response-format)

### Connection Functions 的事件結構
<a name="connection-function-event-structure-details"></a>

Connection Functions 會收到事件物件，其中包含憑證和連線資訊。函數的事件結構如下所示：

```
{
  "clientCertificate": {
    "certificates": {
      "leaf": {
        "serialNumber": "string",
        "issuer": "string",
        "subject": "string",
        "validity": {
          "notBefore": "string",
          "notAfter": "string",
        },
        "sha256Fingerprint": "string"
      }
    }
  },
  "clientIp": "string",
  "endpoint": "string",
  "distributionId": "string",
  "connectionId": "string"
}
```

以下是事件物件結構的範例：

```
{
  "clientCertificate": {
    "certificates": {
      "leaf": {
        "serialNumber": "00:9e:2a:af:16:56:e5:47:25:7d:2e:38:c3:f9:9d:57:fa",
        "issuer": "C=US, O=Ram, OU=Edge, ST=WA, CN=mTLS-CA, L=Snoqualmie",
        "subject": "C=US, O=Ram, OU=Edge, ST=WA, CN=mTLS-CA, L=Snoqualmie",
        "validity": {
          "notBefore": "2025-09-10T23:43:10Z",
          "notAfter": "2055-09-11T00:43:02Z"
        },
        "sha256Fingerprint": "_w6bJ7aOAlGOj7NUhJxTfsfee-ONg_xop3_PTgTJpqs="
      }
    }
  },
  "clientIp": "127.0.0.1",
  "endpoint": "d3lch071jze0cb.cloudfront.net",
  "distributionId": "E1NXS4MQZH501R",
  "connectionId": "NpvTe1925xfj24a67sPQr7ae42BIq03FGhJJKfrQYWZcWZFp96SIIg=="
}
```

### Connection Functions 回應格式
<a name="connection-function-response-format"></a>

您的 Connection Function 必須傳回回應物件，指出是否允許或拒絕連線。使用協助程式方法來做出連線決策：

```
function connectionHandler(connection) {
    // Helper methods to allow or deny connections
    if (/* some logic to determine if function should allow connection */) {
        connection.allow();
    } else {
        connection.deny();
    }
}
```

與檢視器請求和檢視器回應函數不同，連線函數無法修改 HTTP 請求或回應。他們只能允許或拒絕 TLS 連線。

## CloudFront Connection Functions JavaScript 執行期功能
<a name="connection-function-javascript-runtime"></a>

CloudFront Connection Functions 使用 CloudFront Functions JavaScript 執行期 2.0，可提供專為憑證驗證工作負載最佳化的安全且高效能環境。執行時間設計為在幾毫秒內開始，並處理 CloudFront 全球邊緣網路中數百萬個並行執行。

執行期環境包含完整的 JavaScript 語言支援：
+ **ECMAScript 2020 (ES11) 支援** – 現代 JavaScript 功能，包括用於處理大型憑證序號的選用鏈結 (？.)、 nullish coalescing (？？) 和 BigInt 
+ **內建物件** – 物件、陣列、JSON、數學和日期等標準 JavaScript 物件
+ **主控台記錄** – 使用 console.log() 進行偵錯和監控憑證驗證決策。日誌可在測試期間即時提供，有助於疑難排解開發中的驗證邏輯
+ **KeyValueStore 整合** – 原生存取 CloudFront KeyValueStore 以進行超快速的資料查詢操作，啟用即時憑證撤銷檢查、裝置允許清單驗證和租用戶特定的組態擷取

連線函數已針對憑證驗證案例的高效能進行最佳化。執行時間會自動處理記憶體管理、垃圾收集和資源清理，以確保數百萬個並行連線的一致效能。所有操作的設計都具有決定性和快速性，KeyValueStore 查詢通常在微秒內完成。

執行時間環境在函數執行之間完全隔離，確保不同用戶端連線之間的資料不會洩漏。每個函數執行都以清除狀態開頭，無法存取先前的執行結果或來自其他連線的用戶端資料。

## CloudFront Connection Function 協助程式方法和 APIs
<a name="connection-function-helper-methods"></a>

CloudFront Connection Functions 提供專門的協助程式方法，旨在簡化憑證驗證決策並增強可觀測性。這些方法針對連線驗證工作流程進行最佳化，並與 CloudFront 的連線記錄和監控系統無縫整合。
+ **connection.allow()** – 允許 TLS 連線繼續。此方法會通知 CloudFront 完成 TLS 交握，並允許用戶端建立連線。當憑證驗證通過且滿足任何自訂身分驗證邏輯時，請使用此選項
+ **connection.deny()** – 拒絕 TLS 連線並終止交握。此方法會立即關閉連線，並防止任何 HTTP 流量流動。用戶端將收到 TLS 連線錯誤。將此用於無效的憑證、失敗的身分驗證或政策違規
+ **connection.logCustomData()** – 將自訂資料新增至連線日誌 （最多 800 個位元組的 UTF-8 文字）。此方法可讓您在 CloudFront 連線日誌中包含驗證結果、憑證詳細資訊或決策理由，以進行安全性監控、合規稽核和故障診斷

這些方法提供簡潔的宣告式界面，用於做出連線決策，並記錄監控和偵錯的相關資訊。允許/拒絕模式可確保函數的意圖明確，且 CloudFront 可以根據您的決定最佳化連線處理。CloudFront 連線日誌會立即提供自訂記錄資料，並可搭配日誌分析工具使用，以進行安全性監控和操作洞察。

在函數完成之前，請務必呼叫 connection.allow() 或 connection.deny()。如果不呼叫任何方法，CloudFront 預設會拒絕連線，作為安全預防措施。

## CloudFront Connection Function KeyValueStore 整合
<a name="connection-function-kvs-integration"></a>

CloudFront Connection Functions 可以使用 CloudFront KeyValueStore 針對憑證驗證案例執行超快速的資料查詢。KeyValueStore 對 Connection Functions 特別強大，因為它可在所有 CloudFront 節點提供全域、最終一致的資料存取，以及微秒的查詢時間。這使得它非常適合用於維護 TLS 交握期間需要存取的憑證撤銷清單、裝置允許清單、租戶組態和其他驗證資料。

KeyValueStore 整合專為高效能連線驗證工作流程而設計：
+ **kvsHandle.exists(key)** – 檢查 KeyValueStore 中是否存在金鑰，而不擷取值。這是二進位驗證案例最有效的方法，例如憑證撤銷檢查，您只需知道憑證序號是否在撤銷清單中
+ **kvsHandle.get(key)** – 針對更複雜的驗證案例，從 KeyValueStore 擷取值。當您需要存取與憑證或裝置識別符相關聯的組態資料、驗證規則或中繼資料時，請使用此選項

KeyValueStore 操作是非同步的，必須搭配非同步/等待語法使用。KeyValueStore 的總大小限制為 10MB，並支援最多 1，000 萬個鍵值對。KeyValueStore 資料最終在所有節點保持一致，更新通常會在幾秒鐘內傳播。

為了獲得最佳效能，請建構您的 KeyValueStore 金鑰，將查詢操作降至最低。使用憑證序號做為簡易撤銷檢查的金鑰，或建立合併多 CA 環境發行者雜湊和序號的複合金鑰。設計資料結構時，請考慮關鍵複雜性與 KeyValueStore 容量之間的權衡。

## 使用非同步並等待
<a name="connection-function-async-await"></a>

連線函數支援使用非同步/等待語法的非同步操作，這對於使用 KeyValueStore 操作或其他非同步任務至關重要。非同步/等待模式可確保函數在做出連線決策之前等待 KeyValueStore 查詢完成，同時維持 TLS 交握處理所需的高效能特性。

適當的非同步/等待用量對於連線函數至關重要，因為 KeyValueStore 操作雖然速度非常快，但仍是需要跨 CloudFront 分散式基礎設施協調的網路操作。執行時間會自動處理 promise 解析度，並確保您的函數在 5 毫秒執行限制內完成。

**Example ：使用 KeyValueStore 的非同步連線函數**  

```
import cf from 'cloudfront';

async function connectionHandler(connection) {
    const kvsHandle = cf.kvs();
    
    // Async operation to check KeyValueStore for certificate revocation
    const isRevoked = await kvsHandle.exists(connection.clientCertificate.certificates.leaf.serialNumber);
    
    if (isRevoked) {
        // Log the revocation decision with certificate details
        connection.logCustomData(`REVOKED_CERT:${connection.clientCertificate.certificates.leaf.serialNumber}:${connection.clientCertificate.certificates.leaf.issuer}`);
        console.log(`Denying connection for revoked certificate: ${connection.clientCertificate.certificates.leaf.serialNumber}`);
        return connection.deny();
    }
    
    // Log successful validation for monitoring
    connection.logCustomData(`VALID_CERT:${connection.clientCertificate.certificates.leaf.serialNumber}`);
    console.log(`Allowing connection for valid certificate: ${connection.clientCertificate.certificates.leaf.serialNumber}`);
    return connection.allow();
}
```

呼叫 KeyValueStore 方法或其他非同步操作時，一律使用非同步/等待。Connection Function 執行時間會自動處理 promise 解析，並確保在 TLS 交握處理的嚴格時間限制內有適當的執行流程。避免使用 .then() 或回呼模式，因為非同步/等待可在連線函數環境中提供更明確的錯誤處理和更好的效能。

設計非同步連線函數時，請建構您的程式碼，以盡可能減少 KeyValueStore 操作的數量，並儘早在驗證邏輯中執行這些操作。這可確保最大效能，並降低高流量期間逾時問題的風險。請考慮批次處理相關的驗證檢查，並針對您的使用案例使用最有效率的 KeyValueStore 方法 (exists() vs get())。

## 連線函數程式碼範例
<a name="connection-function-code-examples"></a>

下列範例示範適用於不同驗證案例的常見連線函數模式。使用這些範例做為您自己的連線函數實作的起點。

**Example ：裝置憑證驗證**  
此範例會驗證 IoT 裝置、遊戲主控台和其他用戶端身分驗證案例的裝置序號和憑證主旨欄位：  

```
async function connectionHandler(connection) {
    // Custom validation: check device serial number format
    const serialNumber = connection.clientCertificate.certificates.leaf.serialNumber;
    if (!serialNumber.startsWith("DEV")) {
        connection.logCustomData(`INVALID_SERIAL:${serialNumber}`);
        return connection.deny();
    }
    
    // Validate certificate subject contains required organizational unit
    const subject = connection.clientCertificate.certificates.leaf.subject;
    if (!subject.includes("OU=AuthorizedDevices")) {
        connection.logCustomData(`INVALID_OU:${subject}`);
        return connection.deny();
    }
    
    // Allow connection for valid devices
    connection.logCustomData(`VALID_DEVICE:${serialNumber}`);
    return connection.allow();
}
```
除了標準憑證驗證之外，此函數會執行多個驗證檢查，包括裝置序號格式和組織單位驗證。

**Example ：選用具有混合身分驗證的 mTLS**  
此範例使用不同的身分驗證政策處理 mTLS 和非 mTLS 連線：  

```
async function connectionHandler(connection) {
    if (connection.clientCertificate) {
        // mTLS connection - enhanced validation for certificate holders
        const subject = connection.clientCertificate.certificates.leaf.subject;
        connection.logCustomData(`MTLS_SUCCESS:${subject}:${connection.clientIp}`);
        console.log(`mTLS connection from: ${subject}`);
        return connection.allow();
    } else {
        // Non-mTLS connection - apply IP-based restrictions
        const clientIp = connection.clientIp;
        
        // Only allow non-mTLS from specific IP ranges
        if (clientIp.startsWith("203.0.113.") || clientIp.startsWith("198.51.100.")) {
            connection.logCustomData(`NON_MTLS_ALLOWED:${clientIp}`);
            console.log(`Non-mTLS connection allowed from: ${clientIp}`);
            return connection.allow();
        }
        
        connection.logCustomData(`NON_MTLS_DENIED:${clientIp}`);
        return connection.deny();
    }
}
```
此函數為具有憑證的用戶端提供增強的安全性，同時保持與信任 IP 範圍內舊版用戶端的相容性。

# 在部署之前測試 CloudFront 連線函數
<a name="test-connection-functions"></a>

您可以使用 TestConnectionFunction API 操作，在 DEVELOPMENT 階段測試 CloudFront Connection Functions。 TestConnectionFunction 測試可讓您在發佈至 LIVE 階段之前，使用範例連線事件來驗證函數邏輯。

**Topics**
+ [測試程序](#connection-function-testing-process)
+ [測試結果](#connection-function-test-results)
+ [連線測試物件](#connection-test-object)

## 測試程序
<a name="connection-function-testing-process"></a>

若要測試連線函數：

1. 在 DEVELOPMENT 階段建立連線函數

1. 準備代表 TLS 連線事件的測試連線物件

1. 使用 TestConnectionFunction API 操作以測試資料執行您的函數

1. 檢閱測試結果，包括函數輸出、執行日誌和任何錯誤訊息

1. 視需要更新您的函數程式碼，並重複測試程序

## 測試結果
<a name="connection-function-test-results"></a>

當您測試連線函數時，結果包括：
+ **函數摘要** – 有關已測試函數的中繼資料
+ **運算使用率** – 顯示資源使用量的效能指標
+ **執行日誌** – 來自函數的主控台輸出，包括任何記錄陳述式
+ **函數輸出** – 函數傳回的結果
+ **錯誤訊息 – **執行期間發生的任何執行時間錯誤或例外狀況

## 連線測試物件
<a name="connection-test-object"></a>

連線測試物件是二進位 Blob （最多 40KB)，代表函數將處理的 TLS 連線事件。此物件包含您的函數用來進行身分驗證決策的憑證和連線資訊。

**注意**  
連線測試物件的特定結構和格式是由 CloudFront Connection Functions 執行時間所定義。如需為您的使用案例建立適當測試物件的詳細資訊 AWS 支援 ，請參閱 CloudFront Functions 文件或聯絡人。

建立連線函數之後，您可以：
+ **測試 函數** – 使用主控台或 CLI 中的測試功能，透過範例連線事件來驗證您的函數。如需詳細資訊，請參閱連線函數測試。
+ **更新函數** – 視需要修改函數程式碼和組態。DEVELOPMENT 階段中的連線函數可以隨時更新。
+ **發佈函數** – 準備生產時，發佈函數以將其從 DEVELOPMENT 移至 LIVE 階段。如需詳細資訊，請參閱關聯連線函數。
+ **與分佈建立關聯** – 將發佈的函數與啟用 mTLS 的分佈建立關聯，以處理即時連線。如需詳細資訊，請參閱關聯連線函數。

# 將連線函數與分佈建立關聯
<a name="associate-connection-functions"></a>

將 Connection Function 發佈至 LIVE 階段之後，您必須將其與啟用 mTLS 的分佈建立關聯，才能處理即時連線。連線函數與分佈層級相關聯，不同於與快取行為相關聯的檢視器請求和檢視器回應函數。

**Topics**
+ [關聯需求](#connection-function-association-requirements)
+ [使用篩選條件組織函數](#connection-function-organizing-filters)
+ [部署考量](#connection-function-deployment-considerations)

## 關聯需求
<a name="connection-function-association-requirements"></a>

若要將連線函數與分佈建立關聯：
+ 函數必須處於 LIVE 階段
+ 分佈必須啟用 mTLS
+ 分佈必須設定有效的信任存放區
+ 每個分佈只能關聯一個連線函數

## 使用篩選條件組織函數
<a name="connection-function-organizing-filters"></a>

CloudFront 提供篩選功能，可協助您組織和管理連線函數：
+ **分佈 ID 篩選條件** – 尋找與特定分佈相關聯的函數
+ **鍵值存放區篩選條件** – 尋找使用特定鍵值存放區進行資料查詢的函數
+ **階段篩選條件** – 列出 DEVELOPMENT 或 LIVE 階段中的函數

在不同分佈或開發環境中管理多個連線函數時，請使用這些篩選條件。

## 部署考量
<a name="connection-function-deployment-considerations"></a>

部署連線函數時，請考慮下列因素：
+ **全域部署** – 連線函數部署到全球所有 CloudFront 節點，這可能需要幾分鐘的時間
+ **版本管理** – 每個發佈的版本都會建立新的 LIVE 函數來取代先前的版本
+ **回復策略** – 透過保留函數程式碼的先前工作版本來規劃回復
+ 在**生產環境中測試** – 考慮在預備和生產環境中使用個別的分佈

# 使用 CloudFront Functions 和 KeyValueStore 實作交互 TLS （檢視器） 的憑證撤銷
<a name="implement-certificate-revocation"></a>

您可以使用 CloudFront Connection Functions 搭配 KeyValueStore 來實作憑證撤銷檢查。這可讓您維護撤銷的憑證序號清單，並在 TLS 交握期間針對此清單檢查用戶端憑證。

若要實作憑證撤銷，您需要這些元件：
+ 使用檢視器 mTLS 設定的分佈
+ 包含已撤銷憑證序號的 KeyValueStore 
+ 查詢 KeyValueStore 以檢查憑證狀態的連線函數

當用戶端連線時，CloudFront 會根據信任存放區驗證憑證，然後執行連線函數。您的函數會根據 KeyValueStore 檢查憑證序號，並允許或拒絕連線。

**Topics**
+ [步驟 1：為已撤銷的憑證建立 KeyValueStore](create-kvs-revoked-certificates.md)
+ [步驟 2：建立撤銷連線函數](create-revocation-connection-function.md)
+ [步驟 3：測試您的撤銷函數](test-revocation-function.md)
+ [步驟 4：將函數與分佈建立關聯](associate-function-distribution.md)
+ [進階撤銷案例](advanced-revocation-scenarios.md)

# 步驟 1：為已撤銷的憑證建立 KeyValueStore
<a name="create-kvs-revoked-certificates"></a>

建立 KeyValueStore，以存放連線函數在 mTLS 連線期間可以檢查的撤銷憑證序號。

首先，以 JSON 格式準備您撤銷的憑證序號：

```
{
  "data": [
    {
      "key": "ABC123DEF456",
      "value": ""
    },
    {
      "key": "789XYZ012GHI", 
      "value": ""
    }
  ]
}
```

將此 JSON 檔案上傳至 S3 儲存貯體，然後建立 KeyValueStore：

```
aws s3 cp revoked-serials.json s3://your-bucket-name/revoked-serials.json
aws cloudfront create-key-value-store \
  --name revoked-serials-kvs \
  --import-source '{
    "SourceType": "S3",
    "SourceARN": "arn:aws:s3:::your-bucket-name/revoked-serials.json"
  }'
```

等待 KeyValueStore 完成佈建。使用 檢查狀態：

```
aws cloudfront get-key-value-store --name "revoked-serials-kvs"
```

# 步驟 2：建立撤銷連線函數
<a name="create-revocation-connection-function"></a>

建立連線函數，根據 KeyValueStore 檢查憑證序號，以判斷憑證是否遭到撤銷。

建立連線函數，根據 KeyValueStore 檢查憑證序號：

```
aws cloudfront create-connection-function \
  --name "revocation-control" \
  --connection-function-config file://connection-function-config.json \
  --connection-function-code file://connection-function-code.txt
```

組態檔案會指定 KeyValueStore 關聯：

```
{
  "Runtime": "cloudfront-js-2.0",
  "Comment": "A function that implements revocation control via KVS",
  "KeyValueStoreAssociations": {
    "Quantity": 1,
    "Items": [
      {
        "KeyValueStoreArn": "arn:aws:cloudfront::account-id:key-value-store/kvs-id"
      }
    ]
  }
}
```

Connection Function 程式碼會檢查 KeyValueStore 是否有已撤銷的憑證：

```
import cf from 'cloudfront';

async function connectionHandler(connection) {
    const kvsHandle = cf.kvs();
    
    // Get parsed client serial number from client certificate
    const clientSerialNumber = connection.clientCertInfo.serialNumber;
    
    // Check KVS to see if serial number exists as a key
    const serialNumberExistsInKvs = await kvsHandle.exists(clientSerialNumber);
    
    // Deny connection if serial number exists in KVS
    if (serialNumberExistsInKvs) {
        console.log("Connection denied - certificate revoked");
        return connection.deny();
    }
    
    // Allow connections that don't exist in kvs
    console.log("Connection allowed");
    return connection.allow();
}
```

# 步驟 3：測試您的撤銷函數
<a name="test-revocation-function"></a>

使用 CloudFront 主控台以範例憑證測試連線函數。導覽至 主控台中的連線函數，並使用測試索引標籤。

**使用範例憑證進行測試**

1. 將 PEM 格式的範例憑證貼入測試界面

1. 選擇性地指定用戶端 IP 地址以測試 IP 型邏輯

1. 選擇**測試函數**以查看執行結果

1. 檢閱執行日誌以驗證您的函數邏輯

使用有效和已撤銷的憑證進行測試，以確保您的函數正確處理這兩個案例。執行日誌會顯示 console.log 輸出和函數執行期間發生的任何錯誤。

# 步驟 4：將函數與分佈建立關聯
<a name="associate-function-distribution"></a>

發佈連線函數後，請將其與啟用 mTLS 的分佈建立關聯，以啟用憑證撤銷檢查。

您可以從分佈設定頁面或從連線函數的關聯分佈資料表建立函數的關聯。導覽至您的分佈設定，捲動至**檢視器交互驗證 (mTLS)** 區段，選取連線函數，然後儲存變更。

# 進階撤銷案例
<a name="advanced-revocation-scenarios"></a>

如需更複雜的憑證撤銷要求，請考慮下列其他組態：

**Topics**
+ [將憑證撤銷清單 (CRL) 轉換為 KeyValueStore 格式](#convert-crl-kvs-format)
+ [處理多個憑證授權機構](#handle-multiple-cas)
+ [將自訂資料新增至連線日誌](#add-custom-data-logs)
+ [管理 CRL 更新](#manage-crl-updates)
+ [規劃 KeyValueStore 容量](#plan-kvs-capacity)

## 將憑證撤銷清單 (CRL) 轉換為 KeyValueStore 格式
<a name="convert-crl-kvs-format"></a>

如果您有憑證撤銷清單 (CRL) 檔案，您可以使用 OpenSSL 和 jq 將其轉換為 KeyValueStore JSON 格式：

**將 CRL 轉換為 KeyValueStore 格式**

從 CRL 檔案擷取序號：

```
openssl crl -text -noout -in rfc5280_CRL.crl | \
  awk '/Serial Number:/ {print $3}' | \
  cut -d'=' -f2 | \
  sed 's/../&:/g;s/:$//' >> serialnumbers.txt
```

將序號轉換為 KeyValueStore JSON 格式：

```
jq -R -s 'split("\n") | map(select(length > 0)) | {data: map({"key": ., "value": ""})}' \
  serialnumbers.txt >> serialnumbers_kvs.json
```

將格式化檔案上傳至 S3，並建立 KeyValueStore，如步驟 1 所述。

## 處理多個憑證授權機構
<a name="handle-multiple-cas"></a>

當您的 TrustStore 包含多個憑證授權機構 CAs) 時，請在 KeyValueStore 金鑰中包含發行者資訊，以避免來自可能具有相同序號之不同 CAs憑證之間發生衝突。

對於多 CA 案例，請使用發行者的 SHA1 雜湊和序號的組合做為金鑰：

```
import cf from 'cloudfront';

async function connectionHandler(connection) {
    const kvsHandle = cf.kvs();
    const clientCert = connection.clientCertInfo;
    
    // Create composite key with issuer hash and serial number
    const issuer = clientCert.issuer.replace(/[^a-zA-Z0-9]/g, '').substring(0, 20);
    const serialno = clientCert.serialNumber;
    const compositeKey = `${issuer}_${serialno}`;
    
    const cert_revoked = await kvsHandle.exists(compositeKey);
    
    if (cert_revoked) {
        console.log(`Blocking revoked cert: ${serialno} from issuer: ${issuer}`);
        connection.deny();
    } else {
        connection.allow();
    }
}
```

**注意**  
使用發行者識別符 \$1 序號會建立較長的金鑰，這可能會減少您可以存放在 KeyValueStore 中的項目總數。

## 將自訂資料新增至連線日誌
<a name="add-custom-data-logs"></a>

連線函數可以使用 logCustomData 方法將自訂資料新增至 CloudFront 連線日誌。這可讓您在日誌中包含撤銷檢查結果、憑證資訊或其他相關資料。

```
async function connectionHandler(connection) {
    const kvsHandle = cf.kvs();
    const clientSerialNumber = connection.clientCertInfo.serialNumber;
    const serialNumberExistsInKvs = await kvsHandle.exists(clientSerialNumber);
    
    if (serialNumberExistsInKvs) {
        // Log revocation details to connection logs
        connection.logCustomData(`REVOKED:${clientSerialNumber}:DENIED`);
        console.log("Connection denied - certificate revoked");
        return connection.deny();
    }
    
    // Log successful validation
    connection.logCustomData(`VALID:${clientSerialNumber}:ALLOWED`);
    console.log("Connection allowed");
    return connection.allow();
}
```

自訂資料限制為 800 個位元組的有效 UTF-8 文字。如果您超過此限制，CloudFront 會將資料截斷到最近的有效 UTF-8 邊界。

**注意**  
自訂資料記錄只有在為您的分佈啟用連線日誌時才有效。如果未設定連線日誌，logCustomData 方法是無操作。

## 管理 CRL 更新
<a name="manage-crl-updates"></a>

憑證授權機構可以發出兩種類型的 CRLs：
+ **完整 CRLs**：包含所有已撤銷憑證的完整清單
+ **Delta CRLs**：僅列出自上次完整 CRL 以來撤銷的憑證

如需完整的 CRL 更新，請使用更新的資料建立新的 KeyValueStore，並將連線函數關聯重新導向至新的 KeyValueStore。這種方法比計算差異和執行增量更新更簡單。

對於差異 CRL 更新，請使用 update-keys 命令將新的撤銷憑證新增至現有的 KeyValueStore：

```
aws cloudfront update-key-value-store \
  --name "revoked-serials-kvs" \
  --if-match "current-etag" \
  --put file://delta-revoked-serials.json
```

## 規劃 KeyValueStore 容量
<a name="plan-kvs-capacity"></a>

KeyValueStore 的大小限制為 5 MB，並支援最多 1，000 萬個鍵值對。根據您的金鑰格式和資料大小來規劃撤銷清單容量：
+ **僅限序號**：簡單撤銷檢查的高效儲存
+ **發行者識別符 \$1 序號**：多 CA 環境的較長金鑰

對於大型撤銷清單，請考慮實作分層方法，在其中針對不同的憑證類別或期間維護個別的 KeyValueStores。

# 使用 Lambda@Edge 在邊緣自訂
<a name="lambda-at-the-edge"></a>

Lambda@Edge 是 的延伸 AWS Lambda。Lambda@Edge 是運算服務，可讓您執行函數，以自訂 Amazon CloudFront 交付的內容。您可以在 Lambda 主控台中的 AWS 區域美國東部 （維吉尼亞北部） 撰寫 Node.js 或 Python 函數。

建立函數後，您可以使用 Lambda 主控台或 CloudFront 主控台新增觸發，讓函數在更接近檢視器 AWS 的位置執行，而無需佈建或管理伺服器。或者您可以使用 Lambda 和 CloudFront API 操作，以程式設計方式設定函數和觸發。

Lambda@Edge 會自動擴展，從每天幾個請求自動擴展到每秒數千個請求。在更接近瀏覽者而非原始伺服器 AWS 的位置處理請求，可大幅降低延遲並改善使用者體驗。

**注意**  
gRPC 請求不支援 Lambda@Edge。如需更多詳細資訊，請參閱 [使用 gRPC 搭配 CloudFront 分佈](distribution-using-grpc.md)。

**Topics**
+ [Lambda@Edge 如何處理請求和回應](lambda-edge-event-request-response.md)
+ [使用 Lambda@Edge 的方式](lambda-edge-ways-to-use.md)
+ [開始使用 Lambda@Edge 函數 (主控台)](lambda-edge-how-it-works.md)
+ [設定 Lambda@Edge 的 IAM 許可權限和角色](lambda-edge-permissions.md)
+ [撰寫及建立 Lambda@Edge 函數](lambda-edge-create-function.md)
+ [為 Lambda@Edge 函數新增觸發條件](lambda-edge-add-triggers.md)
+ [測試和偵錯 Lambda@Edge 函數](lambda-edge-testing-debugging.md)
+ [刪除 Lambda@Edge 函數和複本](lambda-edge-delete-replicas.md)
+ [Lambda@Edge 事件結構說明頁面](lambda-event-structure.md)
+ [使用請求和回應](lambda-generating-http-responses.md)
+ [Lambda@Edge 範例函數](lambda-examples.md)

# Lambda@Edge 如何處理請求和回應
<a name="lambda-edge-event-request-response"></a>

當您將 CloudFront 分佈與 Lambda@Edge 函數建立關聯時，CloudFront 會攔截在 CloudFront 節點的請求和回應。您可以在發生下列 CloudFront 事件時執行 Lambda 函數：
+ 當 CloudFront 接收到來自檢視器的請求 (檢視器請求) 時
+ 在 CloudFront 轉寄請求至來源 (原始請求) 之前
+ 當 CloudFront 接收到來自來源的回應 (原始回應) 時
+ 在 CloudFront 傳回回應給檢視器 (檢視器回應) 之前

如果您使用的是 AWS WAF，則會在套用任何 AWS WAF 規則後執行 Lambda@Edge 檢視器請求。

如需詳細資訊，請參閱[使用請求和回應](lambda-generating-http-responses.md)及[Lambda@Edge 事件結構說明頁面](lambda-event-structure.md)。

# 使用 Lambda@Edge 的方式
<a name="lambda-edge-ways-to-use"></a>

您的 Amazon CloudFront 分佈有許多 Lambda@Edge 處理用途，例如下列範例：
+ Lambda 函數可檢查 Cookie 和重新寫入 URL，讓使用者看到不同版本網頁的 A/B 測試。
+ CloudFront 可以檢視 `User-Agent` 標頭來根據檢視器使用的裝置傳回不同的物件給檢視器，其中包含裝置相關資訊。例如，根據檢視器裝置的螢幕尺寸，CloudFront 可以傳回不同的圖片大小。同樣地，該函數可以考慮 `Referer` 標頭中的值，讓 CloudFront 傳回最低可用解析度圖片給機器人。
+ 或者，您可以檢查 Cookie 的其他條件。例如，在銷售衣服的零售網站，如果您使用 Cookie 以顯示使用者選擇什麼顏色的夾克，該 Lambda 函數可以變更請求，讓 CloudFront 傳回所選顏色的夾克圖片。
+ 發生 CloudFront 檢視器請求或原始伺服器請求事件時，Lambda 函數可產生 HTTP 回應。
+ 函數可以檢查標頭或授權字符，在 CloudFront 轉送請求到您的原始伺服器之前，插入標頭來控制存取您的內容。
+ Lambda 函數也可以讓網路呼叫外部資源，確認使用者登入資料，或擷取額外的內容以自訂回應。

如需包含範例程式碼的詳細資訊，請參閱 [Lambda@Edge 範例函數](lambda-examples.md)。

如需有關在主控台設定 Lambda@Edge 的詳細資訊，請參閱 [教學課程：建立基本 Lambda@Edge 函數 (主控台)](lambda-edge-how-it-works-tutorial.md)。

# 開始使用 Lambda@Edge 函數 (主控台)
<a name="lambda-edge-how-it-works"></a>

Lambda@Edge 可讓您使用 CloudFront 觸發條件來調用 Lambda 函數。當您將 CloudFront 分佈與 Lambda 函數建立關聯時，CloudFront 會在 CloudFront 節點[攔截請求和回應](https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html)，並執行該函數。Lambda 函數可以提高安全性，或是自訂與檢視器密切相關的資訊來提升效能。

下列清單提供基本概觀，協助瞭解如何透過 CloudFront 建立和使用 Lambda 函數。

**概觀：透過 CloudFront 建立和使用 Lambda 函數**

1. 在美國東部 (維吉尼亞北部) 區域建立 Lambda 函數。

1. 儲存並發佈有編號的函數版本。

   如果您想要變更函數，您必須編輯美國東部 (維吉尼亞北部) 區域內 \$1LATEST 版本的函數。然後，在您設定它來搭配 CloudFront 使用之前，請發佈新編號的版本。

1. 將函數與 CloudFront 分佈和快取行為建立關聯。然後，指定可以使函數執行的一或多個 CloudFront 事件 (*觸發條件*)。例如，您可以建立當 CloudFront 接收來自檢視器的請求時，促使函數執行的觸發條件。

1. 您建立觸發條件時，Lambda 會在全球各地的 AWS 位置建立函數複本。

**提示**  
如需更多詳細資訊，請參閱[如何建立和更新函數](lambda-edge-create-function.md)、[事件結構](lambda-event-structure.md)，以及[如何新增 CloudFront 觸發條件](lambda-edge-add-triggers.md)。您也可以在[Lambda@Edge 範例函數](lambda-examples.md)中找到更多想法並取得程式碼範例。

如需逐步教學課程，請參閱下列主題：

**Topics**
+ [教學課程：建立基本 Lambda@Edge 函數 (主控台)](lambda-edge-how-it-works-tutorial.md)

# 教學課程：建立基本 Lambda@Edge 函數 (主控台)
<a name="lambda-edge-how-it-works-tutorial"></a>

本教學課程將為您示範如何開始使用 Lambda@Edge，協助您建立及設定在 CloudFront 中執行的 Node.js 函數範例。此項範例會在 CloudFront 檢索檔案時，將 HTTP 安全性標頭新增到回應。(這可以改善網站的安全性和隱私。)

本教學課程不需要您自己的網站。不過您建立自己的 Lambda@Edge 解決方案時，會依照類似的步驟進行，並從相同的選項中選擇。

**Topics**
+ [步驟 1：註冊 AWS 帳戶](#lambda-edge-how-it-works-tutorial-AWS)
+ [步驟 2：建立 CloudFront 分佈](#lambda-edge-how-it-works-tutorial-cloudfront)
+ [步驟 3：建立函數](#lambda-edge-how-it-works-tutorial-create-function)
+ [步驟 4：新增一個 CloudFront 觸發條件來執行該函數](#lambda-edge-how-it-works-tutorial-add-trigger)
+ [步驟 5：驗證函數正常執行](#lambda-edge-how-it-works-tutorial-verify)
+ [步驟 6：排除問題](#lambda-edge-how-it-works-tutorial-troubleshoot)
+ [步驟 7：清除範例資源](#lambda-edge-how-it-works-tutorial-cleanup-resources)
+ [相關資訊](#lambda-edge-how-it-works-tutorial-resources)

## 步驟 1：註冊 AWS 帳戶
<a name="lambda-edge-how-it-works-tutorial-AWS"></a>

若您尚未註冊，請先註冊 AWS 帳戶。如需詳細資訊，請參閱[註冊 AWS 帳戶](setting-up-cloudfront.md#sign-up-for-aws)。

## 步驟 2：建立 CloudFront 分佈
<a name="lambda-edge-how-it-works-tutorial-cloudfront"></a>

在建立 Lambda@Edge 範例函數之前，您必須有一個可使用、包含提供內容之來源的 CloudFront 環境。

在此範例中，您會建立 CloudFront 分佈，此分佈是使用 Amazon S3 儲存貯體做為分佈的原始伺服器。如果您已有環境可使用，可以略過此步驟。<a name="lambda-edge-how-it-works-tutorial-cf-proc"></a>

**使用 Amazon S3 原始伺服器建立 CloudFront 分佈**

1. 使用一兩個檔案來建立 Amazon S3 儲存貯體，例如範例內容適用的映像檔案。如需說明，請遵循[將您的內容上傳到 Amazon S3](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.html#GettingStartedUploadContent) 中的步驟。請確定您有設定對應的許可，以授予儲存貯體中物件的公有讀取權限。

1. 請依照[建立 CloudFront Web 分佈](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.html#GettingStartedCreateDistribution)中的步驟來建立 CloudFront 分佈，並新增您的 S3 儲存貯體當做原始伺服器。如果您已有分佈，可以改為新增儲存貯體當做該分佈的來源。
**提示**  
請記下您的分佈 ID。稍後您在本教學課程中為函數新增 CloudFront 觸發條件時，您必須在下拉式清單中選擇分佈的 ID，例如 `E653W22221KDDL`。

## 步驟 3：建立函數
<a name="lambda-edge-how-it-works-tutorial-create-function"></a>

在這個步驟中，您將從 Lambda 主控台提供的藍圖範本開始建立 Lambda 函數。此函數會新增程式碼來更新您的 CloudFront 分佈中的安全標頭。<a name="lambda-edge-how-it-works-tutorial-create-function-blueprint-proc"></a>

**建立 Lambda 函數**

1. 登入 AWS 管理主控台 並開啟位於 的 AWS Lambda 主控台[https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/)。
**重要**  
請確定您位於美國**US-East-1（維吉尼亞北部） ** AWS 區域 (**us-east-1**)。您必須位在此區域，才能建立 Lambda@Edge 函數。

1. 選擇 **Create function (建立函數)**。

1. 在**建立函數**頁面上，選擇**使用藍圖**，然後在搜尋欄位中輸入 **cloudfront** 以篩選 CloudFront 藍圖。
**注意**  
CloudFront 藍圖僅適用於**美國東部 1 (維吉尼亞北部)** 區域 (**us-east-1**)。

1. 選擇**修改 HTTP 回應標頭**藍圖做為函數的範本。

1. 輸入以下有關函數的資訊：
   + **函數名稱**：輸入函數的名稱。
   + **執行角色**：選擇如何為函數設定許可權限。若要使用建議的基本 Lambda@Edge 許可政策範本，請選擇**從 AWS 政策範本建立新角色**。
   + **角色名稱**：輸入政策範本建立的角色名稱。
   + **政策範本**：Lambda 會自動新增**基本 Lambda@Edge 許可權限**政策範本，因為您選擇 CloudFront 藍圖做為您的函數基礎。此原則範本會新增執行角色許可，讓 CloudFront 能夠在全球的 CloudFront 位置為您執行 Lambda 函數。如需詳細資訊，請參閱[設定 Lambda@Edge 的 IAM 許可權限和角色](lambda-edge-permissions.md)。

1. 請在頁面底部選擇**建立函數**。

1. 在顯示的**部署至 Lambda@Edge** 窗格中選擇**取消**。(在本教學課程中，您必須先修改函數程式碼，才能將函數部署至 Lambda@Edge。)

1. 向下捲動至頁面的**程式碼來源**區段。

1. 使用修改原始伺服器傳回之安全性標頭的函數，來取代範本程式碼。例如，您可能會使用與下列類似的程式碼：

   ```
   'use strict';
   export const handler = (event, context, callback) => {
   
       //Get contents of response
       const response = event.Records[0].cf.response;
       const headers = response.headers;
   
       //Set new headers
       headers['strict-transport-security'] = [{key: 'Strict-Transport-Security', value: 'max-age= 63072000; includeSubdomains; preload'}];
       headers['content-security-policy'] = [{key: 'Content-Security-Policy', value: "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'"}];
       headers['x-content-type-options'] = [{key: 'X-Content-Type-Options', value: 'nosniff'}];
       headers['x-frame-options'] = [{key: 'X-Frame-Options', value: 'DENY'}];
       headers['x-xss-protection'] = [{key: 'X-XSS-Protection', value: '1; mode=block'}];
       headers['referrer-policy'] = [{key: 'Referrer-Policy', value: 'same-origin'}];
   
       //Return modified response
       callback(null, response);
   };
   ```

1. 選擇**檔案**，然後選擇**儲存**以儲存更新後的程式碼。

1. 選擇**部署**。

繼續依照下一節的步驟，新增 CloudFront 觸發條件以執行函數。

## 步驟 4：新增一個 CloudFront 觸發條件來執行該函數
<a name="lambda-edge-how-it-works-tutorial-add-trigger"></a>

既然您已擁有用來更新安全標頭的 Lambda 函數，您可以設定 CloudFront 觸發條件來執行函數，在 CloudFront 從您的分佈原始伺服器收到的任何回應中新增標頭。<a name="lambda-edge-how-it-works-tutorial-add-trigger-proc"></a>

**設定函數的 CloudFront 觸發條件**

1. 在 Lambda 主控台頁面的**函數概覽**頁面中，選擇**新增觸發條件**。

1. 針對**觸發組態**選擇 **CloudFront**。

1. 選擇**部署到 Lambda@Edge**。

1. 在**部署到 Lambda@Edge** 面板中，在**設定 CloudFront 觸發條件**下輸入以下資訊：
   + **分佈**：要與函數建立關聯的 CloudFront 分佈 ID。在下拉式清單中選擇分佈 ID。
   + **快取行為**：要用於觸發條件的快取行為。在此範例中，請將值的設定保留為 **\$1**，這代表分佈的預設快取行為。如需詳細資訊，請參閱 [所有分佈設定參考](distribution-web-values-specify.md) 主題中的 [快取行為設定](DownloadDistValuesCacheBehavior.md)。
   + **CloudFront 事件**：指定函數何時執行的觸發條件。我們希望每當 CloudFront 從原始伺服器傳回回應時，都會執行安全標頭函數。因此請在下拉式清單中選擇**原始伺服器回應**。如需詳細資訊，請參閱[為 Lambda@Edge 函數新增觸發條件](lambda-edge-add-triggers.md)。

1. 選取**確認部署到 Lambda@Edge** 核取方塊。

1. 選擇**部署**以新增觸發條件，並將函數複寫至全球 AWS 位置。

1. 請等候函數完成複寫。這通常需要幾分鐘的時間。

    若要檢查複寫是否已完成，請[移至 CloudFront主控台](https://console.aws.amazon.com/cloudfront/v4/home)並檢視您的分佈。等待分佈的狀態是否已從**正在部署**變為日期和時間 (表示您的函數已複寫完成)。若要驗證函數是否可正常運作，請依照下一節的步驟進行。

## 步驟 5：驗證函數正常執行
<a name="lambda-edge-how-it-works-tutorial-verify"></a>

既然您已建立 Lambda 函數並設定觸發條件來為 CloudFront 分佈執行該函數，接下來請進行檢查，以確定函數會如預期執行。在此範例中，我們會檢查 CloudFront 傳回的 HTTP 標頭，以確認安全標頭已新增。<a name="lambda-edge-how-it-works-tutorial-verify-proc"></a>

**驗證您的 Lambda@Edge 函數有新增安全標頭**

1. 在瀏覽器中，輸入 S3 儲存貯體中某個檔案的 URL。例如，您可以使用類似以下的 URL：`https://d111111abcdef8.cloudfront.net/image.jpg`。

   如需用於檔案 URL 中 CloudFront 網域名稱的詳細資訊，請參閱[自訂 CloudFront 中檔案的 URL 格式](LinkFormat.md)。

1. 開啟瀏覽器的網頁開發人員工具列。例如，在 Chrome 瀏覽器視窗中，開啟內容 (按一下滑鼠右鍵) 選單，然後選擇 **Inspect (檢查)**。

1. 選擇 **Network (網路)** 索引標籤。

1. 重新載入頁面來查看您的影像，然後選擇左窗格中的 HTTP 請求。您會看到 HTTP 標頭顯示在個別的窗格中。

1. 查看 HTTP 標頭清單，以驗證預期的安全標頭包含在清單中。例如，您可能會看到類似於以下螢幕擷取畫面所示的標頭。  
![\[HTTP 標頭清單，所要的安全標頭已採用突出顯示。\]](http://docs.aws.amazon.com/zh_tw/AmazonCloudFront/latest/DeveloperGuide/images/lambda-at-edge-security-headers-list.png)

如果安全標頭包含在您的標頭清單中，那就太棒了！這表示您已成功建立第一個 Lambda@Edge 函數。如果 CloudFront 傳回錯誤或有其他問題，請繼續下一個步驟來排除問題。

## 步驟 6：排除問題
<a name="lambda-edge-how-it-works-tutorial-troubleshoot"></a>

如果 CloudFront 傳回錯誤或是未依照預期來新增安全標頭，您可以查看 CloudWatch Logs 來調查函數的執行狀況。請務必使用存放在最 AWS 接近執行函數之位置的日誌。

例如，如果您從倫敦檢視檔案，請試著在 CloudWatch 主控台將區域變更為歐洲 (倫敦)。<a name="lambda-edge-how-it-works-tutorial-cloudwatch-proc"></a>

**檢查您 Lambda@Edge 函數的 CloudWatch Logs**

1. 登入 AWS 管理主控台 ，並在 https：//[https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/) 開啟 CloudWatch 主控台。

1. 將 **Region (區域)** 變更為您在瀏覽器中檢視檔案時所顯示的位置。這是函數執行所在的位置。

1. 在左窗格中，選擇 **Logs (日誌)** 來檢視分佈的日誌。

如需詳細資訊，請參閱[使用 Amazon CloudWatch 監控 CloudFront 指標](monitoring-using-cloudwatch.md)。

## 步驟 7：清除範例資源
<a name="lambda-edge-how-it-works-tutorial-cleanup-resources"></a>

如果您僅為本教學課程建立 Amazon S3 儲存貯體和 CloudFront 分佈，請刪除您配置 AWS 的資源，以免再產生費用。刪除 AWS 資源後，您新增的任何內容將無法再使用。

**工作**
+ [刪除 S3 儲存貯體](#lambda-edge-how-it-works-tutorial-delete-bucket) 
+ [刪除 Lambda 函式](#lambda-edge-how-it-works-tutorial-delete-function)
+ [刪除 CloudFront 分佈](#lambda-edge-how-it-works-tutorial-delete-distribution)

### 刪除 S3 儲存貯體
<a name="lambda-edge-how-it-works-tutorial-delete-bucket"></a>

刪除 Amazon S3 儲存貯體之前，請務必停用儲存貯體的記錄。否則，在刪除日誌時， AWS 請繼續將日誌寫入您的儲存貯體。<a name="lambda-edge-how-it-works-tutorial-delete-bucket-proc"></a>

**停用儲存貯體的記錄**

1. 在以下網址開啟 Amazon S3 主控台：[https://console.aws.amazon.com/s3/](https://console.aws.amazon.com/s3/)。

1. 選取您的儲存貯體，然後選擇 **Properties (屬性)**。

1. 從 **Properties (屬性)** 選擇 **Logging (記錄日誌)**。

1. 清除 **Enabled (已啟用)** 核取方塊。

1. 選擇 **Save** (儲存)。

現在即可刪除儲存貯體。如需詳細資訊，請參閱 *Amazon Simple Storage Service 主控台使用者指南*中的[我該如何刪除 S3 儲存貯體？](https://docs.aws.amazon.com/AmazonS3/latest/userguide/delete-bucket.html)。

### 刪除 Lambda 函式
<a name="lambda-edge-how-it-works-tutorial-delete-function"></a>

如需刪除 Lambda 函數關聯和函數本身 (選用) 的說明，請參閱 [刪除 Lambda@Edge 函數和複本](lambda-edge-delete-replicas.md)。

### 刪除 CloudFront 分佈
<a name="lambda-edge-how-it-works-tutorial-delete-distribution"></a>

刪除 CloudFront 分佈前必須先將其停用。已停用的分佈如此即不再有用，也不會產生費用。您隨時都可以啟用之前停用的分佈。刪除停用的分佈之後，它即不再可供使用。<a name="lambda-edge-how-it-works-tutorial-delete-distribution-proc"></a>

**停用和刪除 CloudFront 分佈**

1. 在 [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home) 開啟 CloudFront 主控台。

1. 選取您要停用的分佈，然後選擇 **Disable (停用)**。

1. 出現確認提示時，請選擇 **Yes, Disable (是，停用)**。

1. 選取已停用的分佈，然後選擇 **Delete (刪除)**。

1. 出現確認提示時，選擇 **Yes, Delete (是，刪除)**。

## 相關資訊
<a name="lambda-edge-how-it-works-tutorial-resources"></a>

現在您對於 Lambda@Edge 函數的運作方式已有了基本了解，請閱讀以下內容來進一步了解：
+ [Lambda@Edge 範例函數](lambda-examples.md)
+ [Lambda@Edge 設計最佳實務](https://aws.amazon.com/blogs/networking-and-content-delivery/lambdaedge-design-best-practices/)
+ [使用 Lambda@Edge 減少延遲並轉移運算至邊緣](https://aws.amazon.com/blogs/networking-and-content-delivery/reducing-latency-and-shifting-compute-to-the-edge-with-lambdaedge/)

# 設定 Lambda@Edge 的 IAM 許可權限和角色
<a name="lambda-edge-permissions"></a>

若要設定 Lambda@Edge，您必須具有以下適用於 AWS Lambda的 IAM 許可權限和角色：
+ [IAM 許可權限](#lambda-edge-permissions-required)：這些許可權限可讓您建立 Lambda 函數，並將其與 CloudFront 分佈建立關聯。
+ [Lambda 函數執行角色](#lambda-edge-permissions-function-execution) (IAM 角色) – Lambda 服務主體會擔任此角色來執行您的函數。
+ [Lambda@Edge 的服務連結角色](#using-service-linked-roles-lambda-edge) – 服務連結角色允許特定 AWS 服務 將 Lambda 函數複寫到 ， AWS 區域 並讓 CloudWatch 使用 CloudFront 日誌檔案。

## 將 Lambda@Edge 函數與 CloudFront 分佈產生關聯所需的 IAM 許可
<a name="lambda-edge-permissions-required"></a>

除了 Lambda 需要的 IAM 許可權限之外，IAM 使用者還需要下列 IAM 許可權限，以便將 Lambda 函數與 CloudFront 分佈建立關聯：
+ `lambda:GetFunction`：授予許可權限以取得 Lambda 函數的組態資訊，以及預先簽署的 URL，以下載包含該函數的 `.zip` 檔案。
+ `lambda:EnableReplication*`：授予許可權限至資源政策，讓 Lambda 複寫服務能夠取得函數程式碼和組態。
+ `lambda:DisableReplication*`：授予許可權限至資源政策，以便讓 Lambda 複寫服務可以刪除函數。
**重要**  
您必須在 `lambda:EnableReplication*` 和 `lambda:DisableReplication*` 動作的結尾新增星號 (`*`)。
+ 對於資源，指定要在 CloudFront 事件發生時執行之函數版本 ARN，如以下範例所示：

  `arn:aws:lambda:us-east-1:123456789012:function:TestFunction:2`
+ `iam:CreateServiceLinkedRole`：授予許可權限建立服務連結角色，供 Lambda@Edge 用來複寫 CloudFront 中的 Lambda 函數。第一次設定 Lambda@Edge 之後，會自動為您建立服務連結角色。您不需要將此許可權限新增至使用 Lambda@Edge 的其他分佈。

  
+ `cloudfront:UpdateDistribution` 或 `cloudfront:CreateDistribution`：授予許可權限以更新或建立分佈。

如需詳細資訊，請參閱下列主題：
+ [適用於 Amazon CloudFront 的 Identity and Access Management](security-iam.md)
+ 《*AWS Lambda 開發人員指南*》中的 [Lambda 資源存取許可權限](https://docs.aws.amazon.com/lambda/latest/dg/intro-permission-model.html#lambda-intro-execution-role)

## 服務主體的函數執行角色
<a name="lambda-edge-permissions-function-execution"></a>

您必須建立 `lambda.amazonaws.com` 和 `edgelambda.amazonaws.com` 服務主體可在執行函數時擔任的 IAM 角色。

**提示**  
在 Lambda 主控台中建立函數時，您可以選擇使用 AWS 政策範本建立新的執行角色。此步驟會*自動*新增執行函數所需的 Lambda@Edge 許可權限。請參閱[教學課程：建立簡單 Lambda@Edge 函數中的步驟 5](lambda-edge-how-it-works-tutorial.md#lambda-edge-how-it-works-tutorial-create-function)。

如需有關手動建立 IAM 角色的詳細資訊，請參閱《*IAM 使用者指南*》中的[建立角色及附加政策 (主控台)](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions_create-policies.html)。

**Example 範例：角色信任政策**  
您可在 IAM 主控台的**信任關係**索引標籤之下新增此角色。請勿在**許可權限**索引標籤下新增此政策。    
****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement": [
      {
         "Effect": "Allow",
         "Principal": {
            "Service": [
               "lambda.amazonaws.com",
               "edgelambda.amazonaws.com"
            ]
         },
         "Action": "sts:AssumeRole"
      }
   ]
}
```

如需更多資訊瞭解授予執行角色所需的許可權限，請參閱《*AWS Lambda 開發人員指南*》中的 [Lambda 資源存取許可權限](https://docs.aws.amazon.com/lambda/latest/dg/intro-permission-model.html#lambda-intro-execution-role)。

**備註**  
根據預設，每當 CloudFront 事件觸發 Lambda 函數時，都會將資料寫入至 CloudWatch Logs。若要使用這些日誌，執行角色需要許可才能將資料寫入 CloudWatch Logs。您能使用預先定義的 AWSLambdaBasicExecutionRole 對執行角色授予許可權限。  
如需 CloudWatch Logs 的詳細資訊，請參閱[邊緣函數日誌](edge-functions-logs.md)。
如果您的 Lambda 函數程式碼存取其他 AWS 資源，例如從 S3 儲存貯體讀取物件，則執行角色需要執行該動作的許可。

## Lambda@Edge 的服務連結角色
<a name="using-service-linked-roles-lambda-edge"></a>

Lambda@Edge 使用 IAM [服務連結角色](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_terms-and-concepts.html#iam-term-service-linked-role)。服務連結角色是直接連結至服務的一種特殊 IAM 角色類型。服務連結角色由服務預先定義，並包含該服務在代表您呼叫其他 AWS 服務時，需要用到的所有權限。

Lambda@Edge 使用以下 IAM 服務連結角色：
+ **AWSServiceRoleForLambdaReplicator** – Lambda@Edge 使用此角色讓 Lambda@Edge 將函數複寫至 AWS 區域。

  您初次在 CloudFront 中新增 Lambda@Edge 觸發條件時，將會自動建立名為 AWSServiceRoleForLambdaReplicator 的角色，以便讓 Lambda@Edge 將函數複寫至 AWS 區域。使用 Lambda@Edge 函數時需要這個角色。AWSServiceRoleForLambdaReplicator 角色的 ARN 類似以下範例：

  `arn:aws:iam::123456789012:role/aws-service-role/replicator.lambda.amazonaws.com/AWSServiceRoleForLambdaReplicator`
+ **AWSServiceRoleForCloudFrontLogger**：CloudFront 使用此角色將日誌檔案推送至 CloudWatch。您可以使用日誌檔案來偵錯 Lambda@Edge 驗證錯誤。

  您新增 Lambda@Edge 函數關聯，讓 CloudFront 將 Lambda@Edge 錯誤日誌檔案推送至 CloudWatch 時，會自動建立名為 AWSServiceRoleForCloudFrontLogger 的角色。AWSServiceRoleForCloudFrontLogger 角色的 ARN 看起來類似如下：

  `arn:aws:iam::account_number:role/aws-service-role/logger.cloudfront.amazonaws.com/AWSServiceRoleForCloudFrontLogger`

服務連結角色可讓設定及使用 Lambda@Edge 變得更輕鬆，因為您不必手動新增必要的許可。Lambda@Edge 會定義其服務連結角色的許可，而且只有 Lambda@Edge 能夠擔任此角色。已定義的許可包括信任政策和許可政策。許可政策無法連接到其他任何 IAM 實體。

您必須移除任何關聯的 CloudFront 或 Lambda@Edge 資源，然後才能刪除服務連結角色。這有助於保護您的 Lambda@Edge 資源，讓您不會移除在存取作用中資源時仍有需要的服務連結角色。

如需服務連結角色的詳細資訊，請參閱[CloudFront 的服務連結角色](security_iam_service-with-iam.md#security_iam_service-with-iam-roles-service-linked)。

### Lambda@Edge 的服務連結角色許可
<a name="slr-permissions-lambda-edge"></a>

Lambda@Edge 使用兩個服務連結角色，分別名為 **AWSServiceRoleForLambdaReplicator** 及 **AWSServiceRoleForCloudFrontLogger**。以下章節說明這些角色的許可。

**Contents**
+ [Lambda Replicator 的服務連結角色許可](#slr-permissions-lambda-replicator)
+ [CloudFront Logger 的服務連結角色許可](#slr-permissions-cloudfront-logger)

#### Lambda Replicator 的服務連結角色許可
<a name="slr-permissions-lambda-replicator"></a>

這個服務連結的角色可讓 Lambda 將 Lambda@Edge 函數複製到 AWS 區域。

AWSServiceRoleForLambdaReplicator 服務連結角色信任 `replicator.lambda.amazonaws.com` 服務來擔任該角色。

角色許可政策允許 Lambda@Edge 在指定資源上完成下列動作：
+ `arn:aws:lambda:*:*:function:*` 的 `lambda:CreateFunction`
+ `arn:aws:lambda:*:*:function:*` 的 `lambda:DeleteFunction`
+ `arn:aws:lambda:*:*:function:*` 的 `lambda:DisableReplication`
+ `all AWS resources` 的 `iam:PassRole`
+  `all AWS resources` 的 `cloudfront:ListDistributionsByLambdaFunction`

#### CloudFront Logger 的服務連結角色許可
<a name="slr-permissions-cloudfront-logger"></a>

此服務連結角色可讓 CloudFront 將日誌檔案推送至您的 CloudWatch，以讓您偵錯 Lambda@Edge 驗證錯誤。

AWSServiceRoleForCloudFrontLogger 服務連結角色信任 `logger.cloudfront.amazonaws.com` 服務來擔任該角色。

角色許可政策允許 Lambda@Edge 在指定 `arn:aws:logs:*:*:log-group:/aws/cloudfront/*` 資源上完成下列動作：
+ `logs:CreateLogGroup` ``
+ `logs:CreateLogStream`
+ `logs:PutLogEvents`

您必須設定許可，允許 IAM 實體 (例如使用者、群組或角色) 刪除 Lambda@Edge 服務連結角色。如需詳細資訊，請參閱 *IAM 使用者指南*中的[服務連結角色許可](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html#service-linked-role-permissions)。

### 建立 Lambda@Edge 的服務連結角色
<a name="create-slr-lambda-edge"></a>

一般而言，您不需要手動建立 Lambda@Edge 的服務連結角色。此服務會在以下情境為您自動建立角色：
+ 您第一次建立觸發條件時，服務會建立 AWSServiceRoleForLambdaReplicator 角色 (如果尚未存在)。這個角色可讓 Lambda 將 Lambda@Edge 函數複寫到 AWS 區域。

  如果您刪除服務連結角色，則當您在分佈中為 Lambda@Edge 新增觸發條件時，將會重新建立此角色。
+ 您更新或建立具有 Lambda@Edge 關聯的 CloudFront 分佈時，服務會建立一個 AWSServiceRoleForCloudFrontLogger 角色 (如果該角色尚不存在)。此角色可讓 CloudFront 將日誌檔案推送至 CloudWatch。

  如果您刪除服務連結角色，當您更新或建立具有 Lambda@Edge 關聯的 CloudFront 分佈時，將會再次建立此角色。

若要手動建立這些服務連結角色，您可以執行下列 AWS Command Line Interface (AWS CLI) 命令：

**建立 AWSServiceRoleForLambdaReplicator 角色**
+ 執行下列命令。

  ```
  aws iam create-service-linked-role --aws-service-name replicator.lambda.amazonaws.com
  ```

**建立 AWSServiceRoleForCloudFrontLogger 角色**
+ 執行下列命令。

  ```
  aws iam create-service-linked-role --aws-service-name logger.cloudfront.amazonaws.com
  ```

### 編輯 Lambda@Edge 服務連結角色
<a name="edit-slr-lambda-edge"></a>

Lambda@Edge 不允許您編輯 AWSServiceRoleForLambdaReplicator 或 AWSServiceRoleForCloudFrontLogger 服務連結角色。在服務建立服務連結角色之後，您將無法變更角色名稱，因為各種實體皆可能會參考角色。不過，您可以使用 IAM 來編輯角色描述。如需詳細資訊，請參閱《*IAM 使用者指南*》中的[編輯服務連結角色](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html#edit-service-linked-role)。

### AWS 區域 支援 Lambda@Edge 服務連結角色
<a name="slr-regions-lambda-edge"></a>

CloudFront 在下列 AWS 區域中支援將服務連結角色用於 Lambda@Edge：
+ 美國東部 (維吉尼亞北部) – `us-east-1`
+ 美國東部 (俄亥俄) – `us-east-2`
+ 美國西部 (加利佛尼亞北部) – `us-west-1`
+ 美國西部 (奧勒岡) – `us-west-2`
+ 亞太地區 (孟買) – `ap-south-1`
+ 亞太地區 (首爾) – `ap-northeast-2`
+ 亞太地區 (新加坡) – `ap-southeast-1`
+ 亞太地區 (雪梨) – `ap-southeast-2`
+ 亞太地區 (東京) – `ap-northeast-1`
+ 歐洲 (法蘭克福) – `eu-central-1`
+ 歐洲 (愛爾蘭) – `eu-west-1`
+ 歐洲 (倫敦) – `eu-west-2`
+ 南美洲 (聖保羅) – `sa-east-1`

# 撰寫及建立 Lambda@Edge 函數
<a name="lambda-edge-create-function"></a>

若要使用 Lambda@Edge，請*撰寫* AWS Lambda 函數的程式碼。為了協助您撰寫 Lambda@Edge 函數，請參閱下列資源：
+  [Lambda@Edge 事件結構說明頁面](lambda-event-structure.md)：瞭解與 Lambda@Edge 搭配使用的事件結構。
+ [Lambda@Edge 範例函數](lambda-examples.md)：範例函數，例如 A/B 測試和產生 HTTP 重新導向。

將 Node.js 或 Python 用於 Lambda@Edge 的程式設計模型和在 AWS 區域中使用 Lambda 的程式設計模型相同。如需詳細資訊，請參閱《*AWS Lambda 開發人員指南*》中的[使用 Node.js 建構 Lambda 函數](https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html)或[使用 Python 建構 Lambda 函數](https://docs.aws.amazon.com/lambda/latest/dg/lambda-python.html)。

在您的 Lambda@Edge 函數中，包含 `callback` 參數並傳回請求或回應事件的適用物件：
+ **請求事件** – 在回應中包含 `cf.request` 物件。

  如果您要產生回應，請在回應中包含 `cf.response` 物件。如需詳細資訊，請參閱[在請求觸發條件中產生 HTTP 回應](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests)。
+ **回應事件** – 在回應中包含 `cf.response` 物件。

在您撰寫自己的程式碼或使用其中一個範例後，接著在 Lambda 中建立函數。若要建立函數或編輯現有函數，請參閱下列主題：

**Topics**
+ [建立 Lambda@Edge 函數](lambda-edge-create-in-lambda-console.md)
+ [編輯 Lambda 函數](lambda-edge-edit-function.md)

 在 Lambda 中建立函數後，您可以設定 Lambda 根據稱為*觸發*的特定 CloudFront 事件執行函數。如需詳細資訊，請參閱[為 Lambda@Edge 函數新增觸發條件](lambda-edge-add-triggers.md)。

# 建立 Lambda@Edge 函數
<a name="lambda-edge-create-in-lambda-console"></a>

若要設定 AWS Lambda 以執行以 CloudFront 事件為基礎的 Lambda 函數，請遵循此程序。<a name="lambda-edge-create-function-procedure"></a>

**建立 Lambda@Edge 函數**

1. 登入 AWS 管理主控台 並在 https：//[https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/) 開啟 AWS Lambda 主控台。

1. 如果您已擁有一個或多個 Lambda 函數，請選擇 **Create function (建立函數)**。

   如果您未擁有任何函數，請選擇 **Get Started Now (立即開始)**。

1. 在頁面頂端的「區域」清單中，選擇**美國東部 (維吉尼亞北部)**。

1. 使用您自己的程式碼建立函數，或以 CloudFront 藍圖為基礎建立函數。
   + 若要使用自己的程式碼來建立函數，請選擇 **Author from scratch (從頭開始編寫)**。
   + 若要顯示 CloudFront 藍圖清單，請在篩選條件欄位輸入 **cloudfront**，然後選擇 **Enter**。

     如果找到想要使用的藍圖，請選擇該藍圖名稱。

1. 在 **Basic information (基本資訊)** 區段中，指定下列的值：

   1. **名稱**：輸入函數名稱。

   1. **角色**：若要快速開始使用，請選擇**從範本建立新角色**。您也可以選擇**選擇現有角色**或**建立自訂角色**，然後依照提示完成本區段的資訊。

   1. **角色名稱**：輸入角色名稱。

   1. **政策範本**：選擇**基本邊緣 Lambda 許可權限**。

1. 如果您在步驟 4 中選擇 **Author from scratch (從頭開始編寫)**，請跳到步驟 7。

   如果您在步驟 4 選擇藍圖，則可於 **cloudfront** 區段建立一個觸發，其可以將此函數與 CloudFront 分佈和 CloudFront 事件中的快取相關聯。我們建議您在此處選擇 **Remove (移除)**，如此函數在建立時就不會有觸發條件。您可以在稍後新增觸發。
**提示**  
建議在新增觸發條件之前，先對函數進行測試及除錯。如果您現在新增觸發條件，函式會在您建立函式並完成複寫至世界各地的 AWS 位置，並部署對應的分佈時立即執行。

1. 選擇 **Create function (建立函數)**。

   Lambda 會建立兩個版本的函數：\$1LATEST 和 Version 1。您只能編輯 \$1LATEST 版本，但是主控台最初會顯示 Version 1。

1. 若要編輯函數，請選擇該函數 ARN 下方、靠近頁面頂端的 **Version 1 (版本 1)**。接著，在 **Versions (版本)** 索引標籤中，選擇 **\$1LATEST (\$1LATEST)**。(如果離開函數再返回，按鈕的標籤會是 **Qualifiers (修飾詞)**。)

1. 在 **Configuration (組態)** 索引標籤中，選擇適用的 **Code entry type (程式碼項目類型)**。然後遵循提示來編輯或上傳程式碼。

1. 針對 **Runtime (執行時間)**，根據函數的程式碼來選擇值。

1. 在 **Tags (標籤)** 區段中，新增任何適用的標籤。

1. 選擇 **Actions (動作)**，然後選擇 **Publish new version (發佈新版本)**。

1. 輸入函數新版本的說明。

1. 選擇 **Publish (發佈)**。

1. 對函數進行測試與偵錯。如需有關在 Lambda 主控台進行測試的詳細資訊，請參閱《*AWS Lambda 開發人員指南*》中的[使用主控台調用 Lambda 函數](https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html#get-started-invoke-manually)。

1. 當您準備好將函數用於 CloudFront 事件執行時，請發佈另一個版本，並編輯該函數以新增觸發。如需詳細資訊，請參閱[為 Lambda@Edge 函數新增觸發條件](lambda-edge-add-triggers.md)。

# 編輯 Lambda 函數
<a name="lambda-edge-edit-function"></a>

建立 Lambda@Edge 函數之後，您可以使用 Lambda 主控台編輯該函數。

**備註**  
原始版本標記為 \$1LATEST。
您只能編輯 \$1LATEST 版本。
每次您編輯 \$1LATEST 版本時，必須發佈新的編號版本。
您無法為 \$1LATEST 建立觸發。
當您發佈新函數版本時，Lambda 不會自動將觸發條件由前一個版本複製到新版本。您必須為新版本重新產生觸發。
當您新增 CloudFront 事件觸發條件到函數時，如果針對相同分佈、快取行為及事件之較早版本的相同函數已具有觸發條件，則 Lambda 會刪除較早版本的觸發條件。
在您對 CloudFront 分佈進行例如新增觸發等更新後，必須等待變更傳播到節點，您由觸發條件所指定的函數才可運作。<a name="lambda-edge-edit-function-procedure"></a>

**若要編輯 Lambda 函數**

1. 登入 AWS 管理主控台 並在 https：//[https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/) 開啟 AWS Lambda 主控台。

1. 在頁面頂端的「區域」清單中，選擇**美國東部 (維吉尼亞北部)**。

1. 在函數清單上選取函數名稱。

   根據預設，主控台會顯示 \$1LATEST 版本。您可以檢視較早的版本 (選擇 **Qualifiers (修飾詞)**)，但是只能編輯 \$1LATEST。

1. 在**程式碼**索引標籤上，針對**程式碼項目類型**，選擇在瀏覽器中編輯程式碼、上傳 .zip 檔案，或從 Amazon S3 上傳檔案。

1. 選擇 **Save (儲存)** 或 **Save and test (儲存並測試)**。

1. 選擇 **Actions (動作)**，然後選擇 **Publish new version (發佈新版本)**。

1. 在 **Publish new version from \$1LATEST (從 \$1LATEST 發佈新版本)** 對話方塊中，輸入新版本的說明。此說明會與自動產生的版本編號一起顯示在版本清單中。

1. 選擇 **Publish (發佈)**。

   新版本會自動成為最新版本。版本編號會顯示於頁面左上角的**版本**按鈕上。
**注意**  
如果您尚未為函數新增觸發條件，請參閱 [為 Lambda@Edge 函數新增觸發條件](lambda-edge-add-triggers.md)。

1. 選擇 **Triggers (觸發條件)** 索引標籤。

1. 選擇 **Add trigger (新增觸發條件)**。

1. 在**新增觸發**對話方塊，選擇虛線方塊，再選擇 **CloudFront**。
**注意**  
如果您已為函數建立一或多個觸發條件，則 CloudFront 為預設服務。

1. 指定下列值，以指示您希望 Lambda 函數在何時執行。

   1. **分佈 ID**：選擇您要為其新增觸發的分佈 ID。

   1. **快取行為**：選擇快取行為，透過該行為指定您要執行函數的物件。

   1. **CloudFront 事件**：選擇促使函數執行的 CloudFront 事件。

   1. **啟用觸發條件及複寫**：選取此核取方塊，讓 Lambda 將函數複寫到全球的 AWS 區域 。

1. 選擇**提交**。

1. 若要為此函數新增更多觸發，請重複操作步驟 10 到 13。

如需更多資訊瞭解在 Lambda 主控台中測試和偵錯函數，請參閱《*AWS Lambda 開發人員指南*》中的[使用主控台調用 Lambda 函數](https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html#get-started-invoke-manually)。

當您準備好將函數用於 CloudFront 事件執行時，請發佈另一個版本，並編輯該函數以新增觸發。如需詳細資訊，請參閱[為 Lambda@Edge 函數新增觸發條件](lambda-edge-add-triggers.md)。

# 為 Lambda@Edge 函數新增觸發條件
<a name="lambda-edge-add-triggers"></a>

Lambda@Edge 觸發器是指 CloudFront 分佈、快取行為及促使函數執行的事件的組合。例如，您可以建立觸發條件，在 CloudFront 收到檢視器針對您為分佈設定的特定快取行為提出的請求時，執行函數。您可以指定一或多個 CloudFront 觸發條件。

**提示**  
當您建立 CloudFront 分佈，您需要指定設定來告知 CloudFront 如何在接收不同的請求時回應。預設設定稱為分佈的*預設快取行為*。您可以設定其他快取行為來定義 CloudFront 在特定情況下回應的方式，例如在接收請求特定的檔案類型的請求時。如需詳細資訊，請參閱[快取行為設定](DownloadDistValuesCacheBehavior.md)。

您首次建立 Lambda 函數時，只能指定*一個*觸發條件。您之後可以使用以下方式將更多觸發條件新增到相同函數：使用 Lambda 主控台或在 CloudFront 主控台上編輯分佈。
+ 如果您要將更多觸發條件新增到同一 CloudFront 分佈的函數中，Lambda 主控台將非常有效。
+ 如果要為多個分佈新增觸發條件，使用 CloudFront 主控台較適合，因為這樣更易於找到您要更新的分佈。您也可以同時更新其他的 CloudFront 設定。

**Topics**
+ [可以觸發 Lambda@Edge 函數的 CloudFront 事件](lambda-cloudfront-trigger-events.md)
+ [選擇要觸發函數的事件](lambda-how-to-choose-event.md)
+ [新增觸發條件到 Lambda@Edge 函數](lambda-edge-add-triggers-console.md)

# 可以觸發 Lambda@Edge 函數的 CloudFront 事件
<a name="lambda-cloudfront-trigger-events"></a>

對於 Amazon CloudFront 分佈中的每個快取行為，您可以新增最多四個觸發條件 (關聯)，以便在特定 CloudFront 事件發生時執行 Lambda 函數。CloudFront 觸發條件可以根據四個 CloudFront 事件之一，如下圖所示。

![\[概念圖：針對與 CloudFront 整合的 Lambda 函數，顯示 CloudFront 如何觸發事件。\]](http://docs.aws.amazon.com/zh_tw/AmazonCloudFront/latest/DeveloperGuide/images/cloudfront-events-that-trigger-lambda-functions.png)


可用於觸發 Lambda@Edge 函數的 CloudFront 事件如下：

**檢視者請求**  
函數會在 CloudFront 接獲檢視器請求時執行，然後再檢視請求的物件是否位在 CloudFront 快取。  
函數不在以下情況執行：  
+ 擷取自訂錯誤頁面時。
+ CloudFront 自動重新導向 HTTP 請求到 HTTPS ([檢視器通訊協定政策](DownloadDistValuesCacheBehavior.md#DownloadDistValuesViewerProtocolPolicy) 的值為**重新導向 HTTP 到 HTTPS** 時)。

**原始伺服器請求**  
函數*只*在 CloudFront 將請求轉傳到您的原始伺服器時執行。如果請求的物件在 CloudFront 快取中，則不會執行該函數。

**原始伺服器回應**  
函數會在 CloudFront 接獲原始伺服器回應後執行，然後才快取回應中的物件。請注意，即使原始伺服器傳回錯誤，函數仍會執行。  
函數不在以下情況執行：  
+ 當請求的檔案在 CloudFront 快取中，並且未過期。
+ 當回應是從原始伺服器請求事件觸發的函數所產生。

**檢視者回應**  
函數會在請求的檔案傳回給檢視器之前執行。請注意，無論檔案是否已在 CloudFront 快取中，函數皆會執行。  
函數不在以下情況執行：  
+ 當原始伺服器傳回 HTTP 狀態碼 400 或更高版本。
+ 當傳回自訂錯誤頁面。
+ 當回應是被檢視器請求事件觸發的函數所產生。
+ CloudFront 自動重新導向 HTTP 請求到 HTTPS ([檢視器通訊協定政策](DownloadDistValuesCacheBehavior.md#DownloadDistValuesViewerProtocolPolicy) 的值為**重新導向 HTTP 到 HTTPS** 時)。

當您新增多個觸發條件至相同的快取行為時，可將其用於針對每個觸發條件執行相同或不同的函數。您也可以將相同的函數與一個以上的分佈建立關聯。

**注意**  
CloudFront 事件觸發執行 Lambda 函數時，該函數必須*先*完成，CloudFront 才能繼續執行。  
例如，當 Lambda 函數是由 CloudFront 檢視器請求事件觸發時，CloudFront 在 Lambda 函數執行完畢之前不會將回應傳回給檢視器或轉送請求給原始伺服器。  
這表示觸發 Lambda 函數的每個請求都會增加請求延遲，因此您會希望函數執行速度越快越好。

# 選擇要觸發函數的事件
<a name="lambda-how-to-choose-event"></a>

當您決定要使用哪個 CloudFront 事件來觸發 Lambda 函數，請考慮以下資訊：

**我希望 CloudFront 快取由 Lambda 函數更改的物件**  
如果要快取由 Lambda 函數修改的物件，以便讓 CloudFront 在下次收到請求時能夠從邊緣節點提供物件，請使用*原始伺服器請求*或*原始伺服器回應*事件。  
這樣可以降低原始伺服器的負載，減少後續請求的延遲，並降低叫用 Lambda@Edge 在後續請求的費用。  
例如，如果您想要新增、移除或變更傳回原始伺服器的物件標頭，且您希望 CloudFront 快取結果，請使用原始伺服器的回應事件。

**我希望函數執行每個請求**  
如果要讓函數執行每個 CloudFront 收到用於分佈的請求，請使用*檢視器請求*或*檢視器回應*事件。  
原始伺服器請求與原始伺服器回應事件，僅會在當請求的物件未快取於節點以及 CloudFront 轉送請求至原始伺服器時發生。

**我希望函數變更快取金鑰**  
如欲變更快取基礎值，請使用*檢視器請求*事件。  
例如，若函數變更其 URL 以包含語言縮寫在路徑中 (例如，因為使用者從下拉式清單中選擇其語言)，請使用檢視器請求事件：  
+ **檢視器請求中的 URL** – https：//https://example.com/en/index.html
+ **當請求來自德國境內 IP 位址時的 URL** – https://example.com/de/index.html
如果您快取根據 Cookie 或請求標頭，也可以使用檢視器請求事件。  
如果函數變更 Cookie 或標頭，可設定 CloudFront 轉送請求適用的部分到原始伺服器。如需詳細資訊，請參閱下列主題：  
+ [根據 Cookie 快取內容](Cookies.md)
+ [根據請求標頭快取內容](header-caching.md)

**函數會影響原始伺服器回應**  
如果您希望依影響原始伺服器回應的方法變更請求，請使用*原始伺服器請求*事件。  
一般而言，大多數檢視器請求事件不會轉送至原始伺服器。CloudFront 會使用已在邊緣快取中的物件回應請求。如果函數依原始伺服器請求事件變更請求，CloudFront 會快取對變更的原始伺服器請求的回應。

# 新增觸發條件到 Lambda@Edge 函數
<a name="lambda-edge-add-triggers-console"></a>

您可以使用 AWS Lambda 主控台或 Amazon CloudFront 主控台，將觸發條件新增至 Lambda@Edge 函數。

**重要**  
您只能為編號的函數版本 (而非 **\$1LATEST**) 建立觸發條件。

------
#### [ Lambda console ]<a name="lambda-edge-add-triggers-procedure"></a>

**將 CloudFront 事件的觸發條件新增至 Lambda@Edge 函數**

1. 登入 AWS 管理主控台 並在 https：//[https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/) 開啟 AWS Lambda 主控台。

1. 在頁面頂端的「區域」清單中，選擇**美國東部 (維吉尼亞北部)**。

1. 在 **Functions (函數)** 頁面上，選擇您要為其新增觸發條件的函數名稱。

1. 在**函數概觀**頁面上，選擇**版本**索引標籤。

1. 選擇您要為其新增觸發的版本。

   選擇版本之後，按鈕的名稱會變更為 **Version: \$1LATEST (版本：\$1LATEST)** 或 **Version: (版本：)** *版本編號*。

1. 選擇 **Triggers (觸發條件)** 索引標籤。

1. 選擇 **Add trigger (新增觸發條件)**。

1. 針對**觸發組態**選擇**選取來源**，輸入 **cloudfront**，然後選擇 **CloudFront**。
**注意**  
如果您已經建立一或多個觸發，則 CloudFront 為預設服務。

1. 指定下列值，以指示您希望 Lambda 函數在何時執行。

   1. **分佈**：選擇您要為其新增觸發的分佈。

   1. **快取行為**：選擇快取行為，透過該行為指定您要執行函數的物件。
**注意**  
如果您為快取行為指定 `*`，Lambda 函式則會部署至預設的快取行為。

   1. **CloudFront 事件**：選擇促使函數執行的 CloudFront 事件。

   1. **包含本文**：如果想要存取您函數中的請求本體，請勾選此核取方塊。

   1. **確認部署到 Lambda@Edge**：選取此核取方塊，以便讓 AWS Lambda 將函數複寫至全球 AWS 區域 。

1. 選擇**新增**。

   將已更新的 CloudFront 分佈部署時，函數便開始針對指定的 CloudFront 事件處理請求。若要判斷是否已部署分佈，請在導覽窗格中選擇 **Distributions (分佈)**。部署分佈時，該分佈**狀態**欄中的值，會從**正在部署**變更為部署的日期與時間。

------
#### [ CloudFront console ]<a name="lambda-create-functions-add-triggers-cloudfront-console-procedure"></a>

**將 CloudFront 事件的觸發條件新增至 Lambda@Edge 函數**

1. 取得您希望新增觸發的 Lambda 函數的 ARN：

   1. 登入 AWS 管理主控台 並在 https：//[https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/) 開啟 AWS Lambda 主控台。

   1. 在頁面頂端的區域清單中，選擇**美國東部 (維吉尼亞北部)**。

   1. 在函數清單上，選取您想要為其新增觸發的函數名稱。

   1. 在**函數概觀**頁面選擇**版本**索引標籤，然後選擇您想要為其新增觸發條件的編號版本。

   1. 選擇**複製 ARN** 按鈕，將 ARN 複製到剪貼簿。Lambda 函數的 ARN 如下所示：

      `arn:aws:lambda:us-east-1:123456789012:function:TestFunction:2`

      最後面的數字 (此範例中為 **2 (2)**) 是該函數的版本編號。

1. 在 [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home) 開啟 CloudFront 主控台。

1. 在分佈清單中，選取您想要為其新增觸發的分佈 ID。

1. 選擇 **Behaviors (動作)** 索引標籤。

1. 選擇您想要新增觸發條件的快取動作，然後選擇**編輯**。

1. 對於**函數關聯**，請在**函數類型**清單中選擇 **Lambda@Edge**，作為您希望函數執行的時間點：針對檢視器請求、檢視器回應、原始伺服器請求或原始伺服器回應。

   如需詳細資訊，請參閱[選擇要觸發函數的事件](lambda-how-to-choose-event.md)。

1. 在**函數 ARN / 名稱**文字方塊中，貼上所選事件發生時您想要執行 Lambda 函數的 ARN。這是您從 Lambda 主控台複製的值。

1. 如果想要存取您函數中的請求本文，請勾選**包含本文**。

   如果您只想要替換請求本體，就不需要選取此選項。

1. 若要針對更多事件類型執行同一個函數，請重複步驟 6 和 7。

1. 選擇**儲存變更**。

1. 若要為此分佈新增更多快取行為的觸發，請重複步驟 5 到 10。

   將已更新的 CloudFront 分佈部署時，函數便開始針對指定的 CloudFront 事件處理請求。若要判斷是否已部署分佈，請在導覽窗格中選擇 **Distributions (分佈)**。部署分佈時，該分佈**狀態**欄中的值，會從**正在部署**變更為部署的時間與日期。

------

# 測試和偵錯 Lambda@Edge 函數
<a name="lambda-edge-testing-debugging"></a>

單獨測試您 Lambda@Edge 函數的程式碼，和進行整合測試，這些動作非常地重要，前者可確保函數能夠完成預期的任務，後者則可確保函數能夠正確地搭配 CloudFront 使用。

在整合測試期間，或是在已經部署函數之後，您可能會需要進行 CloudFront 錯誤的偵錯，例如 HTTP 5xx 錯誤。錯誤可能是從 Lambda 函數傳回的無效回應、觸發函數時的執行錯誤，或是由於 Lambda 服務進行調節所產生的錯誤。本主題中的段落，說明了用來判斷是哪些故障類型造成問題的策略，以及您可採取的問題修正步驟。

**注意**  
當您在疑難排解錯誤時檢閱 CloudWatch 日誌檔案或指標時，請注意這些檔案或指標會顯示在最 AWS 區域 接近函數執行位置的位置。因此，舉例來說，如果您擁有網站或 Web 應用程式，其使用者位於英國，而您擁有與分佈相關聯的 Lambda 函數，則您必須變更區域，才能檢視倫敦 AWS 區域的 CloudWatch 指標或日誌檔案。如需詳細資訊，請參閱[判斷 Lambda@Edge 區域](#lambda-edge-testing-debugging-determine-region)。

**Topics**
+ [測試您的 Lambda@Edge 函數](#lambda-edge-testing-debugging-test-function)
+ [在 CloudFront 中識別 Lambda@Edge 函數錯誤](#lambda-edge-identifying-function-errors)
+ [針對無效的 Lambda@Edge 函數回應 (驗證錯誤) 進行疑難排解](#lambda-edge-testing-debugging-troubleshooting-invalid-responses)
+ [進行 Lambda@Edge 函數執行錯誤的疑難排解](#lambda-edge-testing-debugging-execution-errors)
+ [判斷 Lambda@Edge 區域](#lambda-edge-testing-debugging-determine-region)
+ [判斷您的帳戶是否推送日誌到 CloudWatch](#lambda-edge-testing-debugging-cloudwatch-logs-enabled)

## 測試您的 Lambda@Edge 函數
<a name="lambda-edge-testing-debugging-test-function"></a>

測試您的 Lambda 函數包括兩個步驟：獨立測試和整合測試。

**測試獨立的功能**  
在將您的 Lambda 函數新增到 CloudFront 之前，請務必先使用 CloudFront 主控台中的測試函數，或利用其他的方法，來進行函數的測試。如需有關在 Lambda 主控台進行測試的詳細資訊，請參閱《*AWS Lambda 開發人員指南*》中的[使用主控台調用 Lambda 函數](https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html#get-started-invoke-manually)。

**在 CloudFront 中測試函數的運作**  
完成整合測試非常地重要，這項測試會建立您的函數與分佈的關聯，並根據 CloudFront 事件執行。請務必以正確的事件觸發函數，並傳回對 CloudFront 有效且正確的回應。例如請確定事件的結構正確、只包含有效的標頭等，以此類推。  
當您在 Lambda 主控台中，重複地進行函數的整合測試時，請參閱 Lambda@Edge 教學課程中的步驟，以修改程式碼，或變更呼叫函數的 CloudFront 觸發條件。例如，請確定您使用函式的編號版本，如教學課程的這項步驟中所述：[步驟 4：新增一個 CloudFront 觸發條件來執行該函數](lambda-edge-how-it-works-tutorial.md#lambda-edge-how-it-works-tutorial-add-trigger)。  
在變更和部署函數時，請注意，更新的函數與 CloudFront 觸發條件複寫到所有區域時，將需要幾分鐘的時間。這通常需要幾分鐘的時間，但最多可能需要 15 分鐘。  
若要檢查複寫是否已完成，請移至 CloudFront主控台並檢視您的分佈。  

**如欲檢查您的複寫是否已完成部署**

1. 在 [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home) 中開啟 CloudFront 主控台。

1. 選擇分佈名稱。

1. 檢查分佈的狀態是否已從 **In Progress (進行中)** 變回 **Deployed (已部署)**，這表示您的函數已複寫完成。接著，請依照下一節的步驟來驗證函數是否正常運作。
請注意，在主控台中進行的測試只會驗證您的函數邏輯，並不會套用 Lambda@Edge 特定的服務配額 (先前稱為限制)。

## 在 CloudFront 中識別 Lambda@Edge 函數錯誤
<a name="lambda-edge-identifying-function-errors"></a>

確認您的函數邏輯可正常運作之後，當函數在 CloudFront 中執行時，仍然可能會出現 HTTP 5xx 錯誤。有各種原因可能傳回 HTTP 5xx 錯誤，包括 Lambda 函數錯誤或 CloudFront 中的其他問題。
+ 如果您使用 Lambda@Edge 函數，您可以使用 CloudFront 主控台中的圖形以協助追蹤造成錯誤的原因，然後試圖修正該錯誤。例如，您可以檢視 HTTP 5xx 錯誤是否由 CloudFront 或 Lambda 函數造成，然後針對特定函數檢視相關的日誌檔案以調查該問題。
+ 若要排除 CloudFront 中的一般性 HTTP 錯誤，請參閱下列主題中的故障診斷步驟：[對 CloudFront 中的錯誤回應狀態碼進行疑難排解](troubleshooting-response-errors.md)。

### 導致 CloudFront 中 Lambda@Edge 函數錯誤的原因
<a name="lambda-edge-testing-debugging-function-errors"></a>

Lambda 函數造成 HTTP 5xx 錯誤的原因有許多種，您應該依據錯誤的類型採取相應的疑難排解步驟。錯誤分類如下：

**Lambda 函數執行錯誤**  
如果因為函數中存在未處理的例外狀況，或程式碼中出現錯誤，而使得 CloudFront 未獲得 Lambda 的回應，會造成執行錯誤。例如，如果程式碼包含回呼 (錯誤)。

**無效的 Lambda 函數回應會傳回至 CloudFront**  
函數執行後，CloudFront 會收到來自 Lambda 的回應。如果回應的物件結構不符合 [Lambda@Edge 事件結構說明頁面](lambda-event-structure.md)，或回應中包含無效的標頭或其他無效的欄位，系統會傳回錯誤。

**由於 Lambda 服務配額 (以前稱為限制)，CloudFront 中的執行會受到調節**  
Lambda 服務會在各區域中調節執行作業，並在您超出配額時傳回錯誤。如需詳細資訊，請參閱[Lambda@Edge 的配額](cloudfront-limits.md#limits-lambda-at-edge)。

### 如何判斷故障的類型
<a name="lambda-edge-testing-debugging-failure-type"></a>

為了在您偵錯和解決 CloudFront 傳回的錯誤時協助您決定處理重點，找出為何 CloudFront 會傳回 HTTP 錯誤的原因是很有幫助的。若要開始使用，您可以使用 AWS 管理主控台上 CloudFront 主控台的 **Monitoring** (監控) 區段中提供的圖表。如需在 CloudFront 主控台的**監控**區段檢視圖表的詳細資訊，請參閱[使用 Amazon CloudWatch 監控 CloudFront 指標](monitoring-using-cloudwatch.md)。

以下圖表在您追縱原始伺服器或 Lambda 函數是否傳回錯誤時特別實用，當錯誤是由於 Lambda 函數造成時，也可縮小問題的類型。

**錯誤率圖表**  
在每一個分佈的 **Overview (概觀)** 標籤上，您可以檢視的其中一個圖表就是 **Error rates (錯誤率)** 圖表。此圖表顯示錯誤率佔進入您分配的請求總數的百分比。此圖表顯示總錯誤率，總共 4xx 個錯誤、總共 5xx 個錯誤，以及總共 5xx 個 Lambda 函數的錯誤。根據錯誤類型和磁碟區，您可以採取步驟以針對原因進行調查和故障診斷。  

![\[CloudFront 分佈的錯誤率圖形\]](http://docs.aws.amazon.com/zh_tw/AmazonCloudFront/latest/DeveloperGuide/images/Distribution-error-rate-pct-full.png)

+ 如果您看到 Lambda 錯誤，您可以透過查看該函數傳回的特定錯誤類型，以進一步進行調查。**Lambda@Edge 錯誤**標籤包含了依類型分類的函數錯誤圖表，可協助您找出特定函數的問題。
+ 如果您看到 CloudFront 錯誤，您可以進行故障排除並解決原始伺服器錯誤或變更您的 CloudFront 組態。如需詳細資訊，請參閱[對 CloudFront 中的錯誤回應狀態碼進行疑難排解](troubleshooting-response-errors.md)。

**執行錯誤和無效函數回應圖表**  
**Lambda@Edge 錯誤**標籤包含針對特定分佈 (依類型) 分類 Lambda@Edge 錯誤的圖表。例如一個圖表會依 AWS 區域顯示所有執行錯誤。  
為了讓您更輕鬆地對問題進行故障排除，您可以依區域開啟和檢驗特定函數的日誌檔案，以尋找特定問題。  

**依區域檢視特定函數的日誌檔案**

1. 在 **Lambda@Edge 錯誤**索引標籤的**關聯 Lambda@Edge 函數**下，選擇函數名稱，然後選擇**檢視指標**。

1. 接著在出現您函數名稱的頁面上，於右上角選擇**檢視函數日誌**，然後選擇區域。

   例如，如果您在美國西部 (奧勒岡) 區域的**錯誤**圖表中看到問題，請從下拉式清單選擇該區域。隨即開啟 Amazon CloudWatch 主控台。

1. 在該區域 CloudWatch 主控台的**日誌串流**下，選擇日誌串流以檢視函數事件。
此外，請閱讀此章的下列各節，以了解有關故障排除和修復錯誤的更多建議。

**調節圖表**  
**Lambda@Edge 錯誤**標籤也包含**調節**圖表。有時，如果您到達區域並行數量配額 (先前稱為限制)，則 Lambda 服務會依每一區域為基礎調節您的函數呼叫。如果出現超過限制錯誤，表示您的函數已到達 Lambda 服務對「區域」中的執行作業所施加的配額。如需詳細資訊，包括如何請求提高配額，請參閱[Lambda@Edge 的配額](cloudfront-limits.md#limits-lambda-at-edge)。  

![\[Lambda@Edge 函數執行的限流圖表。\]](http://docs.aws.amazon.com/zh_tw/AmazonCloudFront/latest/DeveloperGuide/images/Lambda-throttles-page.png)


如需如何使用此資訊進行 HTTP 錯誤故障診斷的詳細資訊，請參閱[在 AWS上針對您的內容交付執行偵錯的四個步驟](https://aws.amazon.com/blogs/networking-and-content-delivery/four-steps-for-debugging-your-content-delivery-on-aws/)。

## 針對無效的 Lambda@Edge 函數回應 (驗證錯誤) 進行疑難排解
<a name="lambda-edge-testing-debugging-troubleshooting-invalid-responses"></a>

如果您找到的問題是 Lambda 驗證錯誤，表示您的 Lambda 函數將無效的回應傳回給 CloudFront。請依照本段落中的指引，採取步驟來審視您的函數，並確認回應符合 CloudFront 的請求。

CloudFront 會以兩種方式驗證來自 Lambda 函數的回應：
+ **Lambda 回應必須符合所請求的物件結構。**錯誤的物件結構範例包括：無法剖析的 JSON、遺漏必要的欄位，以及在回應中包含無效的物件。如需更多資訊，請參閱[Lambda@Edge 事件結構說明頁面](lambda-event-structure.md)。
+ **回應必須只包含有效的物件值。**如果回應中包含有效的物件，但是具有不支援的值，將會發生錯誤。此種情況的範例包括：新增或更新被列入不允許或唯讀的標頭 (請參閱 [對邊緣函數的限制](edge-functions-restrictions.md))、超過內文大小的上限 (請參閱 Lambda@Edge [錯誤](lambda-generating-http-responses.md#lambda-generating-http-responses-errors) 主題中的*對所產生回應的大小限制*)，以及無效的字元或值 (請參閱 [Lambda@Edge 事件結構說明頁面](lambda-event-structure.md))。

當 Lambda 傳回無效回應至 CloudFront 時，會將錯誤訊息寫入日誌檔案，而 CloudFront 會在執行 Lambda 函數的區域中推送至 CloudWatch。出現無效的回應時，預設的動作是將日誌檔案傳送到 CloudWatch。不過，如果在函數發布之前，就已建立 Lambda 函數與 CloudFront 的關聯，則可能不會針對您的函數啟用這項預設動作。如需詳細資訊，請參閱本主題稍後的*判斷您的帳戶是否推送日誌到 CloudWatch*。

CloudFront 會將日誌檔案推送到對應您函數執行所在位置的區域 (在與您的分佈具有關聯的日誌群組中)。日誌群組具有下列格式：`/aws/cloudfront/LambdaEdge/DistributionId`，其中 *DistributionId* 是您分佈的 ID。若要判斷您可以在哪個區域中找到 CloudWatch 日誌檔案，請參閱本主題稍後的*判斷 Lambda@Edge 區域*。

如果錯誤是可重現的，您可以建立新的請求來造成錯誤，然後在失敗的 CloudFront 回應 (`X-Amz-Cf-Id` 標頭) 中找到該請求的 ID，然後在日誌檔案中找出單一錯誤。日誌檔案記錄所包含的資訊，可協助您找出傳回錯誤的原因，也可以列出對應的 Lambda 請求 ID，來讓您針對單一請求的範圍，分析錯誤的根本原因。

如果錯誤是間歇性出現，您可以利用 CloudFront 存取日誌，針對失敗的請求找出其請求 ID，然後搜尋 CloudWatch Logs，尋找對應的錯誤訊息。如需詳細資訊，請參閱先前的段落*判斷故障的類型*。

## 進行 Lambda@Edge 函數執行錯誤的疑難排解
<a name="lambda-edge-testing-debugging-execution-errors"></a>

如果問題是 Lambda 的執行錯誤，那麼建立 Lambda 函數的記錄陳述式、將訊息寫入 CloudWatch 日誌檔案 (此日誌檔案會監控您的函數在 CloudFront 中的執行狀況)，然後判斷函數是否如預期運作，這些動作將會有所幫助。接著，您可以在 CloudWatch 日誌檔案中搜尋這些陳述式，來確認您的函數是否正常運作。

**注意**  
即使您未變更您的 Lambda@ Edge 函數，Lambda 函數執行環境的更新仍會對其造成影響，並因而傳回執行錯誤。如需有關測試和遷移至較新版本的資訊，請參閱 [Lambda AWS 和 AWS Lambda@Edge 執行環境的近期更新。](https://aws.amazon.com/blogs/compute/upcoming-updates-to-the-aws-lambda-execution-environment/)

## 判斷 Lambda@Edge 區域
<a name="lambda-edge-testing-debugging-determine-region"></a>

若要檢視您 Lambda@Edge 函數正在接收流量的區域，請在 AWS 管理主控台上的 CloudFront 主控台檢視函數的指標圖形。系統會顯示每個 AWS 區域的指標。在同一頁面中，您可以選擇一個區域並檢視該區域的日誌檔，以便調查問題。您必須檢閱正確區域中的 CloudWatch 日誌檔案 AWS ，以查看 CloudFront 執行 Lambda 函數時建立的日誌檔案。

如需在 CloudFront 主控台的**監控**區段檢視圖表的詳細資訊，請參閱[使用 Amazon CloudWatch 監控 CloudFront 指標](monitoring-using-cloudwatch.md)。

## 判斷您的帳戶是否推送日誌到 CloudWatch
<a name="lambda-edge-testing-debugging-cloudwatch-logs-enabled"></a>

根據預設，CloudFront 會啟用記錄無效的 Lambda 函式回應，並使用其中一個 [Lambda@Edge 的服務連結角色](lambda-edge-permissions.md#using-service-linked-roles-lambda-edge) 將日誌檔案推送至 CloudWatch。如果您擁有 Lambda@Edge 函數，而在無效的 Lambda 函數回應日誌函數推出之前，您已經將此 Lambda@Edge 函數新增到 CloudFront，而當您後續更新 Lambda@Edge 組態時，就會啟用記錄函數 (例如，透過新增 CloudFront 觸發條件)。

您可以執行下列的動作，來確認您的帳戶已啟用推送日誌檔案到 CloudWatch 的功能：
+ **檢查日誌是否顯示在 CloudWatch 中**：請務必查看執行 Lambda@Edge 函數的區域。如需詳細資訊，請參閱[判斷 Lambda@Edge 區域](#lambda-edge-testing-debugging-determine-region)。
+ **在 IAM 中判斷您的帳戶是否存在相關的服務連結角色**：您的帳戶必須擁有 IAM 角色 `AWSServiceRoleForCloudFrontLogger`。如需有關此角色的詳細資訊，請參閱 [Lambda@Edge 的服務連結角色](lambda-edge-permissions.md#using-service-linked-roles-lambda-edge)。

# 刪除 Lambda@Edge 函數和複本
<a name="lambda-edge-delete-replicas"></a>

您可以只在函數複本遭 CloudFront 刪除時，系統才會刪除 Lambda@Edge 函數。Lambda 函數的複本將在下列情況自動刪除：
+ 當您從所有 CloudFront 分佈移除最後一個函數的關聯後。如果一個以上的分佈使用一個函數，則只有在您從上一個分佈移除函數關聯之後，複本才會刪除。
+ 在您刪除最後一個與函數相關聯的分佈後。

複本一般會在幾個小時內刪除。您無法手動刪除 Lambda@Edge 函數複本。這有助於防止刪除仍在使用中的複本，以免導致錯誤情況。

**警告**  
請勿建置在 CloudFront 之外使用 Lambda@Edge 函數複本的應用程式。當這些複本與分佈的關聯遭到移除，或分佈本身遭到刪除時，這些複本就會隨之刪除。外部應用程式所依據使用的複本可能會在無預警的情況下移除，導致應用程式作業失敗。

**從 CloudFront 分佈刪除 Lambda@Edge 函數關聯**

1. 登入 AWS 管理主控台 ，並在 開啟 CloudFront 主控台[https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home)。

1. 選擇具有您要刪除之 Lambda@Edge 函數關聯的分佈 ID。

1. 選擇 **Behaviors (動作)** 索引標籤。

1. 選擇具有您要刪除之 Lambda@Edge 函數關聯的快取行為，然後選擇**編輯**。

1. 在**函數關聯**、**函數類型**下，選擇**沒有關聯**以刪除 Lambda@Edge 函數關聯。

1. 選擇**儲存變更**。

從 CloudFront 分發中刪除 Lambda@Edge 函數關聯之後，您可以選擇性地從 AWS Lambda中刪除 Lambda 函數或函數版本。刪除函數關聯後請等待數小時，以便清除 Lambda@Edge 函數複本。之後，您可以使用 Lambda 主控台、、 AWS CLI Lambda API 或 AWS SDK 刪除函數。

您也可以刪除特定*版本*的 Lambda 函數，前提示該版本未與任何 CloudFront 分佈建立關聯。移除 Lambda 函數版本的所有關聯後，請等待數小時。然後您可以刪除函數版本。

# Lambda@Edge 事件結構說明頁面
<a name="lambda-event-structure"></a>

下列主題說明在觸發時，CloudFront 傳遞到 Lambda@Edge 函數的請求和回應事件物件。

**Topics**
+ [動態原始伺服器選擇](#lambda-event-content-based-routing)
+ [請求事件](#lambda-event-structure-request)
+ [回應事件](#lambda-event-structure-response)

## 動態原始伺服器選擇
<a name="lambda-event-content-based-routing"></a>

您可以根據路徑和請求物件名稱 (例如 `images/*.jpg`)，使用[快取行為中的路徑模式](DownloadDistValuesCacheBehavior.md#DownloadDistValuesPathPattern)將請求路由傳送到原始伺服器。使用 Lambda@Edge，您也可以根據其他特性路由傳送請求到原始伺服器，例如請求標頭中的值。

此動態原始伺服器選擇在很多方面是非常有用的。例如，您可以將請求分佈到各個在不同地理區域的原始伺服器，以協助全域負載平衡。或者，您可以選擇性地路由傳送請求到服務特定函數之不同的原始伺服器：機器人處理、SEO 最佳化、驗證，以此類推。如需示範如何使用此功能的程式碼範例，請參閱[以內容為基礎的動態原始伺服器選擇 - 範例](lambda-examples.md#lambda-examples-content-based-routing-examples)。

在 CloudFront 原始伺服器請求事件中，事件結構中的 `origin` 物件包含了關於該原始伺服器的資訊 (會根據路徑的模式，將請求轉送到自訂此原始伺服器)。您可以更新 `origin` 物件的值，將請求路由傳送到不同的原始伺服器。更新 `origin` 物件時，您不需要定義分佈中的原始伺服器。您也可以使用自訂原始伺服器物件來取代 Amazon S3 原始伺服器物件，反之亦然。不過，您只能為每個請求指定單一原始伺服器；也就是指定自訂原始伺服器或 Amazon S3 原始伺服器，但不能兩者同時指定。

## 請求事件
<a name="lambda-event-structure-request"></a>

下列主題說明 CloudFront 針對[檢視器和原始伺服器請求事件](lambda-cloudfront-trigger-events.md)傳遞到 Lambda 函數的結構。這些範例顯示不含任何主體的 `GET` 請求。在範例之後，會提供檢視器和原始伺服器請求事件中的所有可能欄位清單。

**Topics**
+ [檢視者請求範例](#example-viewer-request)
+ [原始伺服器請求範例](#example-origin-request)
+ [請求事件欄位](#request-event-fields)

### 檢視者請求範例
<a name="example-viewer-request"></a>

下列範例顯示檢視器請求事件物件。

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "viewer-request",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "host": [
              {
                "key": "Host",
                "value": "d111111abcdef8.cloudfront.net"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "curl/7.66.0"
              }
            ],
            "accept": [
              {
                "key": "accept",
                "value": "*/*"
              }
            ]
          },
          "method": "GET",
          "querystring": "",
          "uri": "/"
        }
      }
    }
  ]
}
```

### 原始伺服器請求範例
<a name="example-origin-request"></a>

下列範例顯示原始伺服器請求事件物件。

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "origin-request",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "x-forwarded-for": [
              {
                "key": "X-Forwarded-For",
                "value": "203.0.113.178"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "Amazon CloudFront"
              }
            ],
            "via": [
              {
                "key": "Via",
                "value": "2.0 2afae0d44e2540f472c0635ab62c232b.cloudfront.net (CloudFront)"
              }
            ],
            "host": [
              {
                "key": "Host",
                "value": "example.org"
              }
            ],
            "cache-control": [
              {
                "key": "Cache-Control",
                "value": "no-cache"
              }
            ]
          },
          "method": "GET",
          "origin": {
            "custom": {
              "customHeaders": {},
              "domainName": "example.org",
              "keepaliveTimeout": 5,
              "path": "",
              "port": 443,
              "protocol": "https",
              "readTimeout": 30,
              "responseCompletionTimeout": 30,
              "sslProtocols": [
                "TLSv1",
                "TLSv1.1",
                "TLSv1.2"
              ]
            }
          },
          "querystring": "",
          "uri": "/"
        }
      }
    }
  ]
}
```

### 請求事件欄位
<a name="request-event-fields"></a>

請求事件物件資料包含在兩個子物件中：`config` (`Records.cf.config`) 和 `request` (`Records.cf.request`)。下列清單說明每個子物件的欄位。

#### Config 物件中的欄位
<a name="request-event-fields-config"></a>

下列清單說明 `config` 物件 (`Records.cf.config`) 中的欄位。

**`distributionDomainName` (唯讀)**  
與請求相關的分佈網域名稱。

**`distributionID` (唯讀)**  
與請求相關的分佈 ID。

**`eventType` (唯讀)**  
與請求關聯的觸發條件類型：`viewer-request` 或 `origin-request`。

**`requestId` (唯讀)**  
可獨特識別檢視器至 CloudFront 請求的加密字串。此 `requestId` 值也會在 CloudFront 存取記錄中顯示為 `x-edge-request-id`。如需詳細資訊，請參閱[存取日誌 （標準日誌）](AccessLogs.md)及[日誌檔案欄位](standard-logs-reference.md#BasicDistributionFileFormat)。

#### 請求物件中的欄位
<a name="request-event-fields-request"></a>

下列清單說明 `request` 物件 (`Records.cf.request`) 中的欄位。

**`clientIp` (唯讀)**  
提出請求之檢視器的 IP 位址。如果檢視器使用 HTTP Proxy 或負載平衡器傳送請求，此值為代理或負載平衡器的 IP 位址。

** 標頭 (讀取/寫入)**  
請求中的標頭。注意下列事項：  
+ 在 `headers` 物件的金鑰為標準 HTTP 標頭名稱的小寫版本。使用小寫金鑰提供您區分大小寫的標頭值存取權。
+ 每個標頭物件 (例如 `headers["accept"]` 或 `headers["host"]`) 都是鍵值組的陣列。針對已知的標頭，陣列會在請求中包含每個值的一組鍵值組。
+ `key` 包含在 HTTP 請求中出現的標頭名稱 (區分大小寫)，例如 `Host`、`User-Agent`、`X-Forwarded-For`、`Cookie` 等。
+ `value` 包含在 HTTP 請求中出現的標頭值。
+ 當 Lambda 函數新增或修改請求標頭，而您不想要包含標頭 `key` 欄位時，Lambda@Edge 會使用您提供的標頭名稱來自動插入 `key` 標頭。無論您如何設定標頭名稱格式，各部分插入的標頭索引鍵名稱都會自動以大寫開頭，以連字號 (-) 分隔。

  例如，您可以在沒有 `key` 標頭的情況下，新增標頭如下：

  ```
  "user-agent": [
    {
      "value": "ExampleCustomUserAgent/1.X.0"
    }
  ]
  ```

  在此範例中，Lambda@Edge 會自動插入 `"key": "User-Agent"`。
如需標頭使用限制的詳細資訊，請參閱[對邊緣函數的限制](edge-functions-restrictions.md)。

**`method` (唯讀)**  
請求的 HTTP 方法。

**`querystring` (讀取/寫入)**  
請求中的查詢字串 (如果有的話)。如果請求不包含查詢字串，事件物件仍會包含具有空白值的 `querystring`。如需查詢字串的詳細資訊，請參閱[根據查詢字串參數快取內容](QueryStringParameters.md)。

**`uri` (讀取/寫入)**  
請求物件的相對路徑。如果 Lambda 函數修改了 `uri` 值，請注意下列事項：  
+ 全新的 `uri` 值必須以正斜線 (/) 做為開頭。
+ 當函數變更 `uri` 值時，這會變更檢視器請求的物件。
+ 當函數變更 `uri` 值時，並*不會*變更請求的快取行為或請求傳送的目標原始伺服器。

**`body` (讀取/寫入)**  
HTTP 請求的主題。`body` 結構可包含下列欄位：    
**`inputTruncated` (唯讀)**  
布林值旗標，此旗標會指出 Lambda@Edge 是否截斷了本體。如需詳細資訊，請參閱[使用包含內文選項時的要求內文限制](lambda-at-edge-function-restrictions.md#lambda-at-edge-restrictions-request-body)。  
**`action` (讀取/寫入)**  
您想要對本體採取的動作。`action` 的選項如下：  
+ `read-only:` 此為預設值。從 Lambda 函式將回應傳回時，如果 `action` 為唯讀狀態，則 Lambda@Edge 會忽略對 `encoding` 或 `data` 所進行的任何變更。
+ `replace:` 如果想取代傳送到原始伺服器的本體，請指定此選項。  
**`encoding` (讀取/寫入)**  
本體的編碼。當 Lambda@Edge 向 Lambda 函式顯露內文時，會先將內文轉換為 base64-encoding。如果針對 `action` 選擇 `replace` 以取代本體，您可以選擇使用 `base64` (預設) 或 `text` 編碼。如果將 `encoding` 指定為 `base64`，但內文並非有效的 base64，CloudFront 會傳回錯誤。  
**`data` (讀取/寫入)**  
請求本體的內容。

**`origin` (讀取/寫入) (僅限原始伺服器事件)**  
傳送請求的目標原始伺服器。`origin` 結構必須*確切地包含一個原始伺服器*，這可以是自訂原始伺服器或 Amazon S3 原始伺服器。  
根據您指定的原始伺服器類型 (自訂或 Amazon S3 原始伺服器)，您必須在請求中指定下列欄位：    
**`customHeaders` (讀取/寫入) (自訂和 Amazon S3 原始伺服器)**  
(選用) 您可以透過指定的標頭名稱與每個自訂標頭的值對，於請求中包含自訂標頭。您無法新增已列入不允許的標頭，且具有相同名稱的標頭也無法出現在 `Records.cf.request.headers` 中。[請求標頭的相關注意事項](#request-event-fields-request-headers)也適用於自訂標頭。如需詳細資訊，請參閱[CloudFront 無法新增到原始伺服器請求的自訂標頭](add-origin-custom-headers.md#add-origin-custom-headers-denylist)及[對邊緣函數的限制](edge-functions-restrictions.md)。  
**`domainName` (讀取/寫入) (自訂和 Amazon S3 原始伺服器)**  
原始伺服器的網域名稱。網域名稱不能空白。  
+ **適用於自訂原始伺服器**：指定 DNS 網域名稱，例如 `www.example.com`。網域名稱不能包含冒號 (:)，而且不能是 IP 位址。網域名稱長度上限為 253 個字元。
+ **對於 Amazon S3 原始伺服器** — 指定 Amazon S3 儲存貯體的 DNS 網域名稱，例如 `amzn-s3-demo-bucket.s3.eu-west-1.amazonaws.com`. 名稱可以高達 128 個字元，而且必須全部小寫。  
**`path` (讀取/寫入) (自訂和 Amazon S3 原始伺服器)**  
目錄路徑位於需定位內容請求的原始伺服器中。路徑的開頭應為正斜線 (/)，但結尾不應為正斜線 (例如，結尾不應為 `example-path/`)。僅適用於自訂原始伺服器：路徑應為 URL 編碼，且其長度上限為 255 個字元。  
**`keepaliveTimeout` (讀取/寫入) (僅限自訂原始伺服器)**  
CloudFront 在收到上次封包的回應後維持與自訂原始伺服器的連線所需的時間長度 (以秒為單位)。此值必須是介於 1 到 120 之間的數字。  
**`port` (讀取/寫入) (僅限自訂原始伺服器)**  
CloudFront 應在您的自訂原始伺服器連線的連線埠。此連結埠必須是 80、443，或是介於 1024–65535 之間的數字。  
**`protocol` (讀取/寫入) (僅限自訂原始伺服器)**  
CloudFront 在連線到原始伺服器時應使用的連線通訊協定。此值可以為 `http` 或 `https`。  
**`readTimeout` (讀取/寫入) (自訂和 Amazon S3 原始伺服器)**  
CloudFront 在傳送請求給原始伺服器後應等候回應的時間長度 (以秒為單位)。這也會指定 CloudFront 在收到回應封包後與收到下一個封包前，應等候的時間長度。此值必須是介於 1 到 120 之間的數字。  
如果您需要更高的配額，請參閱[每個原始伺服器的回應逾時](cloudfront-limits.md#limits-web-distributions)。  
**`responseCompletionTimeout` (讀取/寫入) (自訂和 Amazon S3 原始伺服器)**  
從 CloudFront 到原始伺服器的請求可以保持開啟並等待回應的時間 (以秒為單位)。如果此時未從原始伺服器收到完整回應，CloudFront 會結束連線。  
`responseCompletionTimeout` 值必須大於或等於 `readTimeout` 的值。如果您將此值設定為 0，則會移除您設定的任何先前值，並返回預設值。您也可以從事件請求中刪除 `responseCompletionTimeout` 欄位來完成此操作。  
**`sslProtocols` (讀取/寫入) (僅限自訂原始伺服器)**  
當在您原始伺服器建立 HTTPS 連線時，CloudFront 可以使用的最低 SSL/TLS 通訊協定。可為以下任何一個值：`TLSv1.2`、`TLSv1.1`、`TLSv1` 或 `SSLv3`。  
**`authMethod` (讀取/寫入) (僅限 Amazon S3 原始伺服器)**  
如果您使用[原始存取身分 (OAI)](private-content-restricting-access-to-s3.md#private-content-restricting-access-to-s3-oai)，請將此欄位設定為 `origin-access-identity`。如果您未使用 OAI，請將其設定為 `none`。如果您將 `authMethod` 設定為 `origin-access-identity`，有幾項要求如下：  
+ 您必須指定 `region` (請參閱下列欄位)。
+ 當您將請求從某個 Amazon S3 原始伺服器變更為其他原始伺服器時，您必須使用相同的 OAI。
+ 當您將請求從某個自訂原始伺服器變更為 Amazon S3 原始伺服器時，您無法使用 OAI。
此欄位不支援[原始存取控制 (OAC)](private-content-restricting-access-to-s3.md)。  
**`region` (讀取/寫入) (僅限 Amazon S3 原始伺服器)**  
Amazon S3 儲存貯體 AWS 的區域。只有在您將 `authMethod` 設定為 `origin-access-identity` 時才需要。

## 回應事件
<a name="lambda-event-structure-response"></a>

下列主題說明 CloudFront 針對[檢視器和原始伺服器回應事件](lambda-cloudfront-trigger-events.md)傳遞到 Lambda 函數的結構。在範例之後，會提供檢視器和原始伺服器回應事件中的所有可能欄位清單。

**Topics**
+ [原始伺服器回應範例](#lambda-event-structure-response-origin)
+ [檢視者回應範例](#lambda-event-structure-response-viewer)
+ [回應事件欄位](#response-event-fields)

### 原始伺服器回應範例
<a name="lambda-event-structure-response-origin"></a>

下列範例顯示原始伺服器回應事件物件。

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "origin-response",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "x-forwarded-for": [
              {
                "key": "X-Forwarded-For",
                "value": "203.0.113.178"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "Amazon CloudFront"
              }
            ],
            "via": [
              {
                "key": "Via",
                "value": "2.0 8f22423015641505b8c857a37450d6c0.cloudfront.net (CloudFront)"
              }
            ],
            "host": [
              {
                "key": "Host",
                "value": "example.org"
              }
            ],
            "cache-control": [
              {
                "key": "Cache-Control",
                "value": "no-cache"
              }
            ]
          },
          "method": "GET",
          "origin": {
            "custom": {
              "customHeaders": {},
              "domainName": "example.org",
              "keepaliveTimeout": 5,
              "path": "",
              "port": 443,
              "protocol": "https",
              "readTimeout": 30,
              "responseCompletionTimeout": 30,
              "sslProtocols": [
                "TLSv1",
                "TLSv1.1",
                "TLSv1.2"
              ]
            }
          },
          "querystring": "",
          "uri": "/"
        },
        "response": {
          "headers": {
            "access-control-allow-credentials": [
              {
                "key": "Access-Control-Allow-Credentials",
                "value": "true"
              }
            ],
            "access-control-allow-origin": [
              {
                "key": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ],
            "date": [
              {
                "key": "Date",
                "value": "Mon, 13 Jan 2020 20:12:38 GMT"
              }
            ],
            "referrer-policy": [
              {
                "key": "Referrer-Policy",
                "value": "no-referrer-when-downgrade"
              }
            ],
            "server": [
              {
                "key": "Server",
                "value": "ExampleCustomOriginServer"
              }
            ],
            "x-content-type-options": [
              {
                "key": "X-Content-Type-Options",
                "value": "nosniff"
              }
            ],
            "x-frame-options": [
              {
                "key": "X-Frame-Options",
                "value": "DENY"
              }
            ],
            "x-xss-protection": [
              {
                "key": "X-XSS-Protection",
                "value": "1; mode=block"
              }
            ],
            "content-type": [
              {
                "key": "Content-Type",
                "value": "text/html; charset=utf-8"
              }
            ],
            "content-length": [
              {
                "key": "Content-Length",
                "value": "9593"
              }
            ]
          },
          "status": "200",
          "statusDescription": "OK"
        }
      }
    }
  ]
}
```

### 檢視者回應範例
<a name="lambda-event-structure-response-viewer"></a>

下列範例顯示檢視器回應事件物件。

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "viewer-response",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "host": [
              {
                "key": "Host",
                "value": "d111111abcdef8.cloudfront.net"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "curl/7.66.0"
              }
            ],
            "accept": [
              {
                "key": "accept",
                "value": "*/*"
              }
            ]
          },
          "method": "GET",
          "querystring": "",
          "uri": "/"
        },
        "response": {
          "headers": {
            "access-control-allow-credentials": [
              {
                "key": "Access-Control-Allow-Credentials",
                "value": "true"
              }
            ],
            "access-control-allow-origin": [
              {
                "key": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ],
            "date": [
              {
                "key": "Date",
                "value": "Mon, 13 Jan 2020 20:14:56 GMT"
              }
            ],
            "referrer-policy": [
              {
                "key": "Referrer-Policy",
                "value": "no-referrer-when-downgrade"
              }
            ],
            "server": [
              {
                "key": "Server",
                "value": "ExampleCustomOriginServer"
              }
            ],
            "x-content-type-options": [
              {
                "key": "X-Content-Type-Options",
                "value": "nosniff"
              }
            ],
            "x-frame-options": [
              {
                "key": "X-Frame-Options",
                "value": "DENY"
              }
            ],
            "x-xss-protection": [
              {
                "key": "X-XSS-Protection",
                "value": "1; mode=block"
              }
            ],
            "age": [
              {
                "key": "Age",
                "value": "2402"
              }
            ],
            "content-type": [
              {
                "key": "Content-Type",
                "value": "text/html; charset=utf-8"
              }
            ],
            "content-length": [
              {
                "key": "Content-Length",
                "value": "9593"
              }
            ]
          },
          "status": "200",
          "statusDescription": "OK"
        }
      }
    }
  ]
}
```

### 回應事件欄位
<a name="response-event-fields"></a>

回應事件物件資料包含在三個子物件中：`config` (`Records.cf.config`)、`request` (`Records.cf.request`) 和 `response` (`Records.cf.response`)。如需請求物件中欄位的詳細資訊，請參閱[請求物件中的欄位](#request-event-fields-request)。下列清單說明 `config` 和 `response` 子物件中的欄位。

#### Config 物件中的欄位
<a name="response-event-fields-config"></a>

下列清單說明 `config` 物件 (`Records.cf.config`) 中的欄位。

**`distributionDomainName` (唯讀)**  
與回應關聯的分佈的網域名稱。

**`distributionID` (唯讀)**  
與回應關聯的分佈 ID。

**`eventType` (唯讀)**  
與回應關聯的觸發類型：`origin-response` 或 `viewer-response`。

**`requestId` (唯讀)**  
可獨特識別與此回應相關聯之檢視器至 CloudFront 請求的加密字串。此 `requestId` 值也會在 CloudFront 存取記錄中顯示為 `x-edge-request-id`。如需詳細資訊，請參閱[存取日誌 （標準日誌）](AccessLogs.md)及[日誌檔案欄位](standard-logs-reference.md#BasicDistributionFileFormat)。

#### 回應物件中的欄位
<a name="response-event-fields-response"></a>

下列清單說明 `response` 物件 (`Records.cf.response`) 中的欄位。如需如何使用 Lambda@Edge 函式產生 HTTP 請求的資訊，請參閱[在請求觸發條件中產生 HTTP 回應](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests)。

**`headers` (讀取/寫入)**  
回應中的標頭。注意下列事項：  
+ 在 `headers` 物件的金鑰為標準 HTTP 標頭名稱的小寫版本。使用小寫金鑰提供您區分大小寫的標頭值存取權。
+ 每個標頭物件 (例如 `headers["content-type"]` 或 `headers["content-length"]`) 都是鍵值組的陣列。針對已知的標頭，陣列會在回應中包含每個值的一組鍵值組。
+ `key` 包含在 HTTP 回應中出現的標頭名稱 (區分大小寫)，例如 `Content-Type`、`Content-Length`、`Cookie` 等。
+ `value` 包含在 HTTP 回應中出現的標頭值。
+ 當 Lambda 函數新增或修改回應標頭，而您不想要包含標頭 `key` 欄位時，Lambda@Edge 會使用您提供的標頭名稱來自動插入 `key` 標頭。無論您如何設定標頭名稱格式，各部分插入的標頭索引鍵名稱都會自動以大寫開頭，以連字號 (-) 分隔。

  例如，您可以在沒有 `key` 標頭的情況下，新增標頭如下：

  ```
  "content-type": [
    {
      "value": "text/html;charset=UTF-8"
    }
  ]
  ```

  在此範例中，Lambda@Edge 會自動插入 `"key": "Content-Type"`。
如需標頭使用限制的詳細資訊，請參閱[對邊緣函數的限制](edge-functions-restrictions.md)。

**`status`**  
回應的 HTTP 狀態碼。

**`statusDescription`**  
回應的 HTTP 狀態說明。

# 使用請求和回應
<a name="lambda-generating-http-responses"></a>

若要使用 Lambda@Edge 請求和回應，請參閱下列主題：

**Topics**
+ [使用 Lambda@Edge 函數搭配原始伺服器容錯移轉](#lambda-and-origin-failover)
+ [在請求觸發條件中產生 HTTP 回應](#lambda-generating-http-responses-in-requests)
+ [在原始伺服器回應觸發條件中更新 HTTP 回應](#lambda-updating-http-responses)
+ [選擇包含本文選項來存取請求本文](#lambda-include-body-access)

## 使用 Lambda@Edge 函數搭配原始伺服器容錯移轉
<a name="lambda-and-origin-failover"></a>

您可以使用 Lambda@Edge 函數，與您已使用原始伺服器群組來設定的 CloudFront 分佈搭配，例如，針對您設定來確保高可用性的原始伺服器容錯移轉。若要使用 Lambda 函數與原始伺服器群組搭配，請在您建立快取行為時針對原始伺服器群組觸發的原始伺服器請求或原始伺服器回應中指定函數。

如需詳細資訊，請參閱下列內容：
+ **建立原始伺服器群組：**[建立原始伺服器群組](high_availability_origin_failover.md#concept_origin_groups.creating)
+ **原始伺服器容錯移轉如何使用 Lambda@Edge：**[使用原始伺服器容錯移轉與 Lambda@Edge 函數搭配](high_availability_origin_failover.md#concept_origin_groups.lambda)

## 在請求觸發條件中產生 HTTP 回應
<a name="lambda-generating-http-responses-in-requests"></a>

當 CloudFront 收到請求時，您可以使用 Lambda 函數產生 CloudFront 無需轉送回應給原始伺服器，可直接傳回給檢視器的 HTTP 回應。產生 HTTP 回應降低原始伺服器的負載，且通常也可減少檢視器的延遲。

產生 HTTP 回應的一些常見案例包括下列項目：
+ 傳回小型網頁給檢視器
+ 傳回 HTTP 301 或 302 狀態碼來重新導向使用者到另一個網頁
+ 當使用者尚未驗證時，傳回 HTTP 401 狀態碼至檢視器

當下列 CloudFront 事件發生時，Lambda@Edge 函數可以產生 HTTP 回應事件：

**檢視器請求事件**  
當函數被檢視器請求事件觸發時，CloudFront 會將回應傳回檢視器，而且加以不快取。

**原始伺服器請求事件**  
當函數被原始伺服器請求事件觸發時，CloudFront 會檢查先前函數產生的邊緣快取回應。  
+ 如果回應在快取中，系統不會執行該函數，且 CloudFront 會將快取的回應傳回給檢視器。
+ 如果回應不在快取中，會執行該函數，CloudFront 會將回應傳回給檢視器並加以快取。

若要查看產生 HTTP 回應的範本程式碼，請參閱 [Lambda@Edge 範例函數](lambda-examples.md)。您也可以在回應觸發中取代 HTTP 回應。如需詳細資訊，請參閱[在原始伺服器回應觸發條件中更新 HTTP 回應](#lambda-updating-http-responses)。

### 程式設計模型
<a name="lambda-generating-http-responses-programming-model"></a>

本節說明使用 Lambda@Edge 產生 HTTP 回應的程式設計模型。

**Topics**
+ [回應物件](#lambda-generating-http-responses-object)
+ [錯誤](#lambda-generating-http-responses-errors)
+ [必要欄位](#lambda-generating-http-responses-required-fields)

#### 回應物件
<a name="lambda-generating-http-responses-object"></a>

以 `result` 方法的 `callback` 參數傳回的回應，需具備下列結構 (請注意，僅 `status` 欄位為必要)。

```
const response = {
    body: 'content',
    bodyEncoding: 'text' | 'base64',
    headers: {
        'header name in lowercase': [{
            key: 'header name in standard case',
            value: 'header value'
         }],
         ...
    },
    status: 'HTTP status code (string)',
    statusDescription: 'status description'
};
```

回應物件可以包含以下值：

**`body`**  
如有您希望 CloudFront 在產生的回應中傳回的本文。

**`bodyEncoding`**  
您在 `body` 中指定的值的編碼。唯一的有效編碼是 `text` 和 `base64`。如果您在 `response` 物件中加入 `body`，但省略了 `bodyEncoding`，則 CloudFront 會將內文視為文字。  
如果您將 `bodyEncoding` 指定為 `base64` 但本文不是有效的 base64，CloudFront 會傳回錯誤。

**`headers`**  
您希望 CloudFront 於產生的回應中傳回標頭。注意下列事項：  
+ 在 `headers` 物件的金鑰為標準 HTTP 標頭名稱的小寫版本。使用小寫金鑰提供您區分大小寫的標頭值存取權。
+ 每個標頭 (例如 `headers["accept"]` 或 `headers["host"]`) 是一系列的鍵值組。於已知的標頭，陣列在產生的回應中包含每個值的一組鍵值組。
+ `key` (選用) 為在 HTTP 請求中出現的標頭區分大小寫的名稱，例如 `accept` 或 `host`。
+ 指定 `value` 為標頭值。
+ 如果您未加入索引鍵/值組的標頭索引鍵部分，Lambda@Edge 會自動使用您提供的標頭名稱，來插入標頭索引鍵。無論您如何安排標頭格式，各部分插入的標頭索引鍵名稱都會自動以大寫開頭，以連字號 (-) 分隔。

  例如，您可以不用標頭索引鍵，新增標頭如下：`'content-type': [{ value: 'text/html;charset=UTF-8' }]`

  在這個範例中，Lambda@Edge 建立了下列標頭索引鍵：`Content-Type`。
如需標頭使用限制的詳細資訊，請參閱[對邊緣函數的限制](edge-functions-restrictions.md)。

**`status`**  
HTTP 狀態碼 。以字串形式提供狀態碼。CloudFront 使用提供的狀態碼進行以下操作：  
+ 在回應中傳回
+ 當原始伺服器請求事件觸發的函數產生回應時，在 CloudFront 邊緣快取中快取。
+ 登入 CloudFront [存取日誌 （標準日誌）](AccessLogs.md)
如果 `status` 值不在 200 與 599 之間，CloudFront 會將錯誤傳回至檢視器。

**`statusDescription`**  
您想要 CloudFront 在回應中傳回的描述能搭配 HTTP 狀態碼。您不需要使用標準的描述，例如 HTTP 200 狀態碼為 `OK`。

#### 錯誤
<a name="lambda-generating-http-responses-errors"></a>

以下是產生的 HTTP 回應的可能錯誤。

**回應包含本文與指定 204 (無內容) 狀態**  
當函數是由檢視器請求觸發，當下列二者為 true，CloudFront 會傳回 HTTP 502 狀態碼 (無效的閘道) 至檢視器：  
+ `status` 的值是 204 (無內容)
+ 回應包含 `body` 的值
這是因為 Lambda@Edge 強加於 RFC 2616 中選用的限制，也就是 `HTTP 204` 回應不需要包含訊息本文。

**已產生回應的大小限制**  
由 Lambda 函數產生的回應大小上限取決於觸發函數的事件：  
+ **檢視器請求事件** – 40 KB
+ **原始伺服器請求事件** – 1 MB
如果回應超過允許的大小，CloudFront 會將 HTTP 502 狀態碼 (無效的閘道) 傳回檢視器。

#### 必要欄位
<a name="lambda-generating-http-responses-required-fields"></a>

`status` 欄位是必要的。

所有其他欄位是選用的。

## 在原始伺服器回應觸發條件中更新 HTTP 回應
<a name="lambda-updating-http-responses"></a>

當 CloudFront 從原始伺服器收到 HTTP 回應時，如果某個原始伺服器回應觸發關聯到快取行為，您可以修改 HTTP 回應以覆寫原始伺服器傳回的回應。

更新 HTTP 回應的一些常用案例包括下列項目：
+ 變更狀態以設定 HTTP 200 狀態碼並建立靜態本文內容，以在原始伺服器傳回錯誤狀態碼 (4xx 和 5xx) 時，傳回給檢視器。如需程式碼範例，請參閱 [範例：使用原始伺服器請求觸發以更新錯誤狀態碼為 200](lambda-examples.md#lambda-examples-custom-error-static-body)。
+ 變更狀態來設定 HTTP 301 或 HTTP 302 狀態碼，以在原始伺服器會傳回錯誤狀態碼 (4xx 和 5xx) 時，將使用者重新導向到另一個網站。如需程式碼範例，請參閱 [範例：使用原始伺服器請求觸發以更新錯誤狀態碼為 302](lambda-examples.md#lambda-examples-custom-error-new-site)。

**注意**  
函數必須傳回介於 `200` (含) 和 `599` (含) 之間的狀態值，否則 CloudFront 會將錯誤傳回至檢視器。

您也可以在檢視器與原始伺服器請求事件中取代 HTTP 回應。如需詳細資訊，請參閱[在請求觸發條件中產生 HTTP 回應](#lambda-generating-http-responses-in-requests)。

當您使用 HTTP 回應時，Lambda@Edge 不會公開原始伺服器傳回至原始伺服器回應觸發條件的本文。您可以藉由設定為所需的值來產生靜態內容本文，或藉由設定空值來移除函數內的本文。如果您不更新函數的本文欄位，原始伺服器回傳的原始主體會傳回給檢視器。

## 選擇包含本文選項來存取請求本文
<a name="lambda-include-body-access"></a>

您可以選擇讓 Lambda@Edge 在可寫入的 HTTP 方法 (POST、PUT 和 DELETE 等) 中公開請求的內文，如此您就能在 Lambda 函數中存取該內文。您可以選擇唯讀存取或指定您將替換內文。

若要啟用此選項，請在您為函數建立 CloudFront 觸發條件時 (針對檢視器請求或原始伺服器請求的事件)，選擇 **Include Body (包含內文)**。如需詳細資訊，請參閱 [為 Lambda@Edge 函數新增觸發條件](lambda-edge-add-triggers.md)；或者，若要進一步了解使用**包含內文**與您的函數，請參閱 [Lambda@Edge 事件結構說明頁面](lambda-event-structure.md)。

您可能會想使用此功能的情境，包括下列的案例：
+ 處理 Web 表單，例如「聯絡我們」表單，而不將客戶輸入的資料傳回給原始伺服器。
+ 收集由檢視器的瀏覽器所傳送的網站信標資料，並且在邊緣處理這些資料。

如需程式碼範例，請參閱 [Lambda@Edge 範例函數](lambda-examples.md)。

**注意**  
如果請求主體的資料大小過大，Lambda@Edge 會將主體截斷。如需大小上限和截斷的詳細資訊，請參閱[使用包含內文選項時的要求內文限制](lambda-at-edge-function-restrictions.md#lambda-at-edge-restrictions-request-body)。

# Lambda@Edge 範例函數
<a name="lambda-examples"></a>

請參閱下列透過 Amazon CloudFront 使用 Lambda 函數的範例。

**注意**  
如果您為 Lambda@Edge 函數選擇執行時期 Node.js 18 或更新版本，系統會自動為您建立 `index.mjs` 檔案。若要使用下列程式碼範例，請將 `index.mjs` 檔案重新命名為 `index.js`。

**Topics**
+ [一般範例](#lambda-examples-general-examples)
+ [產生回應：範例](#lambda-examples-generated-response-examples)
+ [查詢字串：範例](#lambda-examples-query-string-examples)
+ [根據國家/地區或裝置類型標頭個人化 - 範例](#lambda-examples-redirecting-examples)
+ [以內容為基礎的動態原始伺服器選擇 - 範例](#lambda-examples-content-based-routing-examples)
+ [更新錯誤狀態：範例](#lambda-examples-update-error-status-examples)
+ [存取請求本文：範例](#lambda-examples-access-request-body-examples)

## 一般範例
<a name="lambda-examples-general-examples"></a>

下列範例顯示在 CloudFront 中使用 Lambda@Edge 的常見方法。

**Topics**
+ [範例：A/B 測試](#lambda-examples-a-b-testing)
+ [範例：覆寫回應標頭](#lambda-examples-overriding-response-header)

### 範例：A/B 測試
<a name="lambda-examples-a-b-testing"></a>

您可以使用下列範例測試兩種不同版本的影像，而不需要建立重新導向或變更 URL。此範例會讀取檢視器請求中的 Cookie，並據此修改請求 URL。如果檢視器未傳送具有其中一個預期值的 Cookie，此範例會將檢視器隨機指派給其中一個 URL。

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    if (request.uri !== '/experiment-pixel.jpg') {
        // do not process if this is not an A-B test request
        callback(null, request);
        return;
    }

    const cookieExperimentA = 'X-Experiment-Name=A';
    const cookieExperimentB = 'X-Experiment-Name=B';
    const pathExperimentA = '/experiment-group/control-pixel.jpg';
    const pathExperimentB = '/experiment-group/treatment-pixel.jpg';

    /*
     * Lambda at the Edge headers are array objects.
     *
     * Client may send multiple Cookie headers, i.e.:
     * > GET /viewerRes/test HTTP/1.1
     * > User-Agent: curl/7.18.1 (x86_64-unknown-linux-gnu) libcurl/7.18.1 OpenSSL/1.0.1u zlib/1.2.3
     * > Cookie: First=1; Second=2
     * > Cookie: ClientCode=abc
     * > Host: example.com
     *
     * You can access the first Cookie header at headers["cookie"][0].value
     * and the second at headers["cookie"][1].value.
     *
     * Header values are not parsed. In the example above,
     * headers["cookie"][0].value is equal to "First=1; Second=2"
     */
    let experimentUri;
    if (headers.cookie) {
        for (let i = 0; i < headers.cookie.length; i++) {
            if (headers.cookie[i].value.indexOf(cookieExperimentA) >= 0) {
                console.log('Experiment A cookie found');
                experimentUri = pathExperimentA;
                break;
            } else if (headers.cookie[i].value.indexOf(cookieExperimentB) >= 0) {
                console.log('Experiment B cookie found');
                experimentUri = pathExperimentB;
                break;
            }
        }
    }

    if (!experimentUri) {
        console.log('Experiment cookie has not been found. Throwing dice...');
        if (Math.random() < 0.75) {
            experimentUri = pathExperimentA;
        } else {
            experimentUri = pathExperimentB;
        }
    }

    request.uri = experimentUri;
    console.log(`Request uri set to "${request.uri}"`);
    callback(null, request);
};
```

------
#### [ Python ]

```
import json
import random

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    if request['uri'] != '/experiment-pixel.jpg':
        # Not an A/B Test
        return request

    cookieExperimentA, cookieExperimentB = 'X-Experiment-Name=A', 'X-Experiment-Name=B'
    pathExperimentA, pathExperimentB = '/experiment-group/control-pixel.jpg', '/experiment-group/treatment-pixel.jpg'

    '''
    Lambda at the Edge headers are array objects.

    Client may send multiple cookie headers. For example:
    > GET /viewerRes/test HTTP/1.1
    > User-Agent: curl/7.18.1 (x86_64-unknown-linux-gnu) libcurl/7.18.1 OpenSSL/1.0.1u zlib/1.2.3
    > Cookie: First=1; Second=2
    > Cookie: ClientCode=abc
    > Host: example.com

    You can access the first Cookie header at headers["cookie"][0].value
    and the second at headers["cookie"][1].value.

    Header values are not parsed. In the example above,
    headers["cookie"][0].value is equal to "First=1; Second=2"
    '''

    experimentUri = ""

    for cookie in headers.get('cookie', []):
        if cookieExperimentA in cookie['value']:
            print("Experiment A cookie found")
            experimentUri = pathExperimentA
            break
        elif cookieExperimentB in cookie['value']:
            print("Experiment B cookie found")
            experimentUri = pathExperimentB
            break

    if not experimentUri:
        print("Experiment cookie has not been found. Throwing dice...")
        if random.random() < 0.75:
            experimentUri = pathExperimentA
        else:
            experimentUri = pathExperimentB

    request['uri'] = experimentUri
    print(f"Request uri set to {experimentUri}")
    return request
```

------

### 範例：覆寫回應標頭
<a name="lambda-examples-overriding-response-header"></a>

以下範例說明如何根據另一個標頭的值變更回應標頭的值。

------
#### [ Node.js ]

```
export const handler = async (event) => {
    const response = event.Records[0].cf.response;
    const headers = response.headers;

    const headerNameSrc = 'X-Amz-Meta-Last-Modified';
    const headerNameDst = 'Last-Modified';

    if (headers[headerNameSrc.toLowerCase()]) {
        headers[headerNameDst.toLowerCase()] = [{
            key: headerNameDst,
            value: headers[headerNameSrc.toLowerCase()][0].value,
        }];
        console.log(`Response header "${headerNameDst}" was set to ` +
                    `"${headers[headerNameDst.toLowerCase()][0].value}"`);
    }

    return response;
};
```

------
#### [ Python ]

```
import json 

def lambda_handler(event, context):
    response = event['Records'][0]['cf']['response']
    headers = response['headers']
    
    header_name_src = 'X-Amz-Meta-Last-Modified'
    header_name_dst = 'Last-Modified'
    
    if headers.get(header_name_src.lower()):
        headers[header_name_dst.lower()] = [{
            'key': header_name_dst,
            'value': headers[header_name_src.lower()][0]['value']
        }]
        print(f'Response header "{header_name_dst}" was set to '
              f'"{headers[header_name_dst.lower()][0]["value"]}"')
    
    return response
```

------

## 產生回應：範例
<a name="lambda-examples-generated-response-examples"></a>

以下範例示範如何使用 Lambda@Edge 來產生回應。

**Topics**
+ [範例：提供靜態內容 (產生回應)](#lambda-examples-static-web-server)
+ [範例：產生 HTTP 重新導向 (產生回應)](#lambda-examples-http-redirect)

### 範例：提供靜態內容 (產生回應)
<a name="lambda-examples-static-web-server"></a>

以下範例說明如何使用 Lambda 函數提供靜態網站內容，其可減少原始伺服器的負載，並降低整體延遲。

**注意**  
您可以針對檢視器要求及原始伺服器請求事件產生 HTTP 回應。如需詳細資訊，請參閱[在請求觸發條件中產生 HTTP 回應](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests)。  
您也可以在原始伺服器回應請求事件中取代或移除 HTTP 回應的主體。如需詳細資訊，請參閱[在原始伺服器回應觸發條件中更新 HTTP 回應](lambda-generating-http-responses.md#lambda-updating-http-responses)。

------
#### [ Node.js ]

```
'use strict';

const content = `
<\!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Simple Lambda@Edge Static Content Response</title>
  </head>
  <body>
    <p>Hello from Lambda@Edge!</p>
  </body>
</html>
`;

exports.handler = (event, context, callback) => {
    /*
     * Generate HTTP OK response using 200 status code with HTML body.
     */
    const response = {
        status: '200',
        statusDescription: 'OK',
        headers: {
            'cache-control': [{
                key: 'Cache-Control',
                value: 'max-age=100'
            }],
            'content-type': [{
                key: 'Content-Type',
                value: 'text/html'
            }]
        },
        body: content,
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
import json

CONTENT = """
<\!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Simple Lambda@Edge Static Content Response</title>
</head>
<body>
    <p>Hello from Lambda@Edge!</p>
</body>
</html>
"""

def lambda_handler(event, context):
    # Generate HTTP OK response using 200 status code with HTML body.
    response = {
        'status': '200',
        'statusDescription': 'OK',
        'headers': {
            'cache-control': [
                {
                    'key': 'Cache-Control',
                    'value': 'max-age=100'
                }
            ],
            "content-type": [
                {
                    'key': 'Content-Type',
                    'value': 'text/html'
                }
            ]
        },
        'body': CONTENT
    }
    return response
```

------

### 範例：產生 HTTP 重新導向 (產生回應)
<a name="lambda-examples-http-redirect"></a>

以下範例說明如何產生 HTTP 重新導向。

**注意**  
您可以針對檢視器要求及原始伺服器請求事件產生 HTTP 回應。如需詳細資訊，請參閱[在請求觸發條件中產生 HTTP 回應](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests)。

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    /*
     * Generate HTTP redirect response with 302 status code and Location header.
     */
    const response = {
        status: '302',
        statusDescription: 'Found',
        headers: {
            location: [{
                key: 'Location',
                value: 'https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html',
            }],
        },
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):

    # Generate HTTP redirect response with 302 status code and Location header.

    response = {
        'status': '302',
        'statusDescription': 'Found',
        'headers': {
            'location': [{
                'key': 'Location',
                'value': 'https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html'
            }]
        }
    }

    return response
```

------

## 查詢字串：範例
<a name="lambda-examples-query-string-examples"></a>

以下範例示範以查詢字串使用 Lambda@Edge 的方式。

**Topics**
+ [範例：根據查詢字串參數新增標頭](#lambda-examples-header-based-on-query-string)
+ [範例：標準化查詢字串參數，以提升快取點擊率](#lambda-examples-normalize-query-string-parameters)
+ [範例：重新導向未驗證使用者到登入頁面](#lambda-examples-redirect-to-signin-page)

### 範例：根據查詢字串參數新增標頭
<a name="lambda-examples-header-based-on-query-string"></a>

以下範例說明如何取得查詢字串參數的鍵值對，然後根據這些值新增標頭。

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');
exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    
    /* When a request contains a query string key-value pair but the origin server
     * expects the value in a header, you can use this Lambda function to
     * convert the key-value pair to a header. Here's what the function does:
     * 1. Parses the query string and gets the key-value pair.
     * 2. Adds a header to the request using the key-value pair that the function got in step 1.
     */

    /* Parse request querystring to get javascript object */
    const params = querystring.parse(request.querystring);

    /* Move auth param from querystring to headers */
    const headerName = 'Auth-Header';
    request.headers[headerName.toLowerCase()] = [{ key: headerName, value: params.auth }];
    delete params.auth;

    /* Update request querystring */
    request.querystring = querystring.stringify(params);

    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs, urlencode

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    '''
    When a request contains a query string key-value pair but the origin server
    expects the value in a header, you can use this Lambda function to
    convert the key-value pair to a header. Here's what the function does:
        1. Parses the query string and gets the key-value pair.
        2. Adds a header to the request using the key-value pair that the function got in step 1.
    '''

    # Parse request querystring to get dictionary/json
    params = {k : v[0] for k, v in parse_qs(request['querystring']).items()}

    # Move auth param from querystring to headers
    headerName = 'Auth-Header'
    request['headers'][headerName.lower()] = [{'key': headerName, 'value': params['auth']}]
    del params['auth']

    # Update request querystring
    request['querystring'] = urlencode(params)

    return request
```

------

### 範例：標準化查詢字串參數，以提升快取點擊率
<a name="lambda-examples-normalize-query-string-parameters"></a>

以下範例說明如何在 CloudFront 轉送請求至您的原始伺服器之前，做以下變更至查詢字串以提高您的快取點擊率：
+ 依字母排序鍵值組的參數名稱。
+ 將鍵值組變更為小寫。

如需詳細資訊，請參閱[根據查詢字串參數快取內容](QueryStringParameters.md)。

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    /* When you configure a distribution to forward query strings to the origin and
     * to cache based on an allowlist of query string parameters, we recommend
     * the following to improve the cache-hit ratio:
     * - Always list parameters in the same order.
     * - Use the same case for parameter names and values.
     *
     * This function normalizes query strings so that parameter names and values
     * are lowercase and parameter names are in alphabetical order.
     *
     * For more information, see:
     * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/QueryStringParameters.html
     */

    console.log('Query String: ', request.querystring);

    /* Parse request query string to get javascript object */
    const params = querystring.parse(request.querystring.toLowerCase());
    const sortedParams = {};

    /* Sort param keys */
    Object.keys(params).sort().forEach(key => {
        sortedParams[key] = params[key];
    });

    /* Update request querystring with normalized  */
    request.querystring = querystring.stringify(sortedParams);

    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs, urlencode

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    '''
    When you configure a distribution to forward query strings to the origin and
    to cache based on an allowlist of query string parameters, we recommend
    the following to improve the cache-hit ratio:
    Always list parameters in the same order.
    - Use the same case for parameter names and values.

    This function normalizes query strings so that parameter names and values
    are lowercase and parameter names are in alphabetical order.

    For more information, see:
    https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/QueryStringParameters.html
    '''
    print("Query string: ", request["querystring"])

    # Parse request query string to get js object
    params = {k : v[0] for k, v in parse_qs(request['querystring'].lower()).items()}

    # Sort param keys
    sortedParams = sorted(params.items(), key=lambda x: x[0])

    # Update request querystring with normalized
    request['querystring'] = urlencode(sortedParams)
    
    return request
```

------

### 範例：重新導向未驗證使用者到登入頁面
<a name="lambda-examples-redirect-to-signin-page"></a>

以下範例說明，如果尚未輸入他們的登入資料，如何將使用者重新導向至登入頁面。

------
#### [ Node.js ]

```
'use strict';

function parseCookies(headers) {
    const parsedCookie = {};
    if (headers.cookie) {
        headers.cookie[0].value.split(';').forEach((cookie) => {
            if (cookie) {
                const parts = cookie.split('=');
                parsedCookie[parts[0].trim()] = parts[1].trim();
            }
        });
    }
    return parsedCookie;
}

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    /* Check for session-id in request cookie in viewer-request event,
     * if session-id is absent, redirect the user to sign in page with original
     * request sent as redirect_url in query params.
     */

    /* Check for session-id in cookie, if present then proceed with request */
    const parsedCookies = parseCookies(headers);
    if (parsedCookies && parsedCookies['session-id']) {
        callback(null, request);
        return;
    }

    /* URI encode the original request to be sent as redirect_url in query params */
    const encodedRedirectUrl = encodeURIComponent(`https://${headers.host[0].value}${request.uri}?${request.querystring}`);
    const response = {
        status: '302',
        statusDescription: 'Found',
        headers: {
            location: [{
                key: 'Location',
                value: `https://www.example.com/signin?redirect_url=${encodedRedirectUrl}`,
            }],
        },
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
import urllib

def parseCookies(headers):
    parsedCookie = {}
    if headers.get('cookie'):
        for cookie in headers['cookie'][0]['value'].split(';'):
            if cookie:
                parts = cookie.split('=')
                parsedCookie[parts[0].strip()] = parts[1].strip()
    return parsedCookie

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    '''
    Check for session-id in request cookie in viewer-request event,
    if session-id is absent, redirect the user to sign in page with original
    request sent as redirect_url in query params.
    '''

    # Check for session-id in cookie, if present, then proceed with request
    parsedCookies = parseCookies(headers)

    if parsedCookies and parsedCookies['session-id']:
        return request

    # URI encode the original request to be sent as redirect_url in query params
    redirectUrl = "https://%s%s?%s" % (headers['host'][0]['value'], request['uri'], request['querystring'])
    encodedRedirectUrl = urllib.parse.quote_plus(redirectUrl.encode('utf-8'))

    response = {
        'status': '302',
        'statusDescription': 'Found',
        'headers': {
            'location': [{
                'key': 'Location',
                'value': 'https://www.example.com/signin?redirect_url=%s' % encodedRedirectUrl
            }]
        }
    }
    return response
```

------

## 根據國家/地區或裝置類型標頭個人化 - 範例
<a name="lambda-examples-redirecting-examples"></a>

以下範例說明如何使用 Lambda@Edge 來根據位置或檢視器使用的裝置類型自訂行為。

**Topics**
+ [範例：將檢視器請求重新導向到國家/地區特定的 URL](#lambda-examples-redirect-based-on-country)
+ [範例：根據裝置提供不同的物件版本](#lambda-examples-vary-on-device-type)

### 範例：將檢視器請求重新導向到國家/地區特定的 URL
<a name="lambda-examples-redirect-based-on-country"></a>

以下範例說明如何產生含國家/地區特定 URL 的 HTTP 重新導向回應，並將回應傳回至檢視器。當您希望提供特定國家的回應時，此方法很有用。例如：
+ 如果您有國家/地區特定的子網域 (例如 us.example.com 和 tw.example.com)，當檢視器請求 example.com 時，您可以產生一個重新導向回應。
+ 如果您在串流視訊，但沒有在特定國家/地區串流此內容的權利，您可以在該國家/地區將使用者重新導向到說明他們為何無法檢視影片的頁面。

注意下列事項：
+ 您必須根據 `CloudFront-Viewer-Country` 標頭設定您的分佈為快取。如需詳細資訊，請參閱[根據選取請求標頭的快取](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders)。
+ 在檢視器請求事件後，CloudFront 新增 `CloudFront-Viewer-Country` 標頭。要使用此範例，您必須建立原始伺服器請求事件的觸發。

------
#### [ Node.js ]

```
'use strict';

/* This is an origin request function */
exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    /*
     * Based on the value of the CloudFront-Viewer-Country header, generate an
     * HTTP status code 302 (Redirect) response, and return a country-specific
     * URL in the Location header.
     * NOTE: 1. You must configure your distribution to cache based on the
     *          CloudFront-Viewer-Country header. For more information, see
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
     *       2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
     *          request event. To use this example, you must create a trigger for the
     *          origin request event.
     */

    let url = 'https://example.com/';
    if (headers['cloudfront-viewer-country']) {
        const countryCode = headers['cloudfront-viewer-country'][0].value;
        if (countryCode === 'TW') {
            url = 'https://tw.example.com/';
        } else if (countryCode === 'US') {
            url = 'https://us.example.com/';
        }
    }

    const response = {
        status: '302',
        statusDescription: 'Found',
        headers: {
            location: [{
                key: 'Location',
                value: url,
            }],
        },
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
# This is an origin request function

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    '''
    Based on the value of the CloudFront-Viewer-Country header, generate an
    HTTP status code 302 (Redirect) response, and return a country-specific
    URL in the Location header.
    NOTE: 1. You must configure your distribution to cache based on the
            CloudFront-Viewer-Country header. For more information, see
            https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
          2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
            request event. To use this example, you must create a trigger for the
            origin request event.
    '''

    url = 'https://example.com/'
    viewerCountry = headers.get('cloudfront-viewer-country')
    if viewerCountry:
        countryCode = viewerCountry[0]['value']
        if countryCode == 'TW':
            url = 'https://tw.example.com/'
        elif countryCode == 'US':
            url = 'https://us.example.com/'

    response = {
        'status': '302',
        'statusDescription': 'Found',
        'headers': {
            'location': [{
                'key': 'Location',
                'value': url
            }]
        }
    }

    return response
```

------

### 範例：根據裝置提供不同的物件版本
<a name="lambda-examples-vary-on-device-type"></a>

以下範例說明如何根據使用者使用的裝置類型，提供不同的物件版本，例如行動裝置或平板電腦。注意下列事項：
+ 您必須根據 `CloudFront-Is-*-Viewer` 標頭設定您的分佈為快取。如需詳細資訊，請參閱[根據選取請求標頭的快取](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders)。
+ 在檢視器請求事件後，CloudFront 新增 `CloudFront-Is-*-Viewer` 標頭。要使用此範例，您必須建立原始伺服器請求事件的觸發。

------
#### [ Node.js ]

```
'use strict';

/* This is an origin request function */
exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    /*
     * Serve different versions of an object based on the device type.
     * NOTE: 1. You must configure your distribution to cache based on the
     *          CloudFront-Is-*-Viewer headers. For more information, see
     *          the following documentation:
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-device-type
     *       2. CloudFront adds the CloudFront-Is-*-Viewer headers after the viewer
     *          request event. To use this example, you must create a trigger for the
     *          origin request event.
     */

    const desktopPath = '/desktop';
    const mobilePath = '/mobile';
    const tabletPath = '/tablet';
    const smarttvPath = '/smarttv';

    if (headers['cloudfront-is-desktop-viewer']
        && headers['cloudfront-is-desktop-viewer'][0].value === 'true') {
        request.uri = desktopPath + request.uri;
    } else if (headers['cloudfront-is-mobile-viewer']
               && headers['cloudfront-is-mobile-viewer'][0].value === 'true') {
        request.uri = mobilePath + request.uri;
    } else if (headers['cloudfront-is-tablet-viewer']
               && headers['cloudfront-is-tablet-viewer'][0].value === 'true') {
        request.uri = tabletPath + request.uri;
    } else if (headers['cloudfront-is-smarttv-viewer']
               && headers['cloudfront-is-smarttv-viewer'][0].value === 'true') {
        request.uri = smarttvPath + request.uri;
    }
    console.log(`Request uri set to "${request.uri}"`);

    callback(null, request);
};
```

------
#### [ Python ]

```
# This is an origin request function
def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    '''
    Serve different versions of an object based on the device type.
    NOTE: 1. You must configure your distribution to cache based on the
            CloudFront-Is-*-Viewer headers. For more information, see
            the following documentation:
            https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
            https://docs.aws.amazon.com/console/cloudfront/cache-on-device-type
          2. CloudFront adds the CloudFront-Is-*-Viewer headers after the viewer
            request event. To use this example, you must create a trigger for the
            origin request event.
    '''

    desktopPath = '/desktop';
    mobilePath = '/mobile';
    tabletPath = '/tablet';
    smarttvPath = '/smarttv';

    if 'cloudfront-is-desktop-viewer' in headers and headers['cloudfront-is-desktop-viewer'][0]['value'] == 'true':
        request['uri'] = desktopPath + request['uri']
    elif 'cloudfront-is-mobile-viewer' in headers and headers['cloudfront-is-mobile-viewer'][0]['value'] == 'true':
        request['uri'] = mobilePath + request['uri']
    elif 'cloudfront-is-tablet-viewer' in headers and headers['cloudfront-is-tablet-viewer'][0]['value'] == 'true':
        request['uri'] = tabletPath + request['uri']
    elif 'cloudfront-is-smarttv-viewer' in headers and headers['cloudfront-is-smarttv-viewer'][0]['value'] == 'true':
        request['uri'] = smarttvPath + request['uri']

    print("Request uri set to %s" % request['uri'])

    return request
```

------

## 以內容為基礎的動態原始伺服器選擇 - 範例
<a name="lambda-examples-content-based-routing-examples"></a>

以下範例示範如何使用 Lambda@Edge 來根據請求中的資訊路由到不同的原始伺服器。

**Topics**
+ [範例：使用原始伺服器請求觸發條件，以從自訂原始伺服器變更到 Amazon S3 原始伺服器](#lambda-examples-content-based-S3-origin-based-on-query)
+ [範例：使用原始伺服器請求觸發條件，以變更 Amazon S3 原始伺服器區域](#lambda-examples-content-based-S3-origin-request-trigger)
+ [範例：使用原始伺服器請求觸發條件，變更 Amazon S3 原始伺服器為自訂原始伺服器](#lambda-examples-content-based-custom-origin-request-trigger)
+ [範例：使用原始伺服器請求觸發條件，將流量自一個 Amazon S3 儲存貯體逐漸傳輸到另一個。](#lambda-examples-content-based-gradual-traffic-transfer)
+ [範例：使用原始伺服器請求觸發，根據國家/地區標頭變更原始網域名稱](#lambda-examples-content-based-geo-header)

### 範例：使用原始伺服器請求觸發條件，以從自訂原始伺服器變更到 Amazon S3 原始伺服器
<a name="lambda-examples-content-based-S3-origin-based-on-query"></a>

此函數示範如何使用 origin-request 觸發條件，根據請求屬性從自訂原始伺服器變更至內容被擷取的 Amazon S3 原始伺服器。

------
#### [ Node.js ]

```
'use strict';

 const querystring = require('querystring');
 
 exports.handler = (event, context, callback) => {
     const request = event.Records[0].cf.request;
 
     /**
      * Reads query string to check if S3 origin should be used, and
      * if true, sets S3 origin properties.
      */
 
     const params = querystring.parse(request.querystring);
 
     if (params['useS3Origin']) {
         if (params['useS3Origin'] === 'true') {
             const s3DomainName = 'amzn-s3-demo-bucket.s3.amazonaws.com';
 
             /* Set S3 origin fields */
             request.origin = {
                 s3: {
                     domainName: s3DomainName,
                     region: '',
                     authMethod: 'origin-access-identity',
                     path: '',
                     customHeaders: {}
                 }
             };
             request.headers['host'] = [{ key: 'host', value: s3DomainName}];
         }
     }
     
    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    '''
    Reads query string to check if S3 origin should be used, and
    if true, sets S3 origin properties
    '''
    params = {k: v[0] for k, v in parse_qs(request['querystring']).items()}
    if params.get('useS3Origin') == 'true':
        s3DomainName = 'amzn-s3-demo-bucket.s3.amazonaws.com'

        # Set S3 origin fields
        request['origin'] = {
            's3': {
                'domainName': s3DomainName,
                'region': '',
                'authMethod': 'origin-access-identity',
                'path': '',
                'customHeaders': {}
            }
        }
        request['headers']['host'] = [{'key': 'host', 'value': s3DomainName}]
    return request
```

------

### 範例：使用原始伺服器請求觸發條件，以變更 Amazon S3 原始伺服器區域
<a name="lambda-examples-content-based-S3-origin-request-trigger"></a>

此函數示範如何根據請求屬性，使用 origin-request 觸發條件來變更內容被擷取的 Amazon S3 原始伺服器。

在這個範例中，我們使用 `CloudFront-Viewer-Country` 標頭的值來將 S3 儲存貯體的網域名稱更新為較靠近檢視器的區域內的儲存貯體。這在數種方式中非常受用：
+ 當指定的區域更靠近檢視器的國家/地區時，能減少延遲。
+ 藉由確保該資料與請求來自位於相同國家/地區的原始伺服器，提供資料主權服務。

要使用此範例，須執行下列項目：
+ 您必須根據 `CloudFront-Viewer-Country` 標頭設定您的分佈為快取。如需詳細資訊，請參閱[根據選取請求標頭的快取](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders)。
+ 在原始伺服器請求事件中建立此函數的觸發。CloudFront 將在檢視者請求事件後新增 `CloudFront-Viewer-Country` 標頭，所以若要使用此範例，必須確認函數會執行原始請求。

**注意**  
下列範例程式碼針對您用於原始伺服器的所有 S3 儲存貯體使用相同的原始存取身分 (OAI)。如需更多詳細資訊，請參閱[原始存取身分](private-content-restricting-access-to-s3.md#private-content-restricting-access-to-s3-oai)。

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;

    /**
     * This blueprint demonstrates how an origin-request trigger can be used to
     * change the origin from which the content is fetched, based on request properties.
     * In this example, we use the value of the CloudFront-Viewer-Country header
     * to update the S3 bucket domain name to a bucket in a Region that is closer to
     * the viewer.
     * 
     * This can be useful in several ways:
     *      1) Reduces latencies when the Region specified is nearer to the viewer's
     *         country.
     *      2) Provides data sovereignty by making sure that data is served from an
     *         origin that's in the same country that the request came from.
     * 
     * NOTE: 1. You must configure your distribution to cache based on the
     *          CloudFront-Viewer-Country header. For more information, see
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
     *       2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
     *          request event. To use this example, you must create a trigger for the
     *          origin request event.
     */

    const countryToRegion = {
        'DE': 'eu-central-1',
        'IE': 'eu-west-1',
        'GB': 'eu-west-2',
        'FR': 'eu-west-3',
        'JP': 'ap-northeast-1',
        'IN': 'ap-south-1'
    };

    if (request.headers['cloudfront-viewer-country']) {
        const countryCode = request.headers['cloudfront-viewer-country'][0].value;
        const region = countryToRegion[countryCode];
        
        /**
         * If the viewer's country is not in the list you specify, the request
         * goes to the default S3 bucket you've configured.
         */  
        if (region) {
            /**
             * If you've set up OAI, the bucket policy in the destination bucket
             * should allow the OAI GetObject operation, as configured by default
             * for an S3 origin with OAI. Another requirement with OAI is to provide
             * the Region so it can be used for the SIGV4 signature. Otherwise, the
             * Region is not required.
             */
            request.origin.s3.region = region;
            const domainName = `amzn-s3-demo-bucket-in-${region}.s3.${region}.amazonaws.com`;
            request.origin.s3.domainName = domainName;
            request.headers['host'] = [{ key: 'host', value: domainName }];
        }
    }

    callback(null, request);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    '''
    This blueprint demonstrates how an origin-request trigger can be used to
    change the origin from which the content is fetched, based on request properties.
    In this example, we use the value of the CloudFront-Viewer-Country header
    to update the S3 bucket domain name to a bucket in a Region that is closer to
    the viewer.
    
    This can be useful in several ways:
        1) Reduces latencies when the Region specified is nearer to the viewer's
            country.
        2) Provides data sovereignty by making sure that data is served from an
            origin that's in the same country that the request came from.
    
    NOTE: 1. You must configure your distribution to cache based on the
            CloudFront-Viewer-Country header. For more information, see
            https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
          2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
            request event. To use this example, you must create a trigger for the
            origin request event.
    '''

    countryToRegion = {
        'DE': 'eu-central-1',
        'IE': 'eu-west-1',
        'GB': 'eu-west-2',
        'FR': 'eu-west-3',
        'JP': 'ap-northeast-1',
        'IN': 'ap-south-1'
    }

    viewerCountry = request['headers'].get('cloudfront-viewer-country')
    if viewerCountry:
        countryCode = viewerCountry[0]['value']
        region = countryToRegion.get(countryCode)

        # If the viewer's country in not in the list you specify, the request
        # goes to the default S3 bucket you've configured
        if region:
            '''
            If you've set up OAI, the bucket policy in the destination bucket
            should allow the OAI GetObject operation, as configured by default
            for an S3 origin with OAI. Another requirement with OAI is to provide
            the Region so it can be used for the SIGV4 signature. Otherwise, the
            Region is not required.
            '''
            request['origin']['s3']['region'] = region
            domainName = 'amzn-s3-demo-bucket-in-{0}.s3.{0}.amazonaws.com'.format(region)
            request['origin']['s3']['domainName'] = domainName
            request['headers']['host'] = [{'key': 'host', 'value': domainName}]

    return request
```

------

### 範例：使用原始伺服器請求觸發條件，變更 Amazon S3 原始伺服器為自訂原始伺服器
<a name="lambda-examples-content-based-custom-origin-request-trigger"></a>

此函數示範如何根據請求屬性，使用原始伺服器請求觸發來變更內容被擷取的自訂原始伺服器。

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');
 
 exports.handler = (event, context, callback) => {
     const request = event.Records[0].cf.request;
 
     /**
      * Reads query string to check if custom origin should be used, and
      * if true, sets custom origin properties.
      */
 
     const params = querystring.parse(request.querystring);
 
     if (params['useCustomOrigin']) {
         if (params['useCustomOrigin'] === 'true') {
 
             /* Set custom origin fields*/
             request.origin = {
                 custom: {
                     domainName: 'www.example.com',
                     port: 443,
                     protocol: 'https',
                     path: '',
                     sslProtocols: ['TLSv1', 'TLSv1.1'],
                     readTimeout: 5,
                     keepaliveTimeout: 5,
                     customHeaders: {}
                 }
             };
             request.headers['host'] = [{ key: 'host', value: 'www.example.com'}];
         }
     }
    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    # Reads query string to check if custom origin should be used, and
    # if true, sets custom origin properties

    params = {k: v[0] for k, v in parse_qs(request['querystring']).items()}

    if params.get('useCustomOrigin') == 'true':
            # Set custom origin fields
            request['origin'] = {
                'custom': {
                    'domainName': 'www.example.com',
                    'port': 443,
                    'protocol': 'https',
                    'path': '',
                    'sslProtocols': ['TLSv1', 'TLSv1.1'],
                    'readTimeout': 5,
                    'keepaliveTimeout': 5,
                    'customHeaders': {}
                }
            }
            request['headers']['host'] = [{'key': 'host', 'value': 'www.example.com'}]

    return request
```

------

### 範例：使用原始伺服器請求觸發條件，將流量自一個 Amazon S3 儲存貯體逐漸傳輸到另一個。
<a name="lambda-examples-content-based-gradual-traffic-transfer"></a>

此函數示範如何以可控的方式，從一個 Amazon S3 儲存貯體逐步傳輸流量到另一個儲存貯體。

------
#### [ Node.js ]

```
'use strict';

    function getRandomInt(min, max) {
        /* Random number is inclusive of min and max*/
        return Math.floor(Math.random() * (max - min + 1)) + min;
 }

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const BLUE_TRAFFIC_PERCENTAGE = 80;

    /**
      * This Lambda function demonstrates how to gradually transfer traffic from
      * one S3 bucket to another in a controlled way.
      * We define a variable BLUE_TRAFFIC_PERCENTAGE which can take values from
      * 1 to 100. If the generated randomNumber less than or equal to BLUE_TRAFFIC_PERCENTAGE, traffic
      * is re-directed to blue-bucket. If not, the default bucket that we've configured
      * is used.
      */

    const randomNumber = getRandomInt(1, 100);

if (randomNumber <= BLUE_TRAFFIC_PERCENTAGE) {
         const domainName = 'blue-bucket.s3.amazonaws.com';
         request.origin.s3.domainName = domainName;
         request.headers['host'] = [{ key: 'host', value: domainName}];
     }
    callback(null, request);
};
```

------
#### [ Python ]

```
import math
import random

def getRandomInt(min, max):
    # Random number is inclusive of min and max
    return math.floor(random.random() * (max - min + 1)) + min

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    BLUE_TRAFFIC_PERCENTAGE = 80

    '''
    This Lambda function demonstrates how to gradually transfer traffic from
    one S3 bucket to another in a controlled way.
    We define a variable BLUE_TRAFFIC_PERCENTAGE which can take values from
    1 to 100. If the generated randomNumber less than or equal to BLUE_TRAFFIC_PERCENTAGE, traffic
    is re-directed to blue-bucket. If not, the default bucket that we've configured
    is used.
    '''

    randomNumber = getRandomInt(1, 100)

    if randomNumber <= BLUE_TRAFFIC_PERCENTAGE:
        domainName = 'blue-bucket.s3.amazonaws.com'
        request['origin']['s3']['domainName'] = domainName
        request['headers']['host'] = [{'key': 'host', 'value': domainName}]

    return request
```

------

### 範例：使用原始伺服器請求觸發，根據國家/地區標頭變更原始網域名稱
<a name="lambda-examples-content-based-geo-header"></a>

此函數示範如何根據 `CloudFront-Viewer-Country` 標頭來變更原始伺服器的網域名稱，使內容能從靠近檢視者國家/地區的原始伺服器提供。

為您的分佈實施此功能可以擁有如下所述的優勢：
+ 當指定的區域更靠近檢視器的國家/地區時，可減少延遲
+ 藉由確保該資料與請求來自位於相同國家/地區的原始伺服器來提供資料主權服務

請注意，要啟用此功能，您必須根據 `CloudFront-Viewer-Country` 標頭設定您的分佈為快取。如需詳細資訊，請參閱[根據選取請求標頭的快取](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders)。

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
     const request = event.Records[0].cf.request;
     
  if (request.headers['cloudfront-viewer-country']) {
         const countryCode = request.headers['cloudfront-viewer-country'][0].value;
         if (countryCode === 'GB' || countryCode === 'DE' || countryCode === 'IE' ) {
             const domainName = 'eu.example.com';
             request.origin.custom.domainName = domainName;
             request.headers['host'] = [{key: 'host', value: domainName}];
         } 
     }
     
    callback(null, request);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    viewerCountry = request['headers'].get('cloudfront-viewer-country')
    if viewerCountry:
        countryCode = viewerCountry[0]['value']
        if countryCode == 'GB' or countryCode == 'DE' or countryCode == 'IE':
            domainName = 'eu.example.com'
            request['origin']['custom']['domainName'] = domainName
            request['headers']['host'] = [{'key': 'host', 'value': domainName}]
    return request
```

------

## 更新錯誤狀態：範例
<a name="lambda-examples-update-error-status-examples"></a>

以下範例提供指導，說明如何使用 Lambda@Edge 來變更傳回給使用者的錯誤狀態。

**Topics**
+ [範例：使用原始伺服器請求觸發以更新錯誤狀態碼為 200](#lambda-examples-custom-error-static-body)
+ [範例：使用原始伺服器請求觸發以更新錯誤狀態碼為 302](#lambda-examples-custom-error-new-site)

### 範例：使用原始伺服器請求觸發以更新錯誤狀態碼為 200
<a name="lambda-examples-custom-error-static-body"></a>

此函數示範如何更新回應狀態為 200 並產生靜態本文內容，以在以下案例傳回給檢視器：
+ 函數在原始伺服器回應中觸發。
+ 原始伺服器的回應狀態為錯誤狀態碼 (4xx 和 5xx)

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const response = event.Records[0].cf.response;

    /**
     * This function updates the response status to 200 and generates static
     * body content to return to the viewer in the following scenario:
     * 1. The function is triggered in an origin response
     * 2. The response status from the origin server is an error status code (4xx or 5xx)
     */

    if (response.status >= 400 && response.status <= 599) {
        response.status = 200;
        response.statusDescription = 'OK';
        response.body = 'Body generation example';
    }

    callback(null, response);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    response = event['Records'][0]['cf']['response']

    '''
    This function updates the response status to 200 and generates static
    body content to return to the viewer in the following scenario:
    1. The function is triggered in an origin response
    2. The response status from the origin server is an error status code (4xx or 5xx)
    '''

    if int(response['status']) >= 400 and int(response['status']) <= 599:
        response['status'] = 200
        response['statusDescription'] = 'OK'
        response['body'] = 'Body generation example'
    return response
```

------

### 範例：使用原始伺服器請求觸發以更新錯誤狀態碼為 302
<a name="lambda-examples-custom-error-new-site"></a>

此函數示範如何更新 HTTP 狀態碼為 302，以重新導向到另一個由不同原始伺服器設定的路徑 (快取行為)。注意下列事項：
+ 函數在原始伺服器回應中觸發。
+ 原始伺服器的回應狀態為錯誤狀態碼 (4xx 和 5xx)

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const response = event.Records[0].cf.response;
    const request = event.Records[0].cf.request;

    /**
     * This function updates the HTTP status code in the response to 302, to redirect to another
     * path (cache behavior) that has a different origin configured. Note the following:
     * 1. The function is triggered in an origin response
     * 2. The response status from the origin server is an error status code (4xx or 5xx)
     */

    if (response.status >= 400 && response.status <= 599) {
        const redirect_path = `/plan-b/path?${request.querystring}`;

        response.status = 302;
        response.statusDescription = 'Found';

        /* Drop the body, as it is not required for redirects */
        response.body = '';
        response.headers['location'] = [{ key: 'Location', value: redirect_path }];
    }

    callback(null, response);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    response = event['Records'][0]['cf']['response']
    request = event['Records'][0]['cf']['request']

    '''
    This function updates the HTTP status code in the response to 302, to redirect to another
    path (cache behavior) that has a different origin configured. Note the following:
    1. The function is triggered in an origin response
    2. The response status from the origin server is an error status code (4xx or 5xx)
    '''

    if int(response['status']) >= 400 and int(response['status']) <= 599:
        redirect_path = '/plan-b/path?%s' % request['querystring']

        response['status'] = 302
        response['statusDescription'] = 'Found'

        # Drop the body as it is not required for redirects
        response['body'] = ''
        response['headers']['location'] = [{'key': 'Location', 'value': redirect_path}]

    return response
```

------

## 存取請求本文：範例
<a name="lambda-examples-access-request-body-examples"></a>

以下範例說明如何使用 Lambda@Edge 來處理 POST 請求。

**注意**  
若要使用這些範例，您必須在分佈的 Lambda 函數關聯中啟用*包含內文*選項。依預設不會啟用此功能。  
若要在 CloudFront 主控台中啟用此設定，請選取 **Lambda 函數關聯**中**包含內文**的核取方塊。
若要在 CloudFront API 中或使用 啟用此設定 CloudFormation，請在 中將 `IncludeBody` 欄位設定為 `true` `LambdaFunctionAssociation`。

**Topics**
+ [範例：使用請求觸發條件來讀取 HTML 表單](#lambda-examples-access-request-body-examples-read)
+ [範例：使用請求觸發條件來修改 HTML 表單](#lambda-examples-access-request-body-examples-replace)

### 範例：使用請求觸發條件來讀取 HTML 表單
<a name="lambda-examples-access-request-body-examples-read"></a>

此函數示範如何處理 HTML 表單 (Web 表單) 所產生 POST 請求的主體，例如「聯絡我們」表單。例如，您可能會有如下的 HTML 表單：

```
<html>
  <form action="https://example.com" method="post">
    Param 1: <input type="text" name="name1"><br>
    Param 2: <input type="text" name="name2"><br>
    input type="submit" value="Submit">
  </form>
</html>
```

至於後面的範例函數，該函數必須在 CloudFront 檢視器請求或原始伺服器請求中觸發。

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');

/**
 * This function demonstrates how you can read the body of a POST request 
 * generated by an HTML form (web form). The function is triggered in a
 * CloudFront viewer request or origin request event type.
 */

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;

    if (request.method === 'POST') {
        /* HTTP body is always passed as base64-encoded string. Decode it. */
        const body = Buffer.from(request.body.data, 'base64').toString();
 
        /* HTML forms send the data in query string format. Parse it. */
        const params = querystring.parse(body);
 
        /* For demonstration purposes, we only log the form fields here.
         * You can put your custom logic here. For example, you can store the 
         * fields in a database, such as Amazon DynamoDB, and generate a response
         * right from your Lambda@Edge function.
         */
        for (let param in params) {
            console.log(`For "${param}" user submitted "${params[param]}".\n`);
        }
    }
    return callback(null, request);
};
```

------
#### [ Python ]

```
import base64
from urllib.parse import parse_qs

'''
Say there is a POST request body generated by an HTML such as:

<html>
<form action="https://example.com" method="post">
    Param 1: <input type="text" name="name1"><br>
    Param 2: <input type="text" name="name2"><br>
    input type="submit" value="Submit">
</form>
</html>

'''

'''
This function demonstrates how you can read the body of a POST request 
generated by an HTML form (web form). The function is triggered in a
CloudFront viewer request or origin request event type.
'''

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    if request['method'] == 'POST':
        # HTTP body is always passed as base64-encoded string. Decode it
        body = base64.b64decode(request['body']['data'])

        # HTML forms send the data in query string format. Parse it
        params = {k: v[0] for k, v in parse_qs(body).items()}

        '''
        For demonstration purposes, we only log the form fields here.
        You can put your custom logic here. For example, you can store the
        fields in a database, such as Amazon DynamoDB, and generate a response
        right from your Lambda@Edge function.
        '''
        for key, value in params.items():
            print("For %s use submitted %s" % (key, value))
            
    return request
```

------

### 範例：使用請求觸發條件來修改 HTML 表單
<a name="lambda-examples-access-request-body-examples-replace"></a>

此函數示範如何修改 HTML 表單 (Web 表單) 所產生 POST 請求的主體。此函數會在 CloudFront 檢視器請求或原始伺服器請求中觸發。

------
#### [ Node.js ]

```
'use strict';
				
const querystring = require('querystring');

exports.handler = (event, context, callback) => {
    var request = event.Records[0].cf.request;
    if (request.method === 'POST') {
        /* Request body is being replaced. To do this, update the following
        /* three fields:
         *    1) body.action to 'replace'
         *    2) body.encoding to the encoding of the new data.
         *
         *       Set to one of the following values:
         *
         *           text - denotes that the generated body is in text format.
         *               Lambda@Edge will propagate this as is.
         *           base64 - denotes that the generated body is base64 encoded.
         *               Lambda@Edge will base64 decode the data before sending
         *               it to the origin.
         *    3) body.data to the new body.
         */
        request.body.action = 'replace';
        request.body.encoding = 'text';
        request.body.data = getUpdatedBody(request);
    }
    callback(null, request);
};

function getUpdatedBody(request) {
    /* HTTP body is always passed as base64-encoded string. Decode it. */
    const body = Buffer.from(request.body.data, 'base64').toString();

    /* HTML forms send data in query string format. Parse it. */
    const params = querystring.parse(body);

    /* For demonstration purposes, we're adding one more param.
     *
     * You can put your custom logic here. For example, you can truncate long
     * bodies from malicious requests.
     */
    params['new-param-name'] = 'new-param-value';
    return querystring.stringify(params);
}
```

------
#### [ Python ]

```
import base64
from urllib.parse import parse_qs, urlencode

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    if request['method'] == 'POST':
        '''
        Request body is being replaced. To do this, update the following
        three fields:
            1) body.action to 'replace'
            2) body.encoding to the encoding of the new data.
        
            Set to one of the following values:
        
                text - denotes that the generated body is in text format.
                    Lambda@Edge will propagate this as is.
                base64 - denotes that the generated body is base64 encoded.
                    Lambda@Edge will base64 decode the data before sending
                    it to the origin.
            3) body.data to the new body.
        '''
        request['body']['action'] = 'replace'
        request['body']['encoding'] = 'text'
        request['body']['data'] = getUpdatedBody(request)
    return request

def getUpdatedBody(request):
    # HTTP body is always passed as base64-encoded string. Decode it
    body = base64.b64decode(request['body']['data'])

    # HTML forms send data in query string format. Parse it
    params = {k: v[0] for k, v in parse_qs(body).items()}

    # For demonstration purposes, we're adding one more param

    # You can put your custom logic here. For example, you can truncate long
    # bodies from malicious requests
    params['new-param-name'] = 'new-param-value'
    return urlencode(params)
```

------

# 對邊緣函數的限制
<a name="edge-functions-restrictions"></a>

下列主題說明適用於 CloudFront Functions 和 Lambda@Edge 的限制。某些限制適用於所有邊緣函數，而其他限制僅適用於 CloudFront Functions 或 Lambda@Edge。

每個主題都提供有關使用 CloudFront 開發和部署邊緣函數時應考慮的限制和約束詳細資訊。

了解這些限制可協助您確保邊緣函數如預期運作，並符合支援的功能。

**Topics**
+ [對所有邊緣函數的限制](edge-function-restrictions-all.md)
+ [對 CloudFront Functions 的限制](cloudfront-function-restrictions.md)
+ [對 Lambda@Edge 的限制](lambda-at-edge-function-restrictions.md)

如需有關配額 (先前稱為限制) 的詳細資訊，請參閱 [CloudFront Functions 上的配額](cloudfront-limits.md#limits-functions) 和 [Lambda@Edge 的配額](cloudfront-limits.md#limits-lambda-at-edge)。

# 對所有邊緣函數的限制
<a name="edge-function-restrictions-all"></a>

下列限制適用於所有邊緣函數，包括 CloudFront Functions 和 Lambda@Edge。

**Topics**
+ [AWS 帳戶 擁有權](#function-restrictions-account-ownership)
+ [結合 CloudFront Functions 與 Lambda@Edge](#function-restrictions-combining-functions)
+ [HTTP 狀態碼](#function-restrictions-status-codes)
+ [HTTP 標頭](#function-restrictions-headers)
+ [查詢字串](#function-restrictions-query-strings)
+ [URI](#function-restrictions-uri)
+ [URI、查詢字串，以及標頭編碼](#function-restrictions-encoding)
+ [Microsoft Smooth Streaming](#function-restrictions-microsoft-smooth-streaming)
+ [標記](#function-restrictions-tagging)

## AWS 帳戶 擁有權
<a name="function-restrictions-account-ownership"></a>

若要將邊緣函數與 CloudFront 分佈產生關聯，函數和分佈必須由相同的 AWS 帳戶擁有。

## 結合 CloudFront Functions 與 Lambda@Edge
<a name="function-restrictions-combining-functions"></a>

下列限制適用於指定的快取行為：
+ 每個事件類型 (檢視器請求、原始伺服器請求、原始伺服器回應和檢視器回應) 只能有一個邊緣函數關聯。
+ 您無法在檢視器事件 (檢視器請求和檢視器回應) 中結合 CloudFront Functions 和 Lambda@Edge。

允許邊緣函數的所有其他組合。下表說明了允許的組合。

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/AmazonCloudFront/latest/DeveloperGuide/edge-function-restrictions-all.html)

## HTTP 狀態碼
<a name="function-restrictions-status-codes"></a>

當原始伺服器傳回 HTTP 狀態碼 400 (或更高值) 時，CloudFront 不會針對檢視器回應事件調用邊緣函數。

CloudFront 會針對*所有*原始伺服器回應，呼叫原始伺服器回應事件的 Lambda@Edge 函數，包括在原始伺服器傳回 HTTP 狀態碼 400 (或更高值) 時。如需詳細資訊，請參閱 [在原始伺服器回應觸發條件中更新 HTTP 回應](lambda-generating-http-responses.md#lambda-updating-http-responses)。

## HTTP 標頭
<a name="function-restrictions-headers"></a>

某些 HTTP 標頭不允許使用，這意味著這些標頭不會公開給邊緣函數，且函數無法新增這些標頭。其他標頭則設為唯讀，代表函數可以讀取這些標頭，但無法新增、修改或刪除。

**Topics**
+ [不允許的標頭](#function-restrictions-disallowed-headers)
+ [唯讀標頭](#function-restrictions-read-only-headers)

### 不允許的標頭
<a name="function-restrictions-disallowed-headers"></a>

下列 HTTP 標頭不會公開給邊緣函數，且函數無法新增這些標頭。如果您的函數新增這些標頭之一，CloudFront 驗證請求會失敗，且 CloudFront 會傳回 HTTP 狀態碼 502 (無效的閘道) 給檢視器。
+ `Connection` 
+ `Expect`
+ `Keep-Alive`
+ `Proxy-Authenticate`
+ `Proxy-Authorization`
+ `Proxy-Connection`
+ `Trailer`
+ `Upgrade`
+ `X-Accel-Buffering`
+ `X-Accel-Charset`
+ `X-Accel-Limit-Rate`
+ `X-Accel-Redirect`
+ `X-Amz-Cf-*`
+ `X-Amzn-Auth`
+ `X-Amzn-Cf-Billing`
+ `X-Amzn-Cf-Id`
+ `X-Amzn-Cf-Xff`
+ `X-Amzn-Errortype`
+ `X-Amzn-Fle-Profile`
+ `X-Amzn-Header-Count`
+ `X-Amzn-Header-Order`
+ `X-Amzn-Lambda-Integration-Tag`
+ `X-Amzn-RequestId`
+ `X-Cache`
+ `X-Edge-*`
+ `X-Forwarded-Proto`
+ `X-Real-IP`

### 唯讀標頭
<a name="function-restrictions-read-only-headers"></a>

下列標頭為唯讀的狀態。您的函數可以讀取這些標頭並將其作為函數邏輯的輸入，但無法變更其值。如果您的函數新增或編輯了唯讀標頭，請求會無法通過 CloudFront 驗證，而且 CloudFront 會傳回 HTTP 狀態碼 502 (無效的閘道) 給檢視器。

#### 檢視器請求事件中的唯讀標頭
<a name="function-restrictions-read-only-headers-viewer-request"></a>

下列標頭在檢視器請求事件中為唯讀的狀態。
+ `CDN-Loop`
+ `Content-Length`
+ `Host`
+ `Transfer-Encoding`
+ `Via`

#### 原始伺服器請求事件中的唯讀標頭 (僅限 Lambda@Edge)
<a name="function-restrictions-read-only-headers-origin-request"></a>

下列標頭在原始伺服器請求事件中為唯讀的狀態，僅存在於 Lambda@Edge 中。
+ `Accept-Encoding`
+ `CDN-Loop`
+ `Content-Length`
+ `If-Modified-Since`
+ `If-None-Match`
+ `If-Range`
+ `If-Unmodified-Since`
+ `Transfer-Encoding`
+ `Via`

#### 原始伺服器回應事件中的唯讀標頭 (僅限 Lambda@Edge)
<a name="function-restrictions-read-only-headers-origin-response"></a>

下列標頭在原始伺服器回應事件中為唯讀的狀態，僅存在於 Lambda@Edge 中。
+ `Transfer-Encoding`
+ `Via`

#### 檢視器回應事件中的唯讀標頭
<a name="function-restrictions-read-only-headers-viewer-response"></a>

下列標頭在 CloudFront Functions 與 Lambda@Edge 的檢視器回應事件中為唯讀。
+ `Warning`
+ `Via`

下列標頭在 Lambda@Edge 檢視器回應事件中為唯讀狀態。
+ `Content-Length`
+ `Content-Encoding`
+ `Transfer-Encoding`

## 查詢字串
<a name="function-restrictions-query-strings"></a>

下列限制適用於讀取、更新或在請求 URI 中建立查詢字串的函數。
+ (僅限 Lambda@Edge) 若要存取原始伺服器請求或原始伺服器回應函數中的查詢字串，您的快取政策或原始伺服器請求政策必須針對**查詢字串**設定為 **All** (全部)。
+ 函數可以為檢視器請求和原始伺服器請求事件建立或更新查詢字串 (原始伺服器請求事件僅存在於 Lambda@Edge 中)。
+ 函數可以讀取查詢字串，但無法為原始伺服器回應和檢視器回應事件建立或更新查詢字串 (原始伺服器回應事件僅存在於 Lambda@Edge 中)。
+ 如果函數建立或更新查詢字串，將適用下列限制：
  + 查詢字串不可包含空格、控制字元或片段識別碼 (`#`)。
  + URI 的總大小 (包含查詢字串) 必須小於 8,192 個字元。
  + 我們建議您於 URI 和查詢字串使用 % 編碼。如需詳細資訊，請參閱[URI、查詢字串，以及標頭編碼](#function-restrictions-encoding)。

## URI
<a name="function-restrictions-uri"></a>

如果函數為請求變更了 URI，這不會改變針對請求進行的快取動作，也不會改變請求轉傳目的地的原始伺服器。

URI 的總大小 (包含查詢字串) 必須小於 8,192 個字元。

## URI、查詢字串，以及標頭編碼
<a name="function-restrictions-encoding"></a>

傳遞給邊緣函數的 URI、查詢字串以及標頭值是使用 UTF-8 編碼。您的函數應針對其傳回的 URI、查詢字串和標頭值使用 UTF-8 編碼。百分比編碼與 UTF-8 編碼相容。

下列清單說明了 CloudFront 如何處理 URI、查詢字串值以及標頭的編碼：
+ 如果請求中的值為 UTF-8 編碼，CloudFront 不需變更值，即可將其轉傳給函數。
+ 當請求中的值採用 [ISO-8859-1 編碼](https://en.wikipedia.org/wiki/ISO/IEC_8859-1)時，CloudFront 會將這些值轉換為 UTF-8 編碼，然後再將其轉傳給函數。
+ 當請求中的值採用其他字元編碼時，CloudFront 會假設其為 ISO-8859-1 編碼，並嘗試將 ISO-8859-1 編碼轉換為 UTF-8 編碼。
**重要**  
轉換後的字元中的值可能是原始伺服器請求的不正確轉譯。這可能會導致函數或原始伺服器產生意外結果。

CloudFront 轉傳給您的原始伺服器的 URI、查詢字符串和標頭值，取決於函數是否變更這些值：
+ 如果函數未變更 URI、查詢字串或標頭，CloudFront 會將請求所收到的值轉傳給您的原始伺服器。
+ 如果函數變更了 URI 查詢字串或標頭，CloudFront 會轉傳以 UTF-8 編碼的值。

## Microsoft Smooth Streaming
<a name="function-restrictions-microsoft-smooth-streaming"></a>

您無法將邊緣函數與已轉碼為 Microsoft Smooth Streaming 格式之串流媒體檔案的 CloudFront 分佈與搭配使用。

## 標記
<a name="function-restrictions-tagging"></a>

您無法將標籤新增至邊緣函數。如需 CloudFront 標記功能的詳細資訊，請參閱 [標記分佈](tagging.md)。

# 對 CloudFront Functions 的限制
<a name="cloudfront-function-restrictions"></a>

下列限制僅適用於 CloudFront Functions。

**Contents**
+ [日誌](#cloudfront-function-restrictions-logs)
+ [請求內文](#cloudfront-function-restrictions-request-body)
+ [搭配 CloudFront KeyValueStore API 使用臨時憑證](#regional-endpoint-for-key-value-store)
+ [執行時期](#cloudfront-function-runtime-restrictions)
+ [運算利用率](#cloudfront-function-restrictions-compute-utilization)

如需有關配額 (先前稱為限制) 的詳細資訊，請參閱 [CloudFront Functions 上的配額](cloudfront-limits.md#limits-functions)。

## 日誌
<a name="cloudfront-function-restrictions-logs"></a>

CloudFront Functions 中的函數日誌在 10 KB 處被截斷。

## 請求內文
<a name="cloudfront-function-restrictions-request-body"></a>

CloudFront Functions 無法存取 HTTP 請求的本文。

## 搭配 CloudFront KeyValueStore API 使用臨時憑證
<a name="regional-endpoint-for-key-value-store"></a>

您可以使用 AWS Security Token Service (AWS STS) 來產生臨時安全登入資料 （也稱為*工作階段字符*)。工作階段字符可讓您暫時擔任 AWS Identity and Access Management (IAM) 角色，以便存取 AWS 服務。

若要呼叫 [CloudFront KeyValueStore API](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_Operations_Amazon_CloudFront_KeyValueStore.html)，請在 中使用*區域*端點 AWS STS 來傳回第 *2 版*工作階段字符。如果您使用 (`sts.amazonaws.com`) 的 AWS STS *全域*端點， AWS STS 會產生*第 1* 版工作階段字符，SigV4A 版 (SigV4A) 不支援此字符。 SigV4A 因此，您會收到身分驗證錯誤。

若要呼叫 CloudFront KeyValueStore API，您可以使用下列選項：

**AWS CLI 和 AWS SDKs**  
您可以設定 AWS CLI 或 AWS 開發套件來使用區域 AWS STS 端點。如需詳細資訊，請參閱《AWS SDK 與工具參考指南》**中的 [AWS STS 區域和端點](https://docs.aws.amazon.com/sdkref/latest/guide/feature-sts-regionalized-endpoints.html)。  
如需可用 AWS STS 端點的詳細資訊，請參閱《*IAM 使用者指南*》中的[區域和端點](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html#id_credentials_region-endpoints)。

**SAML**  
您可以設定 SAML 以使用區域 AWS STS 端點。如需詳細資訊，請參閱[如何使用區域 SAML 端點進行容錯移轉](https://aws.amazon.com/blogs/security/how-to-use-regional-saml-endpoints-for-failover/)部落格文章。

**`SetSecurityTokenServicePreferences` API**  
您可以設定 的全域端點 AWS STS 以傳回第 2 版工作階段字符，而不是使用區域 AWS STS 端點。若要這樣做，請使用 [SetSecurityTokenServicePreferences](https://docs.aws.amazon.com/IAM/latest/APIReference/API_SetSecurityTokenServicePreferences.html) API 操作來設定您的 AWS 帳戶。  

**Example 範例：IAM CLI 命令**  

```
aws iam set-security-token-service-preferences --global-endpoint-token-version v2Token
```
我們建議您使用 AWS STS 區域端點，而不是此選項。區域端點提供更高的可用性和容錯移轉案例。

**自訂身分提供者**  
如果您使用的是自訂身分提供者，且由它負責進行聯合並代為承擔該角色，請使用前述選項之一，以作為負責產生工作階段字符的上層身分提供者。

## 執行時期
<a name="cloudfront-function-runtime-restrictions"></a>

CloudFront Functions 執行時期環境不支援動態程式碼評估，而且會限制對網路、檔案系統、環境變數和計時器的存取。如需詳細資訊，請參閱[限制功能](functions-javascript-runtime-10.md#writing-functions-javascript-features-restricted-features)。

**注意**  
若要使用 CloudFront KeyValueStore，您的 CloudFront 函數必須使用 [JavaScript 執行時期 2.0](functions-javascript-runtime-20.md)。

## 運算利用率
<a name="cloudfront-function-restrictions-compute-utilization"></a>

CloudFront Functions 對其可以執行所需的時間有限制，測量為*運算利用率*。運算利用率是介於 0 到 100 之間的數字，表示函數執行所花費的時間，以所允許時間上限的百分比表示。例如，35 的運算利用率表示函數以所允許時間上限的 35% 完成。

當您[測試函數](test-function.md)時，您可以在測試事件的輸出中看到運算利用率值。對於生產函數，您可以在 [CloudFront 主控台中的 Monitoring (監控) 頁面](https://console.aws.amazon.com/cloudfront/v4/home?#/monitoring) 上，或在 CloudWatch 中檢視[運算利用率指標](viewing-cloudfront-metrics.md#monitoring-console.cloudfront-functions)。

# 對 Lambda@Edge 的限制
<a name="lambda-at-edge-function-restrictions"></a>

下列限制僅適用於 Lambda@Edge。

**Contents**
+ [DNS 解析](#lambda-at-edge-restrictions-dns)
+ [HTTP 狀態碼](#lambda-at-edge-restrictions-status-codes)
+ [Lambda 函數版本](#lambda-at-edge-restrictions-version)
+ [Lambda 區域](#lambda-at-edge-restrictions-region)
+ [Lambda 角色許可](#lambda-at-edge-restrictions-role-permissions)
+ [Lambda 功能](#lambda-at-edge-restrictions-features)
+ [支援的執行時期](#lambda-at-edge-restrictions-runtime)
+ [CloudFront 標頭](#lambda-at-edge-restrictions-cloudfront-headers)
+ [使用包含內文選項時的要求內文限制](#lambda-at-edge-restrictions-request-body)
+ [回應逾時和保持連線逾時 (僅限自訂原始伺服器)](#timeout-for-lambda-edge-functions)

如需配額的詳細資訊，請參閱 [Lambda@Edge 的配額](cloudfront-limits.md#limits-lambda-at-edge)。

## DNS 解析
<a name="lambda-at-edge-restrictions-dns"></a>

CloudFront 在執行原始伺服器請求 Lambda@Edge 函數*之前*，會對原始伺服器網域名稱執行 DNS 解析。如果您網域的 DNS 服務遇到問題，且 CloudFront 無法解析網域名稱以取得 IP 位址，您的 Lambda@Edge 函數將不會調用。CloudFront 將傳回 [HTTP 502 狀態碼 (無效的閘道)](http-502-bad-gateway.md) 給用戶端。如需詳細資訊，請參閱[DNS 錯誤 (`NonS3OriginDnsError`)](http-502-bad-gateway.md#http-502-dns-error)。

如果您的函數邏輯修改原始網域名稱，CloudFront 將在函數完成執行後對更新的網域名稱執行另一個 DNS 解析。

如需有關管理 DNS 備援的詳細資訊，請參閱《Amazon Route 53 開發人員指南》**中的[設定 DNS 備援](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/dns-failover-configuring.html)。

## HTTP 狀態碼
<a name="lambda-at-edge-restrictions-status-codes"></a>

無論回應來自原始伺服器或 CloudFront 快取，檢視器回應事件的 Lambda@Edge 函數無法修改回應的 HTTP 狀態碼。

## Lambda 函數版本
<a name="lambda-at-edge-restrictions-version"></a>

您必須使用 Lambda 函數的已編號版本，而不是 `$LATEST` 或別名。

## Lambda 區域
<a name="lambda-at-edge-restrictions-region"></a>

Lambda 函數必須位於美國東部 (維吉尼亞北部)區域。

## Lambda 角色許可
<a name="lambda-at-edge-restrictions-role-permissions"></a>

與 Lambda 函數關聯的 IAM 執行角色必須可由服務主體 `lambda.amazonaws.com` 和 `edgelambda.amazonaws.com` 擔任。如需詳細資訊，請參閱[設定 Lambda@Edge 的 IAM 許可權限和角色](lambda-edge-permissions.md)。

## Lambda 功能
<a name="lambda-at-edge-restrictions-features"></a>

Lambda@Edge 不支援下列 Lambda 函數：
+ **自動** (預設值) 以外的 [Lambda 執行時期管理組態](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-update.html#runtime-management-controls)。
+ 用於存取您 VPC 中的資源的 Lambda 函數組態
+ [Lambda 函數無效字母佇列](https://docs.aws.amazon.com/lambda/latest/dg/invocation-async.html#dlq)
+ [Lambda 環境變數](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html) (自動支援預留環境變數除外)
+ [透過圖層管理 AWS Lambda 相依項](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html)的 Lambda 函數
+ [使用 AWS X-Ray](https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html)
+ Lambda 佈建並行
**注意**  
Lambda@Edge 函數與所有 Lambda 函數共用相同的[區域並行](https://docs.aws.amazon.com/lambda/latest/dg/configuration-concurrency.html)功能。如需詳細資訊，請參閱[Lambda@Edge 的配額](cloudfront-limits.md#limits-lambda-at-edge)。
+ [使用容器映像建立 Lambda 函數](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html)
+ [使用 arm64 架構的 Lambda 函數](https://docs.aws.amazon.com/lambda/latest/dg/foundation-arch.html)
+ 具有超過 512 MB 暫時性儲存的 Lambda 函數
+ 使用[客戶自管金鑰以加密您的 .zip 部署套件](https://docs.aws.amazon.com/lambda/latest/dg/encrypt-zip-package.html)

## 支援的執行時期
<a name="lambda-at-edge-restrictions-runtime"></a>

Lambda@Edge 支援最新版本的 Node.js 和 Python 執行時期。如需支援版本及其未來棄用日期的清單，請參閱《AWS Lambda 開發人員指南》**中的[支援的執行時期](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html#runtimes-supported)。

**提示**  
依照最佳實務，請使用所提供的最新版本執行時期，以獲得效能提升與新功能。
您無法使用棄用的 Node.js 版本來建立或更新函數。您只能將現有函數與這些版本與 CloudFront 分佈建立關聯。使用此版本且與分佈相關聯的函數會繼續執行。不過，建議您將函數移至更新版本的 Node.js。如需詳細資訊，請參閱《AWS Lambda 開發人員指南》**中的[執行時期支援政策](https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html)和 GitHub 上的 [Node.js 發行排程](https://github.com/nodejs/Release#release-schedule)。

## CloudFront 標頭
<a name="lambda-at-edge-restrictions-cloudfront-headers"></a>

Lambda@Edge 函數可以讀取、編輯、移除或新增任何列於 [新增 CloudFront 請求標頭](adding-cloudfront-headers.md) 的 CloudFront 標頭。

**備註**  
如果您希望 CloudFront 新增這些標頭，則必須將 CloudFront 設定為使用[快取政策](controlling-the-cache-key.md)或[原始伺服器請求政策](controlling-origin-requests.md)來新增標頭。
CloudFront 會在檢視器請求事件*之後*新增標頭，這表示檢視器請求中的 Lambda@Edge 函數無法使用這些標頭。標頭僅適用於原始伺服器請求和原始伺服器回應中的 Lambda@Edge 函數。
如果檢視器請求包含具有這些名稱的標頭，且您將 CloudFront 設定為使用[快取政策](controlling-the-cache-key.md)或[伺服器請求政策](controlling-origin-requests.md)來新增標頭，則 CloudFront 會覆寫檢視器請求中的標頭值。檢視器對應的函數會從檢視器請求查看標頭值，而原始伺服器對應的函數則會查看 CloudFront 新增的標頭值。
若檢視器請求函數新增 `CloudFront-Viewer-Country` 標頭，它將無法通過驗證，且 CloudFront 會向檢視器傳回 HTTP 狀態碼 502 (無效的閘道)。

## 使用包含內文選項時的要求內文限制
<a name="lambda-at-edge-restrictions-request-body"></a>

如果您選擇**包含本文**選項以向您的 Lambda@Edge 函數公開請求本文，則請您注意，公開或替換的本文部分須遵循下列的資訊和大小配額。
+ CloudFront 一律會在將要求內文公開給 Lambda@Edge 之前對其進行 base64 編碼。
+ 如果要求內文過大，CloudFront 會在公開給 Lambda@Edge 之前將內文截斷，如下所示：
  + 針對檢視器請求事件，會在 40 KB 處截斷內文。
  + 針對原始伺服器請求事件，會在 1 MB 處截斷內文。
+ 如果以唯讀方式存取請求內文，CloudFront 會將完整的原始伺服器請求內文傳送給原始伺服器。
+ 如果 Lambda@Edge 函數替換了請求本文，則下列大小配額適用於函數所傳回的本文：
  + 如果 Lambda@Edge 函數以純文字格式傳回內文：
    + 針對檢視器請求事件，本文限制為 40 KB。
    + 針對原始伺服器請求事件，本文限制為 1 MB。
  + 如果 Lambda@Edge 函數以 base64 編碼的文字傳回內文：
    + 針對檢視器請求事件，本文限制為 53.2 KB。
    + 針對原始伺服器請求事件，本文限制為 1.33 MB。

**注意**  
如果您的 Lambda@Edge 函數傳回超過這些限制的本文，您的請求將使用 HTTP 502 狀態碼 ([Lambda 驗證錯誤](http-502-bad-gateway.md#http-502-lambda-validation-error)) 失敗。我們建議您更新 Lambda@Edge 函數，讓本文不超過這些限制。

## 回應逾時和保持連線逾時 (僅限自訂原始伺服器)
<a name="timeout-for-lambda-edge-functions"></a>

如果您使用 Lambda@Edge 函數來設定分佈原始伺服器的回應逾時或持續作用逾時，請確認您正在指定原始伺服器可支援的值。如需詳細資訊，請參閱[回應和保持連線逾時配額](DownloadDistValuesOrigin.md#response-keep-alive-timeout-quota)。