需求分析
使用 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 子路徑, 由于實際上并不存在對應的配置, 會導致響應結果與預期不符.