CloudFront Functions 事件结构 - Amazon CloudFront

CloudFront Functions 事件结构

CloudFront Functions 在运行函数时将 event 对象作为输入传递给函数代码。当您测试函数时,可以创建 event 对象并将其传递至您的函数。创建用于测试函数的 event 对象时,您可以省略 context 对象中的 distributionDomainNamedistributionIdrequestId 字段。请确保标头的名称为小写字母,在生产环境中 CloudFront Functions 传递给您的函数的 event 对象中情况总是如此。

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

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

有关更多信息,请参阅以下主题:

版本字段

version 字段包含一个字符串,用于指定 CloudFront Functions 事件对象的版本。当前版本为 1.0

Context 对象

context 对象包含有关事件的上下文信息。其中包括以下字段:

distributionDomainName

与事件关联的分配的 CloudFront 域名(例如 d111111abcdef8.cloudfront.net)。

distributionId

与事件关联的分配的 ID(例如 EDFDVBD6EXAMPLE)。

eventType

事件类型,viewer-requestviewer-response

requestId

唯一标识 CloudFront 请求(及其关联响应)的字符串。

查看器对象

viewer 对象包含一个 ip 字段,其值为发送请求的查看器(客户端)的 IP 地址。如果查看器请求来自 HTTP 代理或负载均衡器,则值为该代理或负载均衡器的 IP 地址。

请求对象

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 对象为请求中的每个标头包含一个字段。标头名称在事件对象中转换为小写,当您的函数代码添加它们时,标头名称必须为小写。当 CloudFront 函数将事件对象转换回 HTTP 请求时,标头名称中每个单词的第一个字母都会大写。单词由连字符 (-) 分隔。例如,如果您的函数代码添加了名为 example-header-name 的标头,CloudFront 会将其转换为 HTTP 请求中的 Example-Header-Name

cookies

表示请求中的 Cookies 的对象(Cookie 标头)。

cookies 对象为请求中的每个 Cookie 包含一个字段。

有关查询字符串、标头和 Cookies 结构的更多信息,请参阅 查询字符串、标头或 Cookie 的结构

有关示例 event 对象,请参阅 示例事件对象

响应对象

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>") 中指定正文内容。执行此操作时,忽略 dataencoding 字段。在这种情况下,CloudFront 将正文视为纯文本。

encoding

body 内容(data 字段)的编码。有效编码仅为 textbase64

如果将 encoding 指定为 base64,但正文不是有效的 base64,CloudFront 将返回错误。

data

body 内容。

有关修改后的状态代码和正文内容的更多信息,请参阅 状态代码和正文

有关标头和 Cookies 结构的更多信息,请参阅 查询字符串、标头或 Cookie 的结构

有关示例 response 对象,请参阅 示例响应对象

状态代码和正文

使用 CloudFront Functions,您可以更新查看器响应状态代码,将整个响应正文替换为新响应正文,或删除响应正文。在评估来自 CloudFront 缓存或源的响应的各个方面后更新查看器响应的一些常见场景包括:

  • 更改状态以设置 HTTP 200 状态代码并创建要返回查看器的静态正文内容。

  • 更改状态以设置将用户重定向到其他网站的 HTTP 301 或 302 状态代码。

  • 决定是提供还是丢弃查看器响应的正文。

注意

如果源返回 400 及以上的 HTTP 错误,则 CloudFront Functions 将无法运行。有关更多信息,请参阅 所有边缘函数的限制

当您在处理 HTTP 响应时,CloudFront Functions 无权访问响应正文。您可以通过将正文内容设置为所需值来替换正文内容,或者通过将值设置为空来删除正文。如果您不更新函数中的正文字段,CloudFront 缓存或源返回的源正文将返回到查看器。

提示

使用 CloudFront Functions 替换正文时,请确保将相应的标头(例如 content-encodingcontent-typecontent-length)与新的正文内容对齐。

例如,如果 CloudFront 源或缓存返回 content-encoding: gzip,但查看器响应函数设置的正文为纯文本,则该函数还需要相应地更改 content-encodingcontent-type 标头。

如果您的 CloudFront 函数配置为返回 400 或以上的 HTTP 错误,则您的查看器将看不到您为相同状态代码指定的自定义错误页面

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

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

查询字符串值或查询字符串对象

除了查询字符串对象之外,函数还可以返回查询字符串值。查询字符串值可用于按任何自定义顺序排列查询字符串参数。

例 示例

要修改函数代码中的查询字符串,请使用如下代码。

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

标头的特殊注意事项

仅对于标头,标头名称在事件对象中转换为小写,当您的函数代码添加它们时,标头名称必须为小写。当 CloudFront 函数将事件对象转换回 HTTP 请求或响应时,标头名称中每个单词的第一个字母都会大写。单词由连字符 (-) 分隔。例如,如果您的函数代码添加了名为 example-header-name 的标头,CloudFront 会将其转换为 HTTP 请求或响应中的 Example-Header-Name

例 示例

请考虑 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 数组)

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

例 示例

请考虑具有以下 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)在 valuemultiValue 属性中重复。这样一来,您可以通过循环 multiValue 数组来访问全部值。

如果函数代码修改具有 multiValue 数组的查询字符串、标头或 Cookie,CloudFront Functions 将使用以下规则来应用更改:

  1. 如果 multiValue 数组存在并进行了任何修改,则应用该修改。value 属性中的第一个元素将被忽略。

  2. 否则,将应用对 value 属性的任何修改,后续值(如果存在)保持不变。

仅当 HTTP 请求或响应中包含重复的查询字符串、标头或具有相同名称的 Cookie 时,才使用 multiValue 属性,如前面的示例所示。但是,如果单个查询字符串、标头或 Cookie 中有多个值,则不使用 multiValue 属性。

例 示例

请考虑带有一个 Accept 标头的请求,该表头中包含三个值。

Accept: application/json, application/xml, text/html

此标头在 request 对象中表示如下。

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

在 HTTP 响应的 Set-Cookie 标头中,标头包含 Cookie 的名称-值对以及可选的一组用分号分隔的属性。

例 示例
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" }

示例响应对象

以下示例显示了一个 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>" } } }

示例事件对象

以下示例显示了一个完整的 event 对象。

注意

event 对象是函数的输入。您的函数只返回 requestresponse 对象,而不返回完整的 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" } ] } } } }