需求分析
使用 API Gateway REST API 可以直接使用 S3 作为后端集成对外提供可以访问的 API. 而当访问的 URL 中存在无效的桶, 或者不存在的对象时, API Gateway 默认回向客户端返回 200 状态码. 而实际上这并不是正确的响应, 本文将介绍如何自定义返回 404 错误页面.
基本功能配置
基本功能的配置过程可以参考文档 https://docs.amazonaws.cn/apigateway/latest/developerguide/integrating-api-with-aws-services-s3.html
整理记录主要步骤:
-
创建一个 REST API, 在 Resources 根路径
/
创建新的资源, 不要开启Proxy resource
选项(后面创建的也都不开), 资源名称定义变量{folder}
-
在
/{folder}
下面继续创建资源{item}
, 完成的结构://{folder}/{item}
-
在
/{folder}
中创建 GET 方法, 完成相应配置. 需要注意Action type
选择Use path override
, 并在 Path override 指定使用新的变量{bucket}
, 这个变量后面会和 URL 中的{folder}
进行映射, 现在创建期间先不做配置. 另外所填入的 Execution role 也需要确认在信任关系中允许 API Gateway, 并且 Policy 中允许对目标 S3 存储桶和对象进行操作.
- 切换到 “Integration request”, 点击右上角的 “Edit” 按钮, 参考下图配置 URL path parameters.
其中 Name=bucket 对应的是我们在上面的 Path override 中定义的变量名称, Mapped from 中的表达式 method.request.path.folder
则表示会从请求信息中解析 Resource 定义 URL 地址中的 folder
变量
-
完成配置后即可切换到 Test 标签页, 在 Path 下方的
folder
中填入 S3 桶名称进行测试, 确认可以在下方的输出面板中看到 XML 格式的返回结果. -
继续在
/{item}
中创建 GET 方法, 配置项和/{folder}
基本一致, 只需要修改 Path override 内容为{bucket}/{object}
. 创建完成后再次编辑 Integration request settings 中的 URL path parameters, 分别添加两个参数, 其中新增的object
参数和前面的bucket
一样, 都将通过method.request.path
进行映射.
Name | Mapped from |
---|---|
bucket | method.request.path.folder |
object | method.request.path.item |
- Deploy 到新创建的 Stage
dev
, 随后从浏览器测试访问 Stage 对应的 Invoke URL 后面带上参数确认工作正常, 注意访问地址中需要包含 Stage 名称dev
, 例如:
https://abc12345.execute-api.cn-northwest-1.amazonaws.com.cn/dev/my-bucket/my-data.json
至此, 和文档一致的配置就完成了. 此时如果我们尝试访问不存在的 S3 对象, 浏览器会直接返回一个 200 状态码的 XML 格式字符串内容, 例如:
下面我们就来配置自定义的 404 页面.
定制 404 页面
首先梳理需求, 对于当前资源的结构来说, 我们希望在访问 API 根路径以及各个子路径的默认 URL 时, 还有对于访问不存在的 S3 对象时均返回自定义的 404 页面. 具体来说:
访问路径 | 期望效果 |
---|---|
/ | 404 页面 |
/有效桶 | 正常对象列表 |
/无效桶 | 404 页面 |
/有效桶/有效对象 | 正常对象 |
/有效桶/无效对象 | 404 页面 |
根路径 /
当前配置如果直接访问 Stage URL 的根路径(包含 Stage 名称), 浏览器会返回 403 错误:
{“message”:“Missing Authentication Token”}
我们在 Resource /
位置直接创建 GET 方法, 类型选择 Mock, 直接完成创建.
Deploy 之后再次访问根路径, 观察浏览器返回了 200 状态的一个空白页面, Response header 中的 Content-Type 显示为 application/json
我们切换到 Integration response 标签页, 先删除默认的 Default - Response, 再切换到 Method responses 删除默认的 Response 200, 随后点击 Create response 创建状态码为 404 的响应, 点击 Add model 设置 Content type 为 text/html
, Model 使用默认的 Empty.
再切回 Integration responses, Create response, 直接创建
完成后的 Integration response 中默认响应变成了 404 状态码
Deploy 后测试访问根路径, 可以看到浏览器显示报错, 观察开发者工具中的 Network 请求记录, 收到的 HTTP 响应状态码为 404.
状态码有了, 接下来就是我们要自定义 404 响应对应的 HTML 页面内容. 再次打开 Resource 根路径的 GET 方法 Integration responses 标签页, 编辑现在默认的 404 Response, 展开 Mapping templates > Add mapping template. 设置 Content type = text/html
, 下方的 Template body 中填入我们要自定义的 HTML 代码.
保存后再次 Deploy, 浏览器 刷新访问 开个新的标签页访问. 实现效果:
/{folder} 路径
需要留意, 因为 S3 API 在访问不存在的桶时后端返回的响应状态码是 301
而不是 404, 所以这里我们需要匹配的目标状态码也得是 301
. 具体步骤:
选中 /{folder}
资源下的 GET 方法, 切到 Method response 标签页, 点击 Create response, 创建状态码为 404 (这是我们要最终返回给浏览器的状态码), Content type = text/html
的响应
再切到 Integration response > Create response, 注意这里在 HTTP status regex 表达式中填入 301, 对应的 Method response status code 默认选择 404, Create.
创建完成后, 在当前页面翻动到底部编辑 404 - Response, Add mapping template, 和前面一样, Content type = text/html, Template body 填入自定义的 HTML 内容.
保存后 Deploy, 在浏览器中再访问一个不存在的桶, 符合预期.
https://abc12345.execute-api.cn-northwest-1.amazonaws.com.cn/dev/invalid-bucket
而继续测试正常存在的桶, 正常访问, 未受影响.
/{folder}/{item} 路径
和上面配置的无效桶的情况不一样的是, 当 S3 对象不存在, S3 后端会返回 404 状态码响应, 所以我们在配置 /{folder}/{item}
路径下 GET 方法时需要注意将 HTTP status regex 表达式中填入 404, 其余步骤均一样:
- Method response 创建响应, 状态码
404
, Response body > Content type = text/html - Integration response 创建响应, HTTP status regex = 404, Method response status code 默认 404
- Integration response 编辑 404 - Response > Add mapping template, Content type = text/html, Template body 粘贴相同 HTML 代码
- Deploy 后测试有效桶+无效对象 URL 访问, 正常得到自定义 404 页面内容.
后记
使用 API Gateway 时, 每次修改配置后务必记得 Deploy 才可生效. 另外, 上面的示例实现的是对 S3 存储桶中直接存放目标文件的访问, 如果桶中还有 “文件夹” (实质上是 Prefix), 需要访问位于文件夹内部的对象, 则需要在前面第一步配置 Resource 时将 {item}
资源的路径定义为 {item+}
, 即贪婪模式, 这样才会将请求访问包含文件夹的目标对象完整信息(例如 folder/object.json
) 传递给后端的 S3. 否则当 URL 参数中包含 /
符号时, 会被判定为要访问对应的 Resource 子路径, 由于实际上并不存在对应的配置, 会导致响应结果与预期不符.