LESSON-26 · 零基礎學生教材
建立 GAS 商品 GET、訂單 POST 與聯絡 POST 契約
完成三條 GAS API 契約、前後端欄位驗證與一致 {ok,data,message,error} envelope;測試環境能讀商品並接收訂單/聯絡資料,不含正式 credential。
先理解:這堂課解決什麼問題?
離線流程已能操作,現在用一個 Apps Script Web App 依 action 分流:doGet 讀商品,doPost 接訂單或聯絡;前端與 GAS 共用固定欄位、JSON 回應與測試 endpoint,且不把 Spreadsheet ID 或秘密放前端。
本堂要完成:完成三條 GAS API 契約、前後端欄位驗證與一致 {ok,data,message,error} envelope;測試環境能讀商品並接收訂單/聯絡資料,不含正式 credential。
學習邊界:先完成「建立 GAS 商品 GET、訂單 POST 與聯絡 POST 契約」,不提前引入尚未教過的框架或建置工具。
先備知識與操作環境
延續上一章的鐵刻運動電商成果。本章只增加 建立 GAS 商品 GET、訂單 POST 與聯絡 POST 契約 的責任;先開啟 examples/minimal.html,再依 Starter → Checkpoint → Solution 留下檔案、操作、預期與實際證據。
- 檔案位置:先找到
project/e-commerce/js/config.js、js/api.js、js/checkout.js、js/contact.js、apps-script/Code.gs、apps-script/SETUP.md,再從本頁的最小實驗開始。 - 瀏覽器:開啟最小實驗與階段頁;每次修改後儲存並重新整理。
- DevTools:依本堂內容使用 Elements、Styles、Computed、Console 或 Network,記下第一個可觀察證據。
- 操作習慣:每次只修改一個檔案或一個責任,先預測、再操作、最後驗收。
先做最小實驗:先看見結果
這不是期末網站,而是一個可以直接看到本堂核心結果的最小實驗。先完成一次操作,再回到檔案和名詞卡對照。
- 入口:開啟
examples/minimal.html。 - 操作:開啟本章最小實驗,按下執行按鈕,再比對畫面、Elements、Console 與 390px;不得把 local mock 當成真實 Sheet 成功。
- 預期:只呈現 L26 在鐵刻運動連續專案新增的責任,且能指出上一章輸入與下一章交付。
- 重設:重新整理
examples/minimal.html;本章最小實驗不送正式資料、不部署。
學生練習流程
依序完成每一張卡;目前只聚焦一個步驟,先操作,再用可觀察證據確認結果。
-
1
最小實驗
先閱讀本頁的問題、檔案地圖、名詞卡與最小例子。 -
2
Step 2
自己回答互動問答 4 題,再展開解析。 -
3
最小實驗 2
開啟最小實驗,實際操作一次並記下證據。 -
4
Starter
把 Starter 複製到自己的工作資料夾,照 3 張微步驟卡逐一完成;每一步都要看到結果再繼續。 -
5
Checkpoint
前往 Checkpoint,只處理 1 個本堂核心缺口;先預測,再用指定證據驗收。 -
6
Solution
前往 Solution,先看逐檔差異與修改原因,再決定要不要把完整內容帶回自己的檔案。 -
7
變體驗收
完成情境變體與獨立挑戰,留下檔案位置、操作、結果與驗收證據,並閱讀下一堂銜接:Sheet 與權威計價:下一堂固定三張工作表 schema、去重與可恢復的完整狀態。
檔案地圖:每個位置負責什麼?
先不要急著背檔名;請把檔案位置和責任連起來。你在階段頁會看到同一份檔案的可執行版本。
js/config.js原生 JavaScript:負責商品、購物車、表單、local mock 或 GAS 測試契約。js/api.js原生 JavaScript:負責商品、購物車、表單、local mock 或 GAS 測試契約。js/checkout.js原生 JavaScript:負責商品、購物車、表單、local mock 或 GAS 測試契約。js/contact.js原生 JavaScript:負責商品、購物車、表單、local mock 或 GAS 測試契約。apps-script/Code.gsGAS 測試後端:展示商品 GET、訂單/聯絡 POST、Sheet schema、權威計價與去重。
先學會這些詞
先從名詞、英文與白話意思開始;每次只展開一張名詞卡,再對照正式定義、範例與驗收證據。專業術語、語法、檔案路徑會使用不同樣式。第一次遇到術語時,先回到這裡,不要靠猜。
商品 GET
4 個詞doGet(e)doGet(e) 白話意思:用來理解「doGet(e)」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 doGet(e) 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 doGet(e) 或排查相關問題時。
- 最小範例
- doGet(e)
- 可觀察證據
- 能在專章範例指出 doGet(e) 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
action=listProductsaction=listProducts 白話意思:用來理解「action=listProducts」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 action=listProducts 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 action=listProducts 或排查相關問題時。
- 最小範例
- action=listProducts
- 可觀察證據
- 能在專章範例指出 action=listProducts 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
active productsactive products 白話意思:用來理解「active products」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 active products 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 active products 或排查相關問題時。
- 最小範例
- active products
- 可觀察證據
- 能在專章範例指出 active products 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
ContentService JSONContentService JSON 白話意思:用來理解「ContentService JSON」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 ContentService JSON 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 ContentService JSON 或排查相關問題時。
- 最小範例
- ContentService JSON
- 可觀察證據
- 能在專章範例指出 ContentService JSON 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
訂單與聯絡 POST
4 個詞
application/x-www-form-urlencodedapplication/x-www-form-urlencoded
白話意思:用來理解「application/x-www-form-urlencoded」的實務概念。
展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將
application/x-www-form-urlencoded定義為可在檔案、畫面或 DevTools 中驗證的技術責任。 - 什麼時候用
- 實作
application/x-www-form-urlencoded或排查相關問題時。 - 最小範例
- application/x-www-form-urlencoded
- 可觀察證據
- 能在專章範例指出
application/x-www-form-urlencoded的操作結果。 - 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
e.parameter.action/e.parameter.payloade.parameter.action/e.parameter.payload
白話意思:用來理解「e.parameter.action/e.parameter.payload」的實務概念。
展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將
e.parameter.action/e.parameter.payload 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。 - 什麼時候用
- 實作
e.parameter.action/e.parameter.payload 或排查相關問題時。 - 最小範例
- e.parameter.action/e.parameter.payload
- 可觀察證據
- 能在專章範例指出
e.parameter.action/e.parameter.payload 的操作結果。 - 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
JSON.parse(e.parameter.payload)JSON.parse(e.parameter.payload)
白話意思:用來理解「JSON.parse(e.parameter.payload)」的實務概念。
展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 JSON.parse(
e.parameter.payload) 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。 - 什麼時候用
- 實作 JSON.parse(
e.parameter.payload) 或排查相關問題時。 - 最小範例
- JSON.parse(e.parameter.payload)
- 可觀察證據
- 能在專章範例指出 JSON.parse(
e.parameter.payload) 的操作結果。 - 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
createOrder/createContactcreateOrder/createContact 白話意思:用來理解「createOrder/createContact」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 createOrder/createContact 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 createOrder/createContact 或排查相關問題時。
- 最小範例
- createOrder/createContact
- 可觀察證據
- 能在專章範例指出 createOrder/createContact 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
API 邊界與回應
4 個詞ok/data/message/error envelopeok/data/message/error envelope 白話意思:用來理解「ok/data/message/error envelope」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 ok/data/message/error envelope 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 ok/data/message/error envelope 或排查相關問題時。
- 最小範例
- ok/data/message/error envelope
- 可觀察證據
- 能在專章範例指出 ok/data/message/error envelope 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
測試 /exec測試 /exec 白話意思:用來理解「測試 /exec」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 測試 /exec 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 測試 /exec 或排查相關問題時。
- 最小範例
- 測試 /exec
- 可觀察證據
- 能在專章範例指出 測試 /exec 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
ContentService redirectContentService redirect 白話意思:用來理解「ContentService redirect」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 ContentService redirect 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 ContentService redirect 或排查相關問題時。
- 最小範例
- ContentService redirect
- 可觀察證據
- 能在專章範例指出 ContentService redirect 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
前端無 secret前端無 secret 白話意思:用來理解「前端無 secret」的實務概念。 展開查看正式定義、範例與驗收證據
- 正式定義
- 本章將 前端無 secret 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
- 什麼時候用
- 實作 前端無 secret 或排查相關問題時。
- 最小範例
- 前端無 secret
- 可觀察證據
- 能在專章範例指出 前端無 secret 的操作結果。
- 容易混淆
- 只記名稱卻沒有實際操作或驗收證據。
最小例子:先看懂,再複製
這段只保留本堂第一個微成果。先預測結果,再逐行讀;完整檔案請開啟下方最小實驗。
function doGet(e) { if (e.parameter.action === 'listProducts') return jsonResponse_({ ok:true, data:{ products:listActiveProducts_() }, message:'商品載入成功' }); return jsonResponse_({ ok:false, message:'不支援的讀取動作', error:'UNKNOWN_ACTION' }); }Live Lab:逐段讀碼與驗證
每一步只回答五件事:為什麼做、改哪裡、會看到什麼、用什麼證據確認、卡住先查什麼。
-
Step 1
以 doGet(e) 處理 action=listProducts,並由
config.js/api.js呼叫後回傳 active 商品 JSON envelope- 目的
- 讓商品讀取有獨立、可觀察且不洩漏 Sheet 設定的 GET 契約。
- 修改位置
apps-script/Code.gs、js/config.js、js/api.js
以 doGet(e) 處理 action=listProducts,並由config.js/api.js呼叫後回傳 active 商品 JSON envelope- 預期結果
- GET 成功回應包含 ok=true、data.products、message;每筆公開
productId/name/image/price/salePrice,Products.active 只用於過濾。 - 驗收證據
- Network
URL/query/response、GAS execution log 與欄位對照。 - 卡住先查
- 先在 Network 確認 action=listProducts,再檢查回應的
ok/data/message/error與第一筆五個公開 Product key。
-
Step 2
以 form-urlencoded 的 action=createOrder 與 payload JSON 字串送出,GAS 由
e.parameter.action 分流並 JSON.parse(e.parameter.payload)- 目的
- 訂單 transport 必須保留識別、購買人資料與品項,金額留給 L27 由後端計算。
- 修改位置
apps-script/Code.gs、js/checkout.js
以 form-urlencoded 的 action=createOrder 與 payload JSON 字串送出,GAS 由e.parameter.action 分流並 JSON.parse(e.parameter.payload)- 預期結果
- 有效 payload 回傳 ok=true、
data.orderNo/status與 message;缺欄或空 items 回傳 ok=false、message 與 error。 - 驗收證據
- Network Form Data 的
action/payload、GAS log 與 {ok,data,message,error} 對照。 - 卡住先查
- 先看 Form Data 是否只有 action 與 payload,再解析 payload 核對 submissionId、buyer、gender、phone、address、email、items 及每筆
productId/qty。
-
Step 3
以 form-urlencoded 的 action=createContact 與 payload JSON 字串送出,加入六個 payload 欄位驗證與共用 {ok,data,message,error} 回應
- 目的
- 聯絡與訂單共用 transport 但不可混用欄位或回應格式。
- 修改位置
apps-script/Code.gs、js/contact.js
以 form-urlencoded 的 action=createContact 與 payload JSON 字串送出,加入六個 payload 欄位驗證與共用 {ok,data,message,error} 回應- 預期結果
- submissionId、姓名、性別、電話、信箱、內容皆送達;成功與錯誤都能由同一 envelope 判斷。
- 驗收證據
- Network Form Data、GAS log、ContentService MIME type、
data/message與 error。 - 卡住先查
- 先確認
e.parameter.action/payload 與 JSON.parse,再逐欄核對submissionId/name/gender/phone/email/message。
-
Step 4
在訂單 form-urlencoded POST 補上 action=createOrder
- 目的
- Checkpoint 只修一個 routing key,讓 GAS 能進入正確 handler。
- 修改位置
js/checkout.js
在訂單 form-urlencoded POST 補上 action=createOrder- 預期結果
- 同一份訂單 payload 從 UNKNOWN_ACTION 改為 ok=true,且回應可讀
data/message/error。 - 驗收證據
- Network Form Data 的 action、payload 與 response 前後差異。
- 卡住先查
- 只查看 form-urlencoded body 最外層是否同時有 action 與 payload。
-
Step 5
專章驗收:商品 GET Query
- 目的
- 將概念轉成可重現的實作證據。
- 修改位置
examples/minimal.html與對應 stage files
操作 商品 GET Query 並記錄結果。- 預期結果
- 能展示 商品 GET Query 的畫面、DOM、Console 或 Network 證據。
- 驗收證據
- 檔案位置、操作、預期、實際與重新驗證。
- 卡住先查
- 只重做一個最小操作並查看第一個錯誤。
-
Step 6
專章驗收:商品 GET 回應欄位
- 目的
- 將概念轉成可重現的實作證據。
- 修改位置
examples/minimal.html與對應 stage files
操作 商品 GET 回應欄位 並記錄結果。- 預期結果
- 能展示 商品 GET 回應欄位 的畫面、DOM、Console 或 Network 證據。
- 驗收證據
- 檔案位置、操作、預期、實際與重新驗證。
- 卡住先查
- 只重做一個最小操作並查看第一個錯誤。
讀碼順序:先找入口,再找被引用的檔案,最後用畫面、DOM、Console、Network 或資料結果驗證。
互動問答
先回答目前這一題,再展開解析;完成後按下一題,讓每一次回答都回到檔案或瀏覽器完成驗證。
Q1概念理解 「doGet(e)」在本堂要解決什麼問題? 本題線索:doGet(e);回到名詞卡與 能在專章範例指出 doGet(e) 的操作結果。 展開解析收合解析
白話解析:
用來理解「doGet(e)」的實務概念。 正式來說,本章將 doGet(e) 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
對照位置:
回到名詞卡「doGet(e)」,再看 能在專章範例指出 doGet(e) 的操作結果。
預期觀察:
你應該能用自己的話說出:實作 doGet(e) 或排查相關問題時。
常見錯誤:
不要只回答「它是doGet(e)」;那是重複名詞,不是說明責任。
立即驗證:
開啟 examples/minimal.html,依序做「開啟本章最小實驗,按下執行按鈕,再比對畫面、Elements、Console 與 390px;不得把 local mock 當成真實 Sheet 成功。」並記下畫面或工具證據。
Q2程式碼閱讀
修改指定規則後,你預測畫面會看到什麼?
本題線索:apps-script/Code.gs、js/config.js、js/api.js;要修改:以 doGet(e) 處理 action=listProducts,並由 config.js/api.js 呼叫後回傳 active 商品 JSON envelope;觀察:Network URL/query/response、GAS execution log 與欄位對照。
展開解析收合解析
白話解析:
先看檔案責任,再預測結果。這一步的目的:讓商品讀取有獨立、可觀察且不洩漏 Sheet 設定的 GET 契約。
對照位置:
對照 Live Lab Step 1,位置是 apps-script/Code.gs、js/config.js、js/api.js;範例內容:function doGet(e) { if (e.parameter.action === 'listProducts') return jsonResponse_({ ok:true, data:{ products:listActiveProducts_() }, message:'商品載入成功' }); return jsonResponse_({ ok:false, message:'不支援的讀取動作', error:'UNKNOWN_ACTION' }); }
預期觀察:
GET 成功回應包含 ok=true、data.products、message;每筆公開 productId/name/image/price/salePrice,Products.active 只用於過濾。
常見錯誤:
如果只改了檔案但沒有結果,先不要重寫全部;先在 Network 確認 action=listProducts,再檢查回應的 ok/data/message/error 與第一筆五個公開 Product key。
立即驗證:
實際操作後檢查 Network URL/query/response、GAS execution log 與欄位對照。。
Q3概念比較
「doGet(e)」和「action=listProducts」在本堂的責任有什麼不同?
本題線索:比較 doGet(e) 與 action=listProducts;操作位置:apps-script/Code.gs、js/checkout.js
展開解析收合解析
白話解析:
doGet(e):用來理解「doGet(e)」的實務概念。;action=listProducts:用來理解「action=listProducts」的實務概念。
對照位置:
對照兩張名詞卡的正式定義:本章將 doGet(e) 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。/本章將 action=listProducts 定義為可在檔案、畫面或 DevTools 中驗證的技術責任。
預期觀察:
你應該能指出兩者分別出現在哪個檔案或工具,以及哪一個結果會改變。
常見錯誤:
不要把「改變畫面」當成所有技術的責任;先說清楚誰負責結構、呈現、行為或驗收。
立即驗證:
在 apps-script/Code.gs、js/checkout.js 做「以 form-urlencoded 的 action=createOrder 與 payload JSON 字串送出,GAS 由 e.parameter.action 分流並 JSON.parse(e.parameter.payload)」,再用 Network Form Data 的 action/payload、GAS log 與 {ok,data,message,error} 對照。 比較前後差異。
Q4除錯驗收
如果完成操作後結果不對,你會先從哪一個證據開始查?
本題線索:examples/minimal.html 與對應 stage files;第一個檢查位置:檔案位置、操作、預期、實際與重新驗證。
展開解析收合解析
白話解析:
先描述症狀,再提出一個最小假設。本堂常見錯誤是:只口頭說明完成。
對照位置:
對照最後一段 Live Lab:examples/minimal.html 與對應 stage files;檢查方式:只重做一個最小操作並查看第一個錯誤。
預期觀察:
你要能指出一個具體位置,而不是一次修改很多檔案。
常見錯誤:
不要先清快取、重裝工具或複製 Solution;先查看第一個可觀察錯誤。
立即驗證:
重新操作並留下「症狀、證據、假設、單一修正、結果」五項紀錄。
常見誤解
- 看到畫面沒有變化,不代表程式沒執行;先確認檔案、路徑、元素與狀態證據。
- 能複製 Solution 不代表理解;請先說出修改哪個檔案、預期哪個結果、用什麼證據確認。
- 本堂只使用既定的 HTML、CSS、JavaScript、Vue CDN、Apps Script mock 與 Cloudflare Pages 驗收範圍。
本堂任務與驗收
完成三條 GAS API 契約、前後端欄位驗證與一致 {ok,data,message,error} envelope;測試環境能讀商品並接收訂單/聯絡資料,不含正式 credential。
- 能用 listProducts GET 留下可重現證據。
- 能用 form-urlencoded
order/contactPOST 留下可重現證據。 - 能用 action+payload 留下可重現證據。
- 能用 後端驗證 留下可重現證據。
- 能用 {ok,data,message,error} envelope 留下可重現證據。
- 能用 測試 endpoint 留下可重現證據。
- 能用 前端無 secret 留下可重現證據。
- Starter、Checkpoint、Solution 的入口都能開啟並知道三者差異。
- 遇到問題時能先寫出症狀,再檢查一個最小假設。
官方延伸閱讀
正式延伸|自主學習
把「GAS 商品 GET、訂單 POST 與聯絡 POST 契約」拆成 4 個可獨立完成、可重設、可驗證的自主學習 module。
開啟自主學習入口|4 個獨立 topic
- 先選一個 topic,讀懂成果、檔案與定位。
- 在自己的 Starter 檔案修改一小段。
- 重新載入,用畫面、Elements、Computed、Console 或文件留下證據。
建議第一個 topic:商品 GET Query。
Learning map
- 商品 GET Query|code-edit|files/starter/apps-script/Code.gs|search 含 action=listProducts、paramCount=1
- 商品 GET 回應欄位|code-edit|files/starter/apps-script/Code.gs|count=5、uniqueCount=5、valid=true
- 訂單 POST 契約|code-edit|files/starter/apps-script/Code.gs|count=8、uniqueCount=8、valid=true
- 聯絡 POST 與 JSON Envelope|code-edit|files/starter/apps-script/Code.gs|count=11、uniqueCount=11、valid=true