使用 Amplify Hosting 部署规范配置构建输出 - AWS Amplify 托管

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

使用 Amplify Hosting 部署规范配置构建输出

Amplify Hosting 部署规范是基于文件系统的规范,它定义了便于部署到 Amplify Hosting 的目录结构。框架可以生成这种预期的目录结构作为其构建命令的输出,使框架能够利用 Amplify Hosting 的服务基元。Amplify Hosting 了解部署包的结构并相应地对其进行部署。

有关解释如何使用部署规范的视频演示,请参阅如何在 Amazon Web Services YouTube 频道 AWS Amplify上使用托管任何网站

以下是 Amplify 期望部署包采用的文件夹结构示例。简而言之,它有一个名为 static 的文件夹、一个名为 compute 的文件夹和一个名为 deploy-manifest.json 的部署清单文件。

.amplify-hosting/ ├── compute/ │ └── default/ │ ├── chunks/ │ │ └── app/ │ │ ├── _nuxt/ │ │ │ ├── index-xxx.mjs │ │ │ └── index-styles.xxx.js │ │ └── server.mjs │ ├── node_modules/ │ └── server.js ├── static/ │ ├── css/ │ │ └── nuxt-google-fonts.css │ ├── fonts/ │ │ ├── font.woff2 │ ├── _nuxt/ │ │ ├── builds/ │ │ │ └── latest.json │ │ └── entry.xxx.js │ ├── favicon.ico │ └── robots.txt └── deploy-manifest.json

Amplify 基元支持 SSR

Amplify Hosting 部署规范定义了与以下基元紧密映射的合约。

静态资产

为框架提供托管静态文件的功能。

计算

为框架提供在端口 3000 上运行 Node.js HTTP 服务器的能力。

图像优化

为框架提供在运行时优化图像的服务。

路由规则

为框架提供一种将传入请求路径映射到特定目标的机制。

这些区域有:.amplify-hosting/static directory

必须将本应从应用程序提供的所有可公开访问的静态文件放在.amplify-hosting/static目录URL中。此目录中的文件通过静态资产基元提供。

静态文件可以在应用程序的根目录 (/) 中访问,URL而无需对其内容、文件名或扩展名进行任何更改。此外,子目录保留在URL结构中,并出现在文件名之前。例如,.amplify-hosting/static/favicon.ico 将从 https://myAppId.amplify-hostingapp.com/favicon.ico 中提供,.amplify-hosting/static/_nuxt/main.js 将从 https://myAppId.amplify-hostingapp.com/_nuxt/main.js 中提供

如果框架支持修改应用程序基本路径的功能,则它必须在 .amplify-hosting/static 目录内的静态资产前面加上基本路径。例如,如果基本路径是 /folder1/folder2,则名为 main.css 的静态资源的构建输出将是 .amplify-hosting/static/folder1/folder2/main.css

这些区域有:.amplify-hosting/compute directory

单个计算资源由 default 目录中包含的名为 .amplify-hosting/compute 的单个子目录表示。路径是 .amplify-hosting/compute/default。此计算资源映射到 Amplify Hosting 的计算基元。

子目录 default 的内容必须符合以下规则。

  • 文件必须存在于子目录 default 的根目录中,才能作为计算资源的入口点。

  • 入口点文件必须是 Node.js 模块,并且它必须启动在端口 3000 上侦听的HTTP服务器。

  • 您可以将其他文件放在 default 子目录中,并从入口点文件中的代码中引用它们。

  • 子目录的内容必须是自包含的。入口点模块中的代码不能引用子目录之外的任何模块。请注意,框架可以随心所欲地捆绑HTTP服务器。如果可以在子目录中使用 node server.js 命令(其中 server.js is 是入口文件的名称)启动计算过程,则 Amplify 认为目录结构符合部署规范。

Amplify Hosting 将 default 子目录中的所有文件捆绑并部署到预置的计算资源中。每个计算资源被分配 512 MB 的临时存储。此存储不在执行实例之间共享,而是在同一执行实例中的后续调用之间共享。执行实例的最大执行时间限制为 15 分钟,并且执行实例中唯一可写的路径是 /tmp 目录。每个计算资源包的压缩大小不能超过 220 MB。例如,压缩后的 .amplify/compute/default 子目录不能超过 220 MB。

这些区域有:.amplify-hosting/deploy-manifest.json file

使用 deploy-manifest.json 文件存储部署的配置详细信息和元数据。deploy-manifest.json 文件必须至少包含一个 version 属性、指定了捕获所有路由的 routes 属性以及以及指定了框架元数据的 framework 属性。

以下对象定义演示了部署清单的配置。

type DeployManifest = { version: 1; routes: Route[]; computeResources?: ComputeResource[]; imageSettings?: ImageSettings; framework: FrameworkMetadata; };

以下主题描述了部署清单中每个属性的详细信息和用法。

使用版本属性

version 属性定义了您正在实施的部署规范的版本。目前,Amplify Hosting 部署规范的唯一版本是版本 1。以下JSON示例演示了该version属性的用法。

"version": 1

使用路由属性

routes 属性使框架能够利用 Amplify Hosting 路由规则基元。路由规则提供了一种机制,用于将传入的请求路径路由到部署包中的特定目标。路由规则仅规定传入请求的目的地,并在通过重写和重定向规则转换请求后应用。有关 Amplify Hosting 如何处理重写和重定向的更多信息,请参阅为 Amplify 应用程序设置重定向和重写

路由规则不会重写或转换请求。如果传入的请求与路由的路径模式匹配,则该请求将按原样路由到路径的目标。

routes 数组中指定的路由规则必须符合以下规则。

  • 必须指定捕获所有路由。捕获所有路由具有匹配所有传入请求的 /* 模式。

  • routes 数组最多可以包含 25 个项目。

  • 您必须指定 Static 路由或 Compute 路由。

  • 如果指定 Static 路由,则 .amplify-hosting/static 目录必须存在。

  • 如果指定 Compute 路由,则 .amplify-hosting/compute 目录必须存在。

  • 如果指定 ImageOptimization 路由,则还必须指定 Compute 路由。这是必要操作,因为纯静态应用程序尚不支持图像优化。

以下对象定义演示了 Route 对象的配置。

type Route = { path: string; target: Target; fallback?: Target; }

以下列表描述了 Route 对象的属性。

类型 必需 描述

path

String

定义与传入请求路径(不包括查询字符串)相匹配的模式。

路径最大长度为 255 个字符。

路径必须以正斜杠 / 开头。

路径可以包含以下任何字符:[A-Z]、[a-z]、[0-9]、[ _-.*$/~"'@:+]。

对于模式匹配,仅支持以下通配符:

  • *(匹配 0 或多个字符)

  • /* 模式被称为捕获所有模式,它将匹配所有传入的请求。

target

目标

一个对象,用于定义将匹配的请求路由到的目标。

如果指定了 Compute 路由,则必须存在相应的 ComputeResource

如果指定了 ImageOptimization,则还必须指定 imageSettings

回退

目标

一个对象,用于定义原始目标返回 404 错误时要回退到的目标。

指定路径的 fallback 种类和 target 种类不能相同。例如,不允许从 Static 回退到 Static。只有没有正文的GET请求才支持回退。如果请求中存在正文,则将在回退期间将其丢弃。

以下对象定义演示了 Target 对象的配置。

type Target = { kind: TargetKind; src?: string; cacheControl?: string; }

以下列表描述了 Target 对象的属性。

类型 必需 描述

类型

Targetkind

定义目标类型的 enum。有效值包括 StaticComputeImageOptimization

src

String

对于 Compute,为是

对于其他基元,为否

一个字符串,用于指定包含基元的可执行代码的部署包中的子目录的名称。仅对计算基元有效,且为必填项。

该值必须指向部署包中存在的计算资源之一。目前,此字段唯一支持的值为 default

cacheControl

String

一个字符串,它指定要应用于响应的 Cache-Control 标头的值。仅对静态和 ImageOptimization基元有效。

指定的值会被自定义标头覆盖。有关 Amplify Hosting 客户标头的更多信息,请参阅为 Amplify 应用程序设置自定义 HTTP 标头

注意

此 Cache-Control 标头仅适用于状态码设置为 200(OK)的成功响应。

以下对象定义演示了 TargetKind 枚举的用法。

enum TargetKind { Static = "Static", Compute = "Compute", ImageOptimization = "ImageOptimization" }

以下列表指定了 TargetKind 枚举的有效值。

静态

将请求路由到静态资产基元。

计算

将请求路由到计算基元。

ImageOptimization

将请求路由到图像优化基元。

以下JSON示例演示了指定多个路由规则的routes属性的用法。

"routes": [ { "path": "/_nuxt/image", "target": { "kind": "ImageOptimization", "cacheControl": "public, max-age=3600, immutable" } }, { "path": "/_nuxt/builds/meta/*", "target": { "cacheControl": "public, max-age=31536000, immutable", "kind": "Static" } }, { "path": "/_nuxt/builds/*", "target": { "cacheControl": "public, max-age=1, immutable", "kind": "Static" } }, { "path": "/_nuxt/*", "target": { "cacheControl": "public, max-age=31536000, immutable", "kind": "Static" } }, { "path": "/*.*", "target": { "kind": "Static" }, "fallback": { "kind": "Compute", "src": "default" } }, { "path": "/*", "target": { "kind": "Compute", "src": "default" } } ]

有关在部署清单中指定路由规则的详细信息,请参阅配置路由规则的最佳实践

使用该computeResources属性

computeResources 属性使框架能够提供有关预置计算资源的元数据。每个计算资源都必须有与之关联的相应路由。

以下对象定义演示了 ComputeResource 对象的用法。

type ComputeResource = { name: string; runtime: ComputeRuntime; entrypoint: string; }; type ComputeRuntime = 'nodejs16.x' | 'nodejs18.x' | 'nodejs20.x';

以下列表描述了 ComputeResource 对象的属性。

类型 必需 描述

name

String

指定计算资源的名称。名称必须与 .amplify-hosting/compute directory 内的子目录名称匹配。

对于部署规范的版本 1,唯一的有效值为 default

runtime

ComputeRuntime

定义预置计算资源的运行时。

有效值包括 nodejs16.xnodejs18.xnodejs20.x

entrypoint

String

为指定的计算资源指定代码将从中运行的启动文件的名称。该文件必须存在于代表计算资源的子目录中。

您的目录结构必须与以下结构类似。

.amplify-hosting |---compute | |---default | |---index.js

该JSONcomputeResource属性的将如下所示。

"computeResources": [ { "name": "default", "runtime": "nodejs16.x", "entrypoint": "index.js", } ]

使用该 imageSettings 属性

imageSettings 属性使框架能够自定义图像优化基元的行为,该基元在运行时提供图像的按需优化。

以下对象定义演示了 ImageSettings 对象的用法。

type ImageSettings = { sizes: number[]; domains: string[]; remotePatterns: RemotePattern[]; formats: ImageFormat[]; minumumCacheTTL: number; dangerouslyAllowSVG: boolean; }; type ImageFormat = 'image/avif' | 'image/webp' | 'image/png' | 'image/jpeg';

以下列表描述了 ImageSettings 对象的属性。

类型 必需 描述

尺寸

Number[]

支持的图像宽度数组。

String[]

允许使用图像优化的外部域的数组。将数组留空,仅允许部署域使用图像优化。

remotePatterns

RemotePattern[]

允许使用图像优化的外部模式的数组。与域类似,但通过正则表达式(regex)提供了更多控制。

格式

ImageFormat[]

允许的输出图像格式的数组。

minimumCacheTTL

数字

优化图像的缓存时长(以秒为单位)。

dangerouslyAllowSVG

布尔值

允许SVG输入图像URLs。默认情况下,出于安全考虑,此功能处于禁用状态。

以下对象定义演示了 RemotePattern 对象的用法。

type RemotePattern = { protocol?: 'http' | 'https'; hostname: string; port?: string; pathname?: string; }

以下列表描述了 RemotePattern 对象的属性。

类型 必需 描述

protocol

String

允许的远程模式的协议。

有效值为 httphttps

hostname

String

允许的远程模式的主机名。

您可以指定文本或通配符。单个 `*` 匹配单个子域。双 `**` 匹配任意数量的子域。在仅指定 `**` 的情况下,Amplify 不允许使用笼统通配符。

port

String

允许的远程模式的端口。

pathname

String

允许的远程模式的路径名称。

以下示例说明了 imageSettings 属性。

"imageSettings": { "sizes": [ 100, 200 ], "domains": [ "example.com" ], "remotePatterns": [ { "protocol": "https", "hostname": "example.com", "port": "", "pathname": "/**", } ], "formats": [ "image/webp" ], "minumumCacheTTL": 60, "dangerouslyAllowSVG": false }

使用框架属性

使用 framework 属性来指定框架元数据。

以下对象定义演示了 FrameworkMetadata 对象的配置。

type FrameworkMetadata = { name: string; version: string; }

以下列表描述了 FrameworkMetadata 对象的属性。

类型 必需 描述

name

String

框架的名称。

版本

String

框架的版本。

它必须是有效的语义版本控制(semver)字符串。

配置路由规则的最佳实践

路由规则提供了一种机制,用于将传入的请求路径路由到部署包中的特定目标。在部署捆绑包中,框架作者可以将部署到以下任一目标的文件发送到构建输出:

  • 静态资源基元 – 文件包含在 .amplify-hosting/static 目录中。

  • 计算基元 – 文件包含在 .amplify-hosting/compute/default 目录中。

框架作者还在部署清单文件中提供了一系列路由规则。数组中的每条规则都按顺序与传入的请求进行匹配,直到完成匹配为止。当存在匹配规则时,请求会被路由到匹配规则中指定的目标。或者,可以为每条规则指定一个回退目标。如果原始目标返回 404 错误,则会将请求路由到回退目标。

部署规范要求遍历顺序中的最后一条规则是捕获所有规则。使用 /* 路径指定了捕获所有规则。如果传入的请求与路由规则数组中先前的任何路由都不匹配,则该请求将被路由到捕获所有规则目标。

对于像这样的SSR框架 Nuxt.js,包罗万象的规则目标必须是计算原语。这是因为SSR应用程序的服务器端渲染页面的路由在构建时是不可预测的。例如,如果 Nuxt.js 应用程序有一个页面,/blog/[slug]其中[slug]有一个动态路由参数。捕获所有规则目标是将请求路由到这些页面的唯一方法。

相比之下,可以使用特定的路径模式来定位构建时已知的路由。例如,Nuxt.js 从/_nuxt路径中提供静态资产。这意味着 /_nuxt/* 路径可以由特定的路由规则来定向,该规则将请求路由到静态资产基元。

公共文件夹路由

大多数SSR框架都提供了从public文件夹中提供可变静态资产的功能。favicon.ico和之类的文件robots.txt通常保存在public文件夹中,并从应用程序的根目录提供URL。例如,favicon.ico 文件是从 https://example.com/favicon.ico 提供的。请注意,这些文件没有可预测的路径模式。它们几乎完全由文件名决定。在 public 文件夹中定位文件的唯一方法是使用捕获所有路由。但是,捕获所有路由目标必须是计算基元。

我们建议使用以下方法之一来管理 public 文件夹。

  1. 使用路径模式来定位包含文件扩展名的请求路径。例如,您可以使用 /*.* 定位所有包含文件扩展名的请求路径。

    请注意,这种方法可能不可靠。例如,如果 public 文件夹内有没有文件扩展名的文件,则此规则不针对这些文件。使用这种方法需要注意的另一个问题是,应用程序的页面名称中可能有句点。例如,/blog/2021/01/01/hello.world 处的页面将被 /*.* 规则将定位。这并不理想,因为该页面不是静态资产。但是,您可以在此规则中添加回退目标,以确保当静态基元出现 404 错误时,请求会回退到计算基元。

    { "path": "/*.*", "target": { "kind": "Static" }, "fallback": { "kind": "Compute", "src": "default" } }
  2. 在构建时识别 public 文件夹中的文件,并为每个文件发出路由规则。这种方法不可扩展,因为部署规范规定了 25 条规则的限制。

    { "path": "/favicon.ico", "target": { "kind": "Static" } }, { "path": "/robots.txt", "target": { "kind": "Static" } }
  3. 建议您的框架用户将所有可变的静态资源存储在 public 文件夹内的子文件夹中。

    在以下示例中,用户可以将所有可变的静态资产存储在 public/assets 文件夹中。然后,可以使用带有路径模式 /assets/* 的路由规则来定位 public/assets 文件夹内所有可变的静态资产。

    { "path": "/assets/*", "target": { "kind": "Static" } }
  4. 为捕获所有路由指定一个静态回退。这种方法的缺点将在下一节“捕获所有回退路由”中详细介绍。

捕获所有回退路由

对于SSR诸如 Nuxt.js,其中为计算原始目标指定了包罗万象的路由,框架作者可以考虑为包罗万象的路由指定静态后备以解决文件夹路由问题。public但是,这种类型的路由规则会破坏服务器端渲染的 404 页面。例如,如果最终用户访问了一个不存在的页面,则应用程序会呈现一个状态码为 404 的 404 页面。但是,如果捕获所有路由具有静态回退,则不会呈现 404 页面。取而代之的是,请求会回退到静态基元中,但最终仍会显示 404 状态码,但是 404 页面无法渲染。

{ "path": "/*", "target": { "kind": "Compute", "src": "default" }, "fallback": { "kind": "Static" } }

基本路径路由

提供修改应用程序基本路径功能的框架应预先设置 .amplify-hosting/static 目录内静态资产的基本路径。例如,如果基本路径是 /folder1/folder2,则名为 main.css 的静态资源的构建输出将是 .amplify-hosting/static/folder1/folder2/main.css

这意味着还需要更新路由规则以反映基本路径。例如,如果基本路径是 /folder1/folder2,则 public 文件夹中静态资产的路由规则将如下所示。

{ "path": "/folder1/folder2/*.*", "target": { "kind": "Static" } }

同样,服务器端路由也需要在它们前面加上基本路径。例如,如果基本路径是 /folder1/folder2,则 /api 路由的路由规则将如下所示。

{ "path": "/folder1/folder2/api/*", "target": { "kind": "Compute", "src": "default" } }

但是,不应将基本路径置于捕获所有路由之前。例如,如果基本路径是 /folder1/folder2,则捕获所有路由将保持如下所示。

{ "path": "/*", "target": { "kind": "Compute", "src": "default" } }

Nuxt.js 路由示例

以下是 Nuxt 应用程序的示例 deploy-manifest.json 文件,演示了如何指定路由规则。

{ "version": 1, "routes": [ { "path": "/_nuxt/image", "target": { "kind": "ImageOptimization", "cacheControl": "public, max-age=3600, immutable" } }, { "path": "/_nuxt/builds/meta/*", "target": { "cacheControl": "public, max-age=31536000, immutable", "kind": "Static" } }, { "path": "/_nuxt/builds/*", "target": { "cacheControl": "public, max-age=1, immutable", "kind": "Static" } }, { "path": "/_nuxt/*", "target": { "cacheControl": "public, max-age=31536000, immutable", "kind": "Static" } }, { "path": "/*.*", "target": { "kind": "Static" }, "fallback": { "kind": "Compute", "src": "default" } }, { "path": "/*", "target": { "kind": "Compute", "src": "default" } } ], "computeResources": [ { "name": "default", "entrypoint": "server.js", "runtime": "nodejs18.x" } ], "framework": { "name": "nuxt", "version": "3.8.1" } }

以下是 Nuxt 的示例 deploy-manifest.json 文件,演示了如何指定包括基本路径在内的路由规则。

{ "version": 1, "routes": [ { "path": "/base-path/_nuxt/image", "target": { "kind": "ImageOptimization", "cacheControl": "public, max-age=3600, immutable" } }, { "path": "/base-path/_nuxt/builds/meta/*", "target": { "cacheControl": "public, max-age=31536000, immutable", "kind": "Static" } }, { "path": "/base-path/_nuxt/builds/*", "target": { "cacheControl": "public, max-age=1, immutable", "kind": "Static" } }, { "path": "/base-path/_nuxt/*", "target": { "cacheControl": "public, max-age=31536000, immutable", "kind": "Static" } }, { "path": "/base-path/*.*", "target": { "kind": "Static" }, "fallback": { "kind": "Compute", "src": "default" } }, { "path": "/*", "target": { "kind": "Compute", "src": "default" } } ], "computeResources": [ { "name": "default", "entrypoint": "server.js", "runtime": "nodejs18.x" } ], "framework": { "name": "nuxt", "version": "3.8.1" } }

有关使用 routes 属性的更多信息,请参阅使用路由属性