

# 使用 CloudFront Functions 在边缘进行自定义
<a name="cloudfront-functions"></a>

借助 CloudFront Functions，您可以在 JavaScript 中编写轻量级函数，以实现大规模、延迟敏感的 CDN 自定义。您的函数可以操作通过 CloudFront 的请求和响应、执行基本身份验证和授权、在边缘生成 HTTP 响应等。CloudFront Functions 运行时环境提供亚毫秒的启动时间，可立即扩展，从而每秒处理数百万个请求，并且非常安全。CloudFront Functions 是 CloudFront 的原生功能，这意味着您可以完全在 CloudFront 中构建、测试和部署代码。

在将 CloudFront 函数与 CloudFront 分配相关联时，CloudFront 在 CloudFront 边缘站点中截获请求和响应并将它们传递到您的函数。当发生以下事件时，您可以调用 CloudFront Functions：
+ 在 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. 选择**添加关联**。

1. 在**关联**对话框中，选择分配和/或缓存行为。对于**事件类型**，请保留默认值。

1. 选择**添加关联**。

   **关联的分配**表显示了关联的分配。

1. 等待几分钟，以便相关的分配完成部署。要检查分配的状态，请在**关联的分配**表中选择分配，然后选择**查看分配**。

   当分配的状态为 **Deployed**（已部署）时，您即可验证函数是否正常工作。

## 验证函数
<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. 选择函数名称，在 **Associated 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-functions-origin-modification.md)
+ [

# CloudFront SaaS Manager 属性的辅助方法
](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 响应发送到查看器（客户端）之前修改 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 Functions，用于提供自定义验证和身份验证逻辑。连接函数目前可用于双向 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` 对象时，您可以省略 `context` 对象中的 `distributionDomainName`、`distributionId` 和 `requestId` 字段。请确保标头的名称为小写字母，在生产环境中 CloudFront Functions 传递给您的函数的 `event` 对象中情况总是如此。

下面显示了此事件对象的结构概述。

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

有关更多信息，请参阅以下主题：

**Topics**
+ [

## 版本字段
](#functions-event-structure-version)
+ [

## Context 对象
](#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`。

## Context 对象
<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`**  
表示请求中的 Cookies 的对象（`Cookie` 标头）。  
`cookies` 对象为请求中的每个 Cookie 包含一个字段。

有关查询字符串、标头和 Cookies 结构的更多信息，请参阅 [查询字符串、标头或 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 函数将事件对象转换回 HTTP 响应时，标头名称中每个单词的第一个字母都会大写。单词由连字符 (`-`) 分隔。例如，如果您的函数代码添加了名为 `example-header-name` 的标头，CloudFront 会将其转换为 HTTP 响应中的 `Example-Header-Name`。

**`cookies`**  
表示响应中的 Cookies 的对象（`Set-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)。

有关标头和 Cookies 结构的更多信息，请参阅 [查询字符串、标头或 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 Functions 将无法运行。有关更多信息，请参阅 [所有边缘函数的限制](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 函数配置为返回 400 或以上的 HTTP 错误，则您的查看器将看不到您为相同状态代码指定的[自定义错误页面](creating-custom-error-pages.md)。

## 查询字符串、标头或 Cookie 的结构
<a name="functions-event-structure-query-header-cookie"></a>

查询字符串、标头和 Cookie 共享同一个结构。查询字符串可以出现在请求中。标头出现在请求和响应中。Cookie 出现在请求和响应中。

每个查询字符串、标头或 Cookie 都是父项 `querystring`、`headers` 或 `cookies` 对象内的唯一字段。字段名称是查询字符串、标头或 Cookie 的名称。每个字段都包含一个 `value` 属性，并带有查询字符串、标头或 Cookie 的值。

**Contents**
+ [

### 查询字符串值或查询字符串对象
](#functions-event-structure-query)
+ [

### 标头的特殊注意事项
](#functions-event-structure-headers)
+ [

### 重复的查询字符串、标头和 Cookies（`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 函数将事件对象转换回 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'};
```

### 重复的查询字符串、标头和 Cookies（`multiValue` 数组）
<a name="functions-event-structure-multivalue"></a>

HTTP 请求或响应可以包含多个具有相同名称的查询字符串、标头或 Cookie。在这种情况下，重复的查询字符串、标头或 Cookie 会折叠为 `request` 或 `response` 对象中的一个字段，但此字段包含名为 `multiValue` 的额外属性。`multiValue` 属性包含一个数组，其中包含每个重复查询字符串、标头或 Cookies 的值。

**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 至 9 的某些特征。

为了获得最新的功能，建议您使用 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 rest 参数语法。

**Unicode**  
源文本和字符串文本可以包含 Unicode 编码的字符。还支持由六个字符组成的 Unicode 代码点转义序列（例如，`\uXXXX`）。

**严格模式**  
默认情况下，函数在严格模式下运行，因此您无需在函数代码中添加 `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`。

**数组**  
支持对数组使用以下 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**  
支持对 `ArrayBuffer` 使用以下方法：  
+ `prototype.isView`
+ `prototype.slice`

**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` 模块可以进行文件系统访问。

**处理访问权限**  
没有处理访问权限。例如，没有像 Node.js 中那样用于处理信息访问权限的 `process` 全局对象。

**环境变量**  
无法访问环境变量。  
相反，可以使用 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 至 9 的某些特征。它还提供了一些不属于 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`
JavaScript 运行时 2.0 支持 `async`、`await`、`const` 和 `let`。  
`await` 只能在 `async` 函数内部使用。不支持 `async` 参数和闭包。

**文本**  
支持 ES 6 模板文本：多行字符串、表达式插值和嵌套模板。

**函数**  
支持所有的 ES 5.1 函数功能。  
支持 ES 6 箭头函数，并支持 ES 6 rest 参数语法。

**Unicode**  
源文本和字符串文本可以包含 Unicode 编码的字符。还支持由六个字符组成的 Unicode 代码点转义序列（例如，`\uXXXX`）。

**严格模式**  
默认情况下，函数在严格模式下运行，因此您无需在函数代码中添加 `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()`

**字符串**  
支持对字符串使用以下 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 中的新功能。

**数字**  
支持所有 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 中的新功能。

**符号**  
支持以下 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()`

## 错误类型
<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**
+ [

### Buffer
](#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)

### Buffer
<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)`

  按 `offset` 偏移量将 `byteLength` 的 `Int8` `value` 写入 `Buffer`。
  + `value`：输入一个整数。
  + `offset`：输入一个整数
  + `byteLength`：输入一个从 `1` 到 `6` 的整数。
+ `Buffer.prototype.writeIntBE(value, offset, byteLength)`

  使用大端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `value`：输入一个整数。
  + `offset`：输入一个整数
  + `byteLength`：输入一个从 `1` 到 `6` 的整数。
+ `Buffer.prototype.writeInt16BE(value, offset, byteLength)`

  使用大端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `value`：输入一个整数。
  + `offset`：输入一个整数
  + `byteLength`：输入一个从 `1` 到 `6` 的整数。
+ `Buffer.prototype.writeInt32BE(value, offset, byteLength)`

  使用大端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `value`：输入一个整数。
  + `offset`：输入一个整数
  + `byteLength`：输入一个从 `1` 到 `6` 的整数。
+ `Buffer.prototype.writeIntLE(offset, byteLength)`

  使用小端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `offset`：输入一个整数。
  + `byteLength`：输入一个从 `1` 到 `6` 的整数。
+ `Buffer.prototype.writeInt16LE(offset, byteLength)`

  使用小端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `offset`：输入一个整数。
  + `byteLength`：输入一个从 `1` 到 `6` 的整数。
+ `Buffer.prototype.writeInt32LE(offset, byteLength)`

  使用小端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `offset`：输入一个整数。
  + `byteLength`：输入一个从 `1` 到 `6` 的整数。
+ `Buffer.prototype.writeUInt8(value, offset, byteLength)`

  按 `offset` 偏移量将 `byteLength` 的 `UInt8` `value` 写入 `Buffer`。
  + `value`：输入一个整数。
  + `offset`：输入一个整数
  + `byteLength`：输入一个从 `1` 到 `6` 的整数。
+ `Buffer.prototype.writeUIntBE(value, offset, byteLength)`

  使用大端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `value`：输入一个整数。
  + `offset`：输入一个整数
  + `byteLength`：输入一个从 `1` 到 `6` 的整数。
+ `Buffer.prototype.writeUInt16BE(value, offset, byteLength)`

  使用大端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `value`：输入一个整数。
  + `offset`：输入一个整数
  + `byteLength`：输入一个从 `1` 到 `6` 的整数。
+ `Buffer.prototype.writeUInt32BE(value, offset, byteLength)`

  使用大端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `value`：输入一个整数。
  + `offset`：输入一个整数
  + `byteLength`：输入一个从 `1` 到 `6` 的整数。
+ `Buffer.prototype.writeUIntLE(value, offset, byteLength)`

  使用小端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `value`：输入一个整数。
  + `offset`：输入一个整数
  + `byteLength`：输入一个从 `1` 到 `6` 的整数。
+ `Buffer.prototype.writeUInt16LE(value, offset, byteLength)`

  使用小端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `value`：输入一个整数。
  + `offset`：输入一个整数
  + `byteLength`：输入一个从 `1` 到 `6` 的整数。
+ `Buffer.prototype.writeUInt32LE(value, offset, byteLength)`

  使用小端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `value`：输入一个整数。
  + `offset`：输入一个整数
  + `byteLength`：输入一个从 `1` 到 `6` 的整数。
+ `Buffer.prototype.writeDoubleBE(value, [offset])`

  使用大端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `value`：输入一个整数。
  + `offset`：可选。输入一个整数。默认值为 0。
+ `Buffer.prototype.writeDoubleLE(value, [offset])`

  使用小端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `value`：输入一个整数。
  + `offset`：可选。输入一个整数。默认值为 0。
+ `Buffer.prototype.writeFloatBE(value, [offset])`

  使用大端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `value`：输入一个整数。
  + `offset`：可选。输入一个整数。默认值为 0。
+ `Buffer.prototype.writeFloatLE(value, [offset])`

  使用小端序按 `offset` 偏移量将 `value` 写入 `Buffer`。
  + `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` 模块可以进行文件系统访问。

**处理访问权限**  
没有处理访问权限。例如，没有像 Node.js 中那样用于处理信息访问权限的 `process` 全局对象。

**环境变量**  
无法访问环境变量。相反，可以使用 CloudFront KeyValueStore 为 CloudFront Functions 创建集中式的键值对数据存储。借助 CloudFront KeyValueStore，无需部署代码更改即对配置数据进行动态更新。有关更多信息，请参阅 [Amazon CloudFront KeyValueStore](kvs-with-functions.md)。

**网络访问**  
不支持网络调用。例如，不支持 XHR、HTTP (S) 和套接字。

# 键值存储的帮助程序方法
<a name="functions-custom-methods"></a>

**注意**  
来自 CloudFront Functions 的键值存储辅助方法调用不会触发 AWS CloudTrail 数据事件。这些事件不会记录在 CloudTrail 事件历史记录中。有关更多信息，请参阅 [使用 AWS CloudTrail 记录 Amazon CloudFront API 调用](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"});
```

**响应**

响应是以使用 `options` 请求的格式解析为值的 `promise`。默认情况下，此值以字符串的形式返回。

### 错误处理
<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}
```

# 源修改的辅助方法
<a name="helper-functions-origin-modification"></a>

如果您在 CloudFront Functions 代码中动态更新或更改请求中使用的源，则本节适用。您只能在*查看器请求* CloudFront Functions 上更新源。CloudFront Functions 有一个模块，模块提供了用于动态更新或更改源的辅助方法。

要使用此模块，请使用 JavaScript 运行时 2.0 创建 CloudFront 函数，并在函数代码的第一行包含以下语句：

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

有关更多信息，请参阅 [适用于 CloudFront Functions 的 JavaScript 运行时系统 2.0 特征](functions-javascript-runtime-20.md)。

**注意**  
Test API 和 Test 控制台页面不测试是否修改了源。但是，测试可确保函数代码的执行没有错误。

## 在 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 更新源，请参阅以下主题中的辅助方法。

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（可选，适用于非 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 有助于减少源上的负载。有关更多信息，请参阅 [使用 Amazon CloudFront Origin Shield](origin-shield.md)。如果未提供此信息，则使用所分配源的源护盾设置。    
**enabled（必需）**  
用于启用或禁用源护盾的布尔表达式。接受的值为 `true` 或 `false`。  
**region（启用时为必需）**  
Origin Shield 的 AWS 区域。指定到源的延迟最低的 AWS 区域。使用区域代码，而不是区域名称。例如，使用 `us-east-2` 指定美国东部（俄亥俄州）区域。  
启用 CloudFront 源护盾时，您必须为其指定 AWS 区域。有关可用 AWS 区域列表以及帮助您为源选择合适区域的信息，请参阅[为 Origin Shield 选择 AWS 区域](origin-shield.md#choose-origin-shield-region)。

**originAccessControlConfig（可选）**  
此源的源访问控制（OAC，Origin Access Control）的唯一标识符。只有当源支持 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`。

**timeouts（可选）**  
超时值，您可以用来指定 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（可选，适用于非 S3 自定义源）**  
服务器名称指示（SNI）是传输层安全性协议（TLS）的扩展，客户端可用它在 TLS 握手过程开始时指示尝试连接到的主机名。此值应与原始服务器上的 TLS 证书中的常用名称匹配。否则，原始服务器可能会引发错误。  
如果未提供此值，则使用 `hostHeader` 参数中的值。如果未提供主机标头，则使用 `domainName` 参数中的值。  
如果既未提供主机标头也未提供域名参数，则使用来自分配的源的域名，或者如果转发到源（FTO）策略包括主机，则使用来自传入请求的主机标头。SNI 不能包含冒号（`:`），也不能为 IP 地址。SNI 最多可以有 253 个字符。

**allowedCertificateNames（可选，适用于非 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 – 更新应用程序负载均衡器请求源**  
以下示例将查看器请求的源更改为应用程序负载均衡器源，并设置自定义标头和超时。  

```
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 – 更新启用了源护盾的源**  
在以下示例中，分配中的源启用了源护盾。函数代码仅更新用于源的域名，忽略了所有其他可选参数。在这种情况下，由于未更新源护盾参数，源护盾仍将与修改后的源域名一起使用。  

```
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 引用，而源 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（可选，适用于非 S3 自定义源）**  
向源发出请求时使用的主机标头。如果未提供此值，则使用 `domainName` 参数中的值。  
如果既未提供主机标头也未提供域名参数，则使用来自分配的源的域名，或者如果转发到源（FTO）策略包括主机，则使用来自传入请求的主机标头。主机标头不能包含冒号（`:`），也不能为 IP 地址。主机标头最多可以有 253 个字符。

**sni（可选，适用于非 S3 自定义源）**  
服务器名称指示（SNI）是传输层安全性协议（TLS）的扩展，客户端可用它在 TLS 握手过程开始时指示尝试连接到的主机名。此值应与原始服务器上的 TLS 证书中的常用名称匹配。否则，原始服务器可能会引发错误。  
如果未提供此值，则使用 `hostHeader` 参数中的值。如果未提供主机标头，则使用 `domainName` 参数中的值。  
如果既未提供主机标头也未提供域名参数，则使用来自分配的源的域名，或者如果转发到源（FTO）策略包括主机，则使用来自传入请求的主机标头。SNI 不能包含冒号（`:`），也不能为 IP 地址。SNI 最多可以有 253 个字符。

**allowedCertificateNames（可选，适用于非 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 – 选择应用程序负载均衡器请求源**  
以下示例从与分配关联的源列表中选择名为 `myALB-prod` 的应用程序负载均衡器源，并将 `myALB-prod` 的配置设置应用于请求。  

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

**Example ：选择应用程序负载均衡器请求源并覆盖主机标头**  
与上一示例类似，以下示例从与分配关联的源列表中选择名为 `myALB-prod` 的应用程序负载均衡器源，并将 `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（可选，适用于非 S3 自定义源）**  
向源发出请求时使用的主机标头。如果未提供此值，则使用 `domainName` 参数中的值。  
如果既未提供主机标头也未提供域名参数，则使用来自分配的源的域名，或者如果转发到源（FTO）策略包括主机，则使用来自传入请求的主机标头。主机标头不能包含冒号（`:`），也不能为 IP 地址。主机标头最多可以有 253 个字符。  
**sni（可选，适用于非 S3 自定义源）**  
服务器名称指示（SNI）是传输层安全性协议（TLS）的扩展，客户端可用它在 TLS 握手过程开始时指示尝试连接到的主机名。此值应与原始服务器上的 TLS 证书中的常用名称匹配，否则原始服务器可能会引发错误。  
如果未提供此值，则使用 `hostHeader` 参数中的值。如果未提供主机标头，则使用 `domainName` 参数中的值。  
如果既未提供主机标头也未提供域名参数，则使用来自分配的源的域名，或者如果转发到源（FTO）策略包括主机，则使用来自传入请求的主机标头。SNI 不能包含冒号（`:`），也不能为 IP 地址。SNI 最多可以有 253 个字符。  
**allowedCertificateNames（可选，适用于非 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 属性的辅助方法
<a name="saas-specific-logic-function-code"></a>

使用以下适用于 CloudFront SaaS Manager 的辅助函数，在您创建的函数中检索多租户分配的值。要使用此页上的示例，您必须先使用 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` 字段仅在针对具有关联连接组的多租户分配调用您的函数时才会出现。有关更多信息，请参阅 [Context 对象](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 提供了 `async` 和 `await` 语法来处理 `Promise` 对象。Promise 表示延迟的结果，可以通过标记为 `async` 的函数中的 `await` 关键字进行访问。各种新的 WebCrypto 函数都使用 Promise。

有关 `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` 链式方法查看 Promise。您可以使用 `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 令牌（CWT）支持功能，可使用该功能在 CloudFront 边缘站点实施基于令牌的安全身份验证和授权。此支持功能以模块形式提供，可通过 CloudFront 函数访问。

要使用此模块，请使用 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 结构，最大令牌大小的限制为 1 KB。

## 令牌结构
<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})
```参数

**token（必需）**  
用于验证的编码令牌。这必须是 JavaScript 缓冲区。

**handlerContext（必需）**  
一个 JavaScript 对象，用于存储 validateToken 调用的上下文。目前，仅支持 key 属性。

**key（必需）**  
用于消息摘要计算的私密密钥。可以字符串或 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)
```参数

**generatorContext（必需）**  
这是一个 JavaScript 对象，用作生成令牌的上下文且包含以下键值对：    
**cwtTag（可选）**  
此值是一个布尔值，如果为 `true`，则指定应添加 `cwtTag`。  
**coseTag（必需）**  
指定 COSE 标签类型。目前仅支持 `MAC0`。  
**key（必需）**  
用于计算消息摘要的私密密钥。此值可以是字符串或 JavaScript `Buffer`。

**payload（必需）**  
用于编码的令牌有效载荷。有效载荷必须采用 `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 区域边缘缓存（REC）。如果预期的 REC 不可用且请求使用备用 REC，则不会更新此值。这不包括正在使用的源护盾位置，除非主 REC 和源护盾的位置相同。

**注意**  
如果 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. 选择**添加关联**。在出现的对话框中，选择分配、事件类型和/或缓存行为。

   对于事件类型，请选择您希望此函数在何时运行：
   + **查看器请求** – 在 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 资源名称 (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>

您可以采用以下任一格式存储键值对中的值：
+ 字符串
+ 字节编码的字符串
+ JSON 

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

CloudFront 函数及其所有键值存储数据均能得到安全处理，如下所示：
+ CloudFront 会在您调用 [CloudFront KeyValueStore](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_Operations_Amazon_CloudFront_KeyValueStore.html) API 操作时，在处于静态或在传输过程中（读取或写入键值存储时），对每个键值存储进行加密。
+ 在函数运行时，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 SDK 导入键值对。但是，您只能在*最初* 创建键值存储时导入键值对的文件。

要创建键值对的文件，请参阅[键值对的文件格式](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` 参数。
   + 一个 `import-source` 参数，允许您从存储在 Amazon S3 存储桶中的文件中导入键值对。只有在您首次创建键值存储时，才能从文件导入。有关文件结构的信息，请参阅[键值对的文件格式](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>

创建键值存储时，数据存储可以具有以下状态值。


****  

| 值 | 说明 | 
| --- | --- | 
|  **预置**  |  键值存储已创建，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 是一项使用签名版本 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
```

**Output**

```
{
    "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);
  }
})();
```