

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

# 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"
                    }
                ]
            }
        }
    }
}
```