以下為根據原文內容,萃取並結構化的 18 個問題解決案例。每一案均覆蓋問題、根因、解決方案(含程式碼/流程)、效益與訓練要點,方便用於教學、專案演練與評估。
Case #1: 從 Code-First 轉為 Contract-First 並行開發
Problem Statement(問題陳述)
業務場景:公司即將把前台網站外包,但核心會員系統由內部維護。以往皆採 Code-First,先寫 API 再讓前端跟進,導致規格不穩定、返工頻繁,外包端常在等候後端修改,交付延誤。希望先定義 API 合約,讓外包與內部能並行。 技術挑戰:規格如何在實作前落定?如何快速提供可呼叫的 Mock 來驗證情境?如何同步產生 SDK 以減少前端重複工作? 影響範圍:專案 lead time、協作效率、返工率、整合風險與 QA 進度。 複雜度評級:中
Root Cause Analysis(根因分析)
直接原因:
- 合約晚定:API 端點與參數在實作中頻繁變更,造成前端/外包無所適從。
- 缺乏可執行規格:沒有 Mock 服務,需求驗證只能等 MVP。
- 缺少制式產物:SDK 文件、測試案例僅在後期出現,前期驗證不足。
深層原因:
- 架構層面:未把 API 視為產品,缺乏規格中心的版本化機制。
- 技術層面:未採 OpenAPI/Mock 工具鏈;缺自動生 SDK 流程。
- 流程層面:仍以「先會動」思維啟動,未建立 Contract-First 作業節拍。
Solution Design(解決方案設計)
解決策略:採 Contract-First。先以 OpenAPI 定義實體/行為/授權,再用 Mock(如 Prism)驗證情境,落定合約後前後端/QA/技術寫手同步展開。以 codegen 產生 SDK,降低重工。
實施步驟:
- 定義 API 合約與實體
- 實作細節:以 FSM 推導行為清單;以 JSON Schema 定義 Member 實體;路由命名統一。
- 所需資源:Mermaid、OpenAPI、Swagger Editor/Studio
- 預估時間:1-2 天
- 建立 Mock 服務與情境資料
- 實作細節:用 Prism/SwaggerHub Mock;準備註冊/驗證/登入等範例 payload。
- 所需資源:Prism、Postman/Newman
- 預估時間:0.5-1 天
- 產出 SDK 與文件
- 實作細節:openapi-generator 產生多語 SDK;串 CI 發佈;技術寫手依規格撰寫用例。
- 所需資源:openapi-generator、CI/CD
- 預估時間:1 天
- 並行開發與測試
- 實作細節:後端按合約實作;前端/外包對 Mock/SDK 開發;QA 撰寫契約測試。
- 所需資源:xUnit/Postman/Newman
- 預估時間:持續
關鍵程式碼/設定:
# OpenAPI 片段:/api/members/register
openapi: 3.0.3
paths:
/api/members/register:
post:
summary: Register a new member
operationId: registerMember
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [email]
properties:
email: { type: string, format: email }
responses:
'200':
description: Registered
content:
application/json:
schema:
$ref: '#/components/schemas/Member'
components:
schemas:
Member:
type: object
properties:
_id: { type: string }
email: { type: string, format: email }
state: { type: string, enum: [unverified, verified, restricted, banned] }
實際案例:會員註冊/驗證流程;Mock 回應固定案例。 實作環境:.NET 7/8、OpenAPI 3、Prism、openapi-generator 實測數據:
- 改善前:前端等待後端端點穩定約 2-3 週
- 改善後:合約落定後前後端並行,首週即可串 Mock
- 改善幅度:需求到首次整合時間縮短約 30-40%(內部演練觀察)
Learning Points(學習要點) 核心知識點:
- Contract-First 與 Code-First 差異
- OpenAPI/Mock/SDK 工具鏈
- 規格版本化與並行開發
技能要求:
- 必備技能:OpenAPI 編寫、Postman 測試
- 進階技能:Mock 驅動開發、CI 產 SDK
延伸思考:
- 合約變更如何控管相容性?
- Mock 如何加入錯誤案例/延遲/限流模擬?
- 如何用契約測試守護 API 相容性?
Practice Exercise(練習題)
- 基礎練習:為 /register 與 /verify 製作 OpenAPI 與 Mock(30 分)
- 進階練習:產出多語 SDK 並寫最小用例(2 小時)
- 專案練習:以 Contract-First 完成會員註冊全流程與契約測試(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):是否涵蓋主要端點與實體
- 程式碼品質(30%):OAS 結構、可讀性與範例齊備
- 效能優化(20%):Mock 可壓測/錯誤注入
- 創新性(10%):自動化 SDK/文件生成
Case #2: 用 OOP 對應 REST,統一路由與語意
Problem Statement(問題陳述)
業務場景:多個團隊同時開 API,命名習慣不一,端點路由、動詞使用、Input/Output 不一致,後續維護困難且學習成本高。需建立通用的 OOP→REST 對應規則,降低審查與溝通成本。 技術挑戰:如何把 class/instance method 清楚映射到 REST path、HTTP 動詞、參數與回傳;如何兼容 class method(無 id)與 instance method(有 id)。 影響範圍:API 一致性、文件可讀性、學習曲線、錯誤率。 複雜度評級:低
Root Cause Analysis(根因分析)
直接原因:
- 無統一路由規範:各自發明,導致不可預期。
- 缺 OOP 視角:未把物件動作映射為資源操作。
- 文件不足:開發靠口傳,執行不一。
深層原因:
- 架構層面:缺乏 API 設計原則(Ubiquitous Language)。
- 技術層面:未建立模板或路由規則產生器。
- 流程層面:API Review 未檢查一致性。
Solution Design(解決方案設計)
解決策略:採用可解釋的對應規則:/api/{class}/[{id}]:{method},class method 省略 id;輸入對應 method 參數,輸出對應回傳值;輔以 Attribute Routing 模板與 Lint 規則。
實施步驟:
- 制定命名與路由規範
- 實作細節:定義 class/instance 對應;方法名動詞白名單;HTTP 動詞映射。
- 所需資源:規範文件、Repo 模板
- 預估時間:0.5 天
- 提供範本與 Lint
- 實作細節:建立 ASP.NET Core 路由模板;撰寫 API Lint 規則(如 spectral)。
- 所需資源:ASP.NET Core、Spectral
- 預估時間:1 天
關鍵程式碼/設定:
// C# 類別對應
public class Man {
public static Man Create(...) {...}
public static Man Get(int id) {...}
public decimal Work(TimeSpan duration) {...}
}
// 對應 REST
// POST /api/man:create
// GET /api/man/{id}
// POST /api/man/{id}:work
// ASP.NET Core 路由範本
[ApiController]
[Route("api/[controller]")]
public class ManController : ControllerBase {
[HttpPost("man:create")] public IActionResult Create(...) { ... }
[HttpGet("man/{id}")] public IActionResult Get(int id) { ... }
[HttpPost("man/{id}:work")] public IActionResult Work(int id, [FromBody] WorkDto dto) { ... }
}
實際案例:Member.Register/Verify/… 同樣對應 :register/:verify 實作環境:ASP.NET Core 7/8 實測數據:
- 改善前:API Review 常見命名議題 > 50%
- 改善後:路由命名爭議大幅下降,Review 時間縮短約 30%(團隊回饋)
Learning Points(學習要點) 核心知識點:
- OOP 到 REST 的可逆映射
- Attribute Routing 與一致性
- API Lint 基礎
技能要求:
- 必備技能:ASP.NET Core Routing
- 進階技能:API Lint/規範自動檢查
延伸思考:
- 如何處理批次與查詢型端點命名?
- 方法語意如何避免動詞濫用?
- 多語言 SDK 命名一致性如何維護?
Practice Exercise(練習題)
- 基礎練習:把 3 個類別方法映射成 REST(30 分)
- 進階練習:為規範寫 spectral 3 條 Lint 規則(2 小時)
- 專案練習:用範本完成會員全套端點路由(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):類別方法完整映射
- 程式碼品質(30%):路由清晰、一致
- 效能優化(20%):路由模板可復用
- 創新性(10%):Lint 自動化程度
Case #3: 用 FSM 取代 CRUD,避免非法狀態
Problem Statement(問題陳述)
業務場景:會員註冊/驗證/停權/解禁等流程以 CRUD 寫成,外部僅需 UPDATE 欄位即可直接變更 state,經常引發「未驗證→已驗證」被任意修改、邏輯錯亂與安全漏洞。 技術挑戰:如何讓狀態變更只能透過合法轉移?如何把狀態與行為封裝並公開為 API? 影響範圍:資料一致性、安全性、維護成本。 複雜度評級:中
Root Cause Analysis(根因分析)
直接原因:
- 狀態暴露:state 欄位可被 CRUD 任意修改。
- 缺少狀態機:未定義合法轉移,導致流程不受控。
- 檢查分散:各 API 自行檢查,維護困難。
深層原因:
- 架構層面:未以生命週期設計資源。
- 技術層面:未封裝狀態轉移為方法。
- 流程層面:需求只對準畫面,不對準原則。
Solution Design(解決方案設計)
解決策略:繪製 Member FSM,將變更包成動作(Register/Verify/Restrict/Allow/Ban/Permit/Remove),state 唯讀;API 僅暴露動作端點;方法內強制檢查與轉移。
實施步驟:
- 建立 FSM 與方法清單
- 實作細節:Mermaid 狀態圖;標示轉移動作。
- 所需資源:Mermaid
- 預估時間:0.5 天
- 封裝轉移邏輯
- 實作細節:Enum 狀態+方法檢查;API 僅對應動作端點。
- 所需資源:C#、ASP.NET Core
- 預估時間:1 天
關鍵程式碼/設定:
public enum MemberState { CREATED, UNVERIFIED, VERIFIED, RESTRICTED, BANNED, DELETED }
public class Member {
public int Id { get; private set; }
public MemberState State { get; private set; }
public static Member Register(string email) {
var m = new Member(email);
m.State = MemberState.UNVERIFIED; return m;
}
public bool Verify(string code) {
if (State != MemberState.UNVERIFIED) return false;
// verify code...
State = MemberState.VERIFIED; return true;
}
}
// REST:POST /api/members/{id}:verify
實際案例:註冊→驗證→登入等動作端點;state 僅回傳不可直接更新。 實作環境:ASP.NET Core、C# 實測數據:
- 改善前:非法狀態變更缺陷頻出(測試階段高)
- 改善後:非法變更降至接近 0;測試腳本清晰可覆蓋轉移路徑(團隊觀察)
Learning Points(學習要點) 核心知識點:
- 以 FSM 驅動 API 介面
- 封裝狀態轉移
- 動作端點代替狀態欄位更新
技能要求:
- 必備技能:狀態機建模、C# 封裝
- 進階技能:從 FSM 生成 API 規格
延伸思考:
- 如何對轉移做審計與追蹤?
- 若要新增狀態是否會破壞相容性?
- 轉移與交易的原子性如何保障?
Practice Exercise(練習題)
- 基礎練習:為 Verified→Restricted/Allow 編寫方法(30 分)
- 進階練習:把所有轉移做成端點+整合測試(2 小時)
- 專案練習:全域改造 CRUD→FSM(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):所有轉移可用
- 程式碼品質(30%):封裝與檢查完整
- 效能優化(20%):方法重用設計
- 創新性(10%):從 FSM 自動產合約
Case #4: 狀態轉移原子性與並發控制
Problem Statement(問題陳述)
業務場景:會員狀態變更(例如解禁與停權)偶發同時操作,導致最後狀態不一致或覆寫。需要把每次狀態轉移做成不可分割的原子操作。 技術挑戰:如何避免並發下雙寫、亂序更新?如何簡潔實作悲觀/樂觀鎖? 影響範圍:資料一致性、客服回報量、法規合規。 複雜度評級:高
Root Cause Analysis(根因分析)
直接原因:
- 無鎖:多請求同時修改同筆資料。
- 缺重試:碰撞後直接覆寫或失敗未補償。
- 轉移無交易保護:跨多資料存取未包成交易。
深層原因:
- 架構層面:未規劃狀態操作交易邊界。
- 技術層面:未使用行鎖/版本號;無事件一致性處理。
- 流程層面:壓測與並發測試不足。
Solution Design(解決方案設計)
解決策略:針對關鍵轉移實作樂觀鎖(RowVersion)+重試、必要時悲觀鎖(SELECT … FOR UPDATE);所有轉移包在交易內,失敗回滾;事件發佈使用 outbox。
實施步驟:
- 加入版本欄位與樂觀鎖
- 實作細節:RowVersion + DbUpdateConcurrencyException 重試。
- 所需資源:EF Core
- 預估時間:0.5 天
- 交易與 outbox 事件
- 實作細節:同交易寫入事件 outbox,背景送出。
- 所需資源:EF Core、背景服務
- 預估時間:1 天
關鍵程式碼/設定:
public class MemberEntity {
public int Id { get; set; }
public string State { get; set; }
[Timestamp] public byte[] RowVersion { get; set; }
}
public async Task<bool> VerifyAsync(int id, string code) {
for(int retry=0; retry<3; retry++){
using var tx = await _db.Database.BeginTransactionAsync();
var m = await _db.Members.FindAsync(id);
if (m.State != "unverified") return false;
// check code...
m.State = "verified";
try {
await _db.SaveChangesAsync();
await _outbox.AddAsync(new Event(...));
await tx.CommitAsync();
return true;
} catch (DbUpdateConcurrencyException) {
await tx.RollbackAsync();
// retry
}
}
return false;
}
實際案例:Verify/Allow/Ban/Permit 等轉移保證原子性。 實作環境:EF Core、SQL Server/MySQL/Postgres 實測數據:
- 改善前:並發下狀態混亂偶發
- 改善後:並發轉移穩定,重試成功率高,無混亂案例(壓測報告)
Learning Points(學習要點) 核心知識點:
- 樂觀/悲觀鎖
- 交易與 outbox pattern
- 並發測試與重試策略
技能要求:
- 必備技能:EF Core 交易與鎖
- 進階技能:事件一致性
延伸思考:
- 大流量下鎖競爭如何降低?
- API 是否需要 idempotency key?
- 事件重複投遞的防重設計?
Practice Exercise(練習題)
- 基礎練習:為 Verify 加入 RowVersion 檢查(30 分)
- 進階練習:完成 outbox 發佈與重試(2 小時)
- 專案練習:壓測並發 500 並保證轉移正確(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):轉移具原子性
- 程式碼品質(30%):交易/重試清晰
- 效能優化(20%):鎖競爭控制
- 創新性(10%):idempotency 設計
Case #5: 角色與授權策略(JWT + Policy + Scope)
Problem Statement(問題陳述)
業務場景:外包商、使用者、客服、系統作業等多角色須使用 API。當前用 API Key 粗粒度控管,難以限制操作範圍(如僅查 masked 個資、僅允許本人更新)。 技術挑戰:如何用 scope 與 policy 開細粒度控制,並把角色-行為對應到狀態機? 影響範圍:安全性、錯誤授權、合規。 複雜度評級:中
Root Cause Analysis(根因分析)
直接原因:
- 單一 Key 無法表達權限範圍。
- API Gateway 層面無法判斷「本人」。
- 授權規則散落在各端點,難以維護。
深層原因:
- 架構層面:缺 role-scope-operation 映射。
- 技術層面:未運用 JWT claims + ASP.NET Policy。
- 流程層面:授權需求未納入設計階段。
Solution Design(解決方案設計)
解決策略:定義最小 scope 集(REGISTER/READ/DELETE/BASIC/STAFF/ADMIN/SYSTEM),用 JWT Claims 挾帶角色與 scopes;用 ASP.NET Authorization Policy 實作「本人」檢查。
實施步驟:
- 定義 scope 與對應操作
- 實作細節:從 FSM 動作倒推 scope;整理映射表。
- 所需資源:設計文件
- 預估時間:0.5 天
- 實作 JWT + Policy
- 實作細節:JwtBearer;AddAuthorization 加入多個 Policy;端點套 [Authorize]。
- 所需資源:ASP.NET Core
- 預估時間:1 天
關鍵程式碼/設定:
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options => { options.Authority = "..."; options.Audience = "api"; });
builder.Services.AddAuthorization(opts =>
{
opts.AddPolicy("Scope.READ", p => p.RequireClaim("scope", "READ"));
opts.AddPolicy("Scope.BASIC", p => p.RequireClaim("scope", "BASIC"));
opts.AddPolicy("SelfOnly", p => p.RequireAssertion(ctx => {
var userId = ctx.User.FindFirst("sub")?.Value;
var routeId = ctx.Resource is HttpContext http ? http.Request.RouteValues["id"]?.ToString() : null;
return userId == routeId;
}));
});
// 使用
[Authorize(Policy="Scope.BASIC"), Authorize(Policy="SelfOnly")]
[HttpPost("api/members/{id}:reset-password")]
public IActionResult ResetPassword(string id, ...) { ... }
實際案例:會員本人可重設自己密碼;客服可執行 restrict/permit;外包商僅讀 masked。 實作環境:ASP.NET Core、JWT(如 Auth0/Azure AD B2C/自架) 實測數據:
- 改善前:誤授權風險高,需人工稽核
- 改善後:授權由 policy 集中管理與測試;誤授權事件消失(內部觀察)
Learning Points(學習要點) 核心知識點:
- JWT/Claims/Scopes
- ASP.NET Policy/Role/Claims 授權
- 「本人」判定策略
技能要求:
- 必備技能:JWT 基本配置
- 進階技能:客製化 Policy
延伸思考:
- 多租戶場景如何擴充 claims?
- Scope 與 Role 怎麼協同?
- Gateway 與應用層責任切分?
Practice Exercise(練習題)
- 基礎練習:為 READ/REGISTER 建立 Policy(30 分)
- 進階練習:實作 SelfOnly 與整合測試(2 小時)
- 專案練習:完成全套角色/範圍控管(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):Policy 覆蓋所有操作
- 程式碼品質(30%):授權邏輯集中一致
- 效能優化(20%):授權快取與 Claims 最小化
- 創新性(10%):多層授權協作
Case #6: Scope 設計收斂,避免權限爆炸
Problem Statement(問題陳述)
業務場景:授權需求複雜,若為每個端點開一個 scope,將形成管理與文件爆炸。需在維持表達力下縮減 scope 數量。 技術挑戰:如何用「最小可組合單位」涵蓋所有操作?如何在 OpenAPI 清楚表達? 影響範圍:安全清晰度、管理成本、開發對齊。 複雜度評級:中
Root Cause Analysis(根因分析)
直接原因:
- 以端點為單位設 scope 導致數量失控。
- 缺乏分類:建立/異動/查詢/系統作業混在一起。
- 文檔不一致:前端與後端認知不一。
深層原因:
- 架構層面:未以行為類別抽象 scope。
- 技術層面:OpenAPI 安全定義使用不熟。
- 流程層面:授權與設計未同步。
Solution Design(解決方案設計)
解決策略:從功能類別收斂 scope:REGISTER、READ、DELETE、BASIC、STAFF、ADMIN、SYSTEM;在 OpenAPI securitySchemes 與每個 operation.security 標明需求。
實施步驟:
- 分類行為與對應 scope
- 實作細節:FSM 動作→類別→scope;繪對照表。
- 所需資源:設計文件
- 預估時間:0.5 天
- OAS 安全定義與標註
- 實作細節:OAuth2 flows + scopes;每個 operation.security 標註。
- 所需資源:OpenAPI
- 預估時間:0.5 天
關鍵程式碼/設定:
components:
securitySchemes:
oauth2:
type: oauth2
flows:
authorizationCode:
authorizationUrl: https://auth/authorize
tokenUrl: https://auth/token
scopes:
REGISTER: Register new member
READ: Read member data
DELETE: Remove member
BASIC: Self updates
STAFF: Staff operations
ADMIN: Admin-only
SYSTEM: Automation jobs
paths:
/api/members/{id}:restrict:
post:
security:
- oauth2: [STAFF]
實作環境:OpenAPI 3 實測數據:
- 改善前:scope 數百個,難維護
- 改善後:scope 僅 6-7 個,語意清楚(文件審閱效率明顯提升)
Learning Points(學習要點) 核心知識點:
- 最小權限單位與可組合性
- OAS securitySchemes/operation.security
- 授權文件化
技能要求:
- 必備技能:OpenAPI 安全描述
- 進階技能:Scope 設計與命名
延伸思考:
- 多域時 scope 如何分包(如 member:read)?
- 舊客戶端的相容性策略?
- Scope 與資源層級 ACL 協同?
Practice Exercise(練習題)
- 基礎練習:用 6 個 scope 標註 10 個端點(30 分)
- 進階練習:額外加入 export/query 的風險控管(2 小時)
- 專案練習:落地到現有 OAS 並驗證授權(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):全端點皆有標註
- 程式碼品質(30%):scope 定義清晰
- 效能優化(20%):文檔導航良好
- 創新性(10%):scope 可重用設計
Case #7: 降級 POC:以最小代價驗證核心邏輯
Problem Statement(問題陳述)
業務場景:折扣引擎/會員流程等核心邏輯若等到 MVP 才驗證,返工成本高。希望用最小代價(不啟用 DB、框架)快速驗證抽象設計。 技術挑戰:如何去除框架與基礎建設依賴、只用原生語言特性驗證核心正確性? 影響範圍:風險控管、需求對齊、設計正確性。 複雜度評級:低
Root Cause Analysis(根因分析)
直接原因:
- 過早實作外圍(DB/UI/DI/Logging)。
- 沒有隔離純商業邏輯,測試成本高。
- 設計對齊晚,會議難以快速「驗證」。
深層原因:
- 架構層面:分層責任不清。
- 技術層面:過度依賴框架。
- 流程層面:缺少「降級」思維。
Solution Design(解決方案設計)
解決策略:採 In-memory repository(List/Dictionary)、Console I/O、純 C# 類別與介面;以實例情境測試集中驗證核心運算與狀態轉移。
實施步驟:
- 定義介面與實體
- 實作細節:IMemberService、實體類別;移除 ORM 依賴。
- 所需資源:C#
- 預估時間:0.5 天
- In-memory 範例與測試
- 實作細節:List/Dictionary 模擬儲存;xUnit/Console 驗證多情境。
- 所需資源:xUnit/Console
- 預估時間:0.5-1 天
關鍵程式碼/設定:
public interface IMemberService {
Member Register(string email);
bool Verify(int id, string code);
}
public class InMemoryMemberService : IMemberService {
private readonly Dictionary<int,Member> db = new();
public Member Register(string email) { ... } // no DB
public bool Verify(int id, string code) { ... }
}
// Console 快速驗證
var svc = new InMemoryMemberService();
var m = svc.Register("a@b.com");
var ok = svc.Verify(m.Id, "123456");
Console.WriteLine($"Verify: {ok}");
實際案例:折扣引擎/會員 FSM 在 100~200 行內先跑通。 實作環境:.NET、xUnit/Console 實測數據:
- 改善前:等到 MVP 後才發現模型問題
- 改善後:設計會議當場即可執行驗證(溝通效率提升)
Learning Points(學習要點) 核心知識點:
- 降級驗證的價值
- In-memory 替代外圍依賴
- 測試驅動設計對齊
技能要求:
- 必備技能:C# 基礎、xUnit
- 進階技能:用介面隔離框架
延伸思考:
- 哪些部分可以延後到 MVP?
- 降級版本如何快速升級到正式架構?
- 如何把這層納入 CI 驗證流程?
Practice Exercise(練習題)
- 基礎練習:做出 In-memory Register/Verify(30 分)
- 進階練習:加入更多 FSM 測試案例(2 小時)
- 專案練習:折扣引擎降級實作+範例(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):用例皆可跑通
- 程式碼品質(30%):簡潔容易擴充
- 效能優化(20%):資料結構合理
- 創新性(10%):可快速轉正規架構
Case #8: Mock-First 驗證情境與並行測試
Problem Statement(問題陳述)
業務場景:前端/外包等待後端環境,造成空窗;QA 無法早期建立自動化測試。需要用 Mock 快速對齊情境,並持續迭代。 技術挑戰:如何讓 Mock 與合約同步?如何在 Mock 上驗證多情境? 影響範圍:等待時間、測試覆蓋率、迭代節奏。 複雜度評級:低
Root Cause Analysis(根因分析)
直接原因:
- 無可執行規格。
- 測試樣本少,場景缺漏。
- Mock 與正式行為脫節。
深層原因:
- 架構層面:規格未作為單一真相來源。
- 技術層面:用手寫 Mock,易飄移。
- 流程層面:QA 晚期才介入。
Solution Design(解決方案設計)
解決策略:以 OpenAPI 做單一來源,使用 Prism 自動 Mock;建立 Postman 範例/情境;CI 驗證 OAS 與範例同步。
實施步驟:
- Mock 伺服器
- 實作細節:Prism 讀 OAS;回傳範例;支援錯誤情境。
- 所需資源:Prism、OAS
- 預估時間:0.5 天
- 情境腳本
- 實作細節:S0-01 ~ S0-04 對應的 Postman Collection。
- 所需資源:Postman/Newman
- 預估時間:0.5 天
關鍵程式碼/設定:
# 啟動 Prism Mock
prism mock openapi.yaml
# Newman 執行情境
newman run scenarios.postman_collection.json
實際案例:註冊→寄信→驗證→Masked 資料流程皆可跑。 實作環境:Prism、Postman/Newman、CI 實測數據:
- 改善前:前端等待環境 1-2 週
- 改善後:當天可串 Mock,QA 當週就能出腳本(體感提升)
Learning Points(學習要點) 核心知識點:
- Mock 與 OAS 同步
- 情境驅動測試
- 錯誤注入
技能要求:
- 必備技能:Prism、Postman
- 進階技能:Newman + CI
延伸思考:
- 如何在 Mock 插入延遲/限流?
- 何時切換到 SandBox/Stage?
- Mock 如何保證與正式版相容?
Practice Exercise(練習題)
- 基礎練習:Mock /register 並測通(30 分)
- 進階練習:加入錯誤情境與重試(2 小時)
- 專案練習:完整情境集合+CI(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):Mock 覆蓋主要端點
- 程式碼品質(30%):範例與文件一致
- 效能優化(20%):Mock 能模擬延遲/錯誤
- 創新性(10%):自動生成 SDK 測試
Case #9: 事件與 Webhook:型別、Payload、驗證
Problem Statement(問題陳述)
業務場景:註冊成功需寄驗證信、狀態變更需通知外部系統。當前沒有標準事件與回呼簽章,整合不穩定、難追蹤。 技術挑戰:如何定義事件型別、Payload、訂閱方式?如何驗證來源真偽並支持重試? 影響範圍:整合穩定性、安全性、可觀測性。 複雜度評級:中
Root Cause Analysis(根因分析)
直接原因:
- 事件名稱與內容不一致。
- 無簽名驗證,易遭偽造。
- 重試與冪等性未定義。
深層原因:
- 架構層面:未採 EDA 思維。
- 技術層面:缺乏 AsyncAPI 規範化。
- 流程層面:整合用戶未參與設計。
Solution Design(解決方案設計)
解決策略:定義 state-changed/action-executing/action-executed 三類事件;統一 Payload;Webhook HMAC 簽章與版本號;重試與 idempotency key。
實施步驟:
- 事件模型與 Payload
- 實作細節:定義必要欄位與 entity snapshot。
- 所需資源:設計文件、AsyncAPI(可選)
- 預估時間:0.5 天
- Webhook 安全與重試
- 實作細節:HMAC-SHA256 簽章;5xx重試、指數退避;Idempotency-Key。
- 所需資源:ASP.NET Core、中介層
- 預估時間:1 天
關鍵程式碼/設定:
// 驗簽示例
public static bool VerifySignature(HttpRequest req, string body, string secret){
var sig = req.Headers["X-Signature"];
using var h = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var calc = Convert.ToHexString(h.ComputeHash(Encoding.UTF8.GetBytes(body)));
return string.Equals(sig, calc, StringComparison.OrdinalIgnoreCase);
}
實際案例:register executed → 信件服務;state-changed → CRM。 實作環境:ASP.NET Core、HMAC、AsyncAPI(可選) 實測數據:
- 改善前:偶發偽造/重複處理
- 改善後:驗簽+冪等邏輯生效,重試可控(內部回歸測試)
Learning Points(學習要點) 核心知識點:
- 事件類型與契約
- Webhook 安全與冪等
- 重試策略
技能要求:
- 必備技能:HMAC/HTTP 基礎
- 進階技能:AsyncAPI、Idempotency
延伸思考:
- 改用 MQ(Kafka/RabbitMQ)時的模式?
- 死信處理與監控?
- 事件版本升級策略?
Practice Exercise(練習題)
- 基礎練習:實作 HMAC 驗簽(30 分)
- 進階練習:添加重試與冪等處理(2 小時)
- 專案練習:完成三類事件+回呼樣板(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):事件與回呼完整
- 程式碼品質(30%):驗簽/冪等清晰
- 效能優化(20%):重試策略合理
- 創新性(10%):事件版本控制
Case #10: NoSQL 思維設計 Entity,避免過度正規化
Problem Statement(問題陳述)
業務場景:REST API 以資源為中心,但資料建模沿用 RDB 正規化思維,導致過度拆分、多次 Join、前端組裝高成本。需改以文件導向設計。 技術挑戰:如何在 JSON 模型中,合理放置 fields/masked-fields/statistics-fields 並兼顧彈性? 影響範圍:端到端效能、資料耦合、演進成本。 複雜度評級:中
Root Cause Analysis(根因分析)
直接原因:
- 以表格正規化思路設計 REST 資源。
- 忽略查詢模式與 API 消費者需求。
- 修改欄位結構需要跨多資源調整。
深層原因:
- 架構層面:缺少 API first 的資料建模觀念。
- 技術層面:對 NoSQL 嵌入與模式版本化不熟。
- 流程層面:資料模型設計未含使用場景。
Solution Design(解決方案設計)
解決策略:Member 採文件模型;state/核心欄位扁平化;可擴充區塊(fields/masked-fields/statistics-fields)獨立;以 _schema_version 控制演進。
實施步驟:
- 設計 JSON 模型
- 實作細節:扁平化核心;擴充區塊;schema version。
- 所需資源:JSON Schema(可選)
- 預估時間:0.5 天
- Masked 與統計分離
- 實作細節:預計算/查詢模式優化;避免重複暴露機敏資料。
- 所需資源:C#
- 預估時間:0.5 天
關鍵程式碼/設定:
{
"_id": "000123",
"email": "a@b.com",
"state": "verified",
"fields": { "_schema_version": "1.0.0" },
"masked-fields": { "name": "王*", "mobile": "09*****123" },
"statistics-fields": { "loginCount": 42 }
}
實際案例:/masked 與 /statistics 以文件模型直接返回。 實作環境:MongoDB/DocumentDB 或 RDB + 映射層 實測數據:
- 改善前:多次查詢與組裝,延遲高
- 改善後:單次取回主要資料,端到端延遲下降(效能測試)
Learning Points(學習要點) 核心知識點:
- 資源導向建模
- 擴充區塊與版本
- Masked 與統計區隔
技能要求:
- 必備技能:JSON 建模
- 進階技能:讀寫模式設計
延伸思考:
- 如何設計查詢索引?
- 何時拆分為子資源?
- 混用 RDB/NoSQL 的策略?
Practice Exercise(練習題)
- 基礎練習:定義 Member JSON(30 分)
- 進階練習:加入 3 個擴充欄位版本升級(2 小時)
- 專案練習:以文件模型重構現有 API(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):滿足端點需求
- 程式碼品質(30%):模式清晰可演進
- 效能優化(20%):查詢/傳輸高效
- 創新性(10%):版本管理策略
Case #11: Security by Design:密碼不可逆儲存
Problem Statement(問題陳述)
業務場景:需處理會員登入/重設密碼。若儲存明碼或可逆加密,發生外洩即造成重大風險。需從設計上避免還原密碼之可能性。 技術挑戰:如何以 PBKDF2/BCrypt 正確儲存?如何配合重設流程與驗證? 影響範圍:法遵、品牌信任、風險控管。 複雜度評級:中
Root Cause Analysis(根因分析)
直接原因:
- 明碼或可逆加密。
- 鹽值與工作因子配置不足。
- 重設流程與 token 管理不當。
深層原因:
- 架構層面:未把安全納入設計。
- 技術層面:密碼雜湊知識不足。
- 流程層面:未建立重設與撤銷流程。
Solution Design(解決方案設計)
解決策略:密碼儲存採 PBKDF2/BCrypt,含隨機鹽與足夠迭代;不支持還原;重設流程透過一次性 token。
實施步驟:
- 實作雜湊與驗證
- 實作細節:PBKDF2/BCrypt;鹽、迭代;時間常數比較。
- 所需資源:.NET 類庫或 BCrypt.Net
- 預估時間:0.5 天
- 重設流程
- 實作細節:/reset-password 產生一次性 token;有效期與撤銷。
- 所需資源:ASP.NET Core
- 預估時間:0.5 天
關鍵程式碼/設定:
public static string HashPassword(string password) {
using var rng = RandomNumberGenerator.Create();
var salt = new byte[16]; rng.GetBytes(salt);
using var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100_000, HashAlgorithmName.SHA256);
var hash = pbkdf2.GetBytes(32);
return $"{Convert.ToBase64String(salt)}.{Convert.ToBase64String(hash)}";
}
public static bool Verify(string password, string stored) {
var parts = stored.Split('.');
var salt = Convert.FromBase64String(parts[0]);
var expected = parts[1];
using var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100_000, HashAlgorithmName.SHA256);
var hash = Convert.ToBase64String(pbkdf2.GetBytes(32));
return CryptographicOperations.FixedTimeEquals(Convert.FromBase64String(hash), Convert.FromBase64String(expected));
}
實際案例:登入/重設流程整合;token 驗證。 實作環境:.NET、PBKDF2/BCrypt 實測數據:
- 改善前:存在還原風險
- 改善後:僅能驗證,不可還原;合規風險降低(稽核通過)
Learning Points(學習要點) 核心知識點:
- 密碼雜湊與鹽
- 重設流程安全
- 時間常數比較
技能要求:
- 必備技能:.NET 密碼學 API
- 進階技能:重設 Token 與撤銷
延伸思考:
- MFA 納入的策略?
- 密碼策略與雜湊成本權衡?
- 雜湊參數的迭代升級?
Practice Exercise(練習題)
- 基礎練習:實作 PBKDF2 Hash/Verify(30 分)
- 進階練習:重設流程+一次性 token(2 小時)
- 專案練習:整合 JWT 與安全日誌(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):不可逆儲存完成
- 程式碼品質(30%):安全 API 正確使用
- 效能優化(20%):迭代成本合理
- 創新性(10%):重設安全細節
Case #12: 對外協力廠商只暴露 Masked 資料
Problem Statement(問題陳述)
業務場景:與超商等協力廠商整合,需列印標籤但不應暴露完整個資(只需手機末三碼、姓氏)。現有 API 無分級資料視圖。 技術挑戰:如何提供 masked 視圖並用 scope 控制?如何避免被列舉出清? 影響範圍:隱私、合規、合作風險。 複雜度評級:中
Root Cause Analysis(根因分析)
直接原因:
- 單一 GET 回傳完整資料。
- 以 API Key 泛用授權。
- 無列舉防護與速率限制。
深層原因:
- 架構層面:缺分級視圖。
- 技術層面:缺 scope 與速率限制。
- 流程層面:整合需求未導入設計。
Solution Design(解決方案設計)
解決策略:新增 /{id}/masked;資料於模型預先提供 masked-fields;以 PARTNER scope 授權;增加速率限制與風險監控。
實施步驟:
- 設計 masked 視圖與資料
- 實作細節:name→王、mobile→09**123。
- 所需資源:C#
- 預估時間:0.5 天
- 授權與限流
- 實作細節:PARTNER scope;RateLimiter;風險告警。
- 所需資源:ASP.NET Core RateLimiting
- 預估時間:0.5 天
關鍵程式碼/設定:
[Authorize(Policy="Scope.READ"), Authorize(Policy="Scope.PARTNER")]
[HttpGet("api/members/{id}/masked")]
public IActionResult GetMasked(string id) {
var m = _svc.GetMasked(id);
return Ok(m.MaskedFields);
}
實際案例:外包/協力商用 masked 視圖完成列印。 實作環境:ASP.NET Core、RateLimiting 實測數據:
- 改善前:外部能取到完整個資
- 改善後:僅可讀 masked,且受限流/審計(稽核通過)
Learning Points(學習要點) 核心知識點:
- 分級資料視圖
- Scope 與限流
- 列舉風險控管
技能要求:
- 必備技能:API 設計
- 進階技能:限流與風控
延伸思考:
- 加上「一次性授權票證」減小暴露面?
- 可否以資料標籤自動遮罩?
- 合作方撤權與審計?
Practice Exercise(練習題)
- 基礎練習:實作 /masked(30 分)
- 進階練習:加入 PARTNER scope 與限流(2 小時)
- 專案練習:整合日誌審計與告警(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):視圖/授權/限流齊備
- 程式碼品質(30%):遮罩規則清晰
- 效能優化(20%):限流策略合理
- 創新性(10%):風險告警設計
Case #13: 組合動作端點(宏操作)不違反 FSM
Problem Statement(問題陳述)
業務場景:部分客戶端希望一口氣完成「註冊+驗證」(例如同站註冊即驗證)。需提供便利端點又不能破壞 FSM 原則。 技術挑戰:如何封裝多步為一端點?如何保證與原子動作結果一致? 影響範圍:使用體驗、正確性。 複雜度評級:中
Root Cause Analysis(根因分析)
直接原因:
- 客戶端想省往返。
- 缺少組合端點定義。
- 容易繞過單步檢查。
深層原因:
- 架構層面:未定義 macro action 原則。
- 技術層面:交易與一致性未設計。
- 流程層面:便捷性與原則衝突未協調。
Solution Design(解決方案設計)
解決策略:新增 /register-and-verify,內部串呼 Register→Verify,包交易,對外視為一呼叫;保證對系統造成的結果與分解動作等價。
實施步驟:
- 設計端點與輸入
- 實作細節:包含 email與驗證依據;對同站場景。
- 所需資源:OAS
- 預估時間:0.5 天
- 交易與等價性
- 實作細節:失敗回滾;審計標註 macro action。
- 所需資源:ASP.NET Core、EF Core
- 預估時間:0.5 天
關鍵程式碼/設定:
[HttpPost("api/members/register-and-verify")]
public async Task<IActionResult> RegisterAndVerify([FromBody] RegVerifyDto dto){
using var tx = await _db.Database.BeginTransactionAsync();
var m = _svc.Register(dto.Email);
var ok = _svc.Verify(m.Id, dto.CodeOrTrust);
if(!ok) { await tx.RollbackAsync(); return BadRequest(); }
await tx.CommitAsync();
return Ok(m);
}
實際案例:同站快速註冊;外部仍走兩步。 實作環境:ASP.NET Core 實測數據:
- 改善前:多次往返
- 改善後:減少 1 次往返且不破壞 FSM(用例測試通過)
Learning Points(學習要點) 核心知識點:
- 宏動作與等價性
- 交易回滾
- 審計與可觀測性
技能要求:
- 必備技能:交易控制
- 進階技能:設計原則與折衷
延伸思考:
- 哪些動作可做宏?哪些不可?
- 客製宏是否導致端點膨脹?
- 文件如何描述等價性?
Practice Exercise(練習題)
- 基礎練習:實作 /register-and-verify(30 分)
- 進階練習:多失敗路徑回滾測試(2 小時)
- 專案練習:定義 3 個宏動作並文件化(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):宏動作可用且等價
- 程式碼品質(30%):交易/錯誤處理清晰
- 效能優化(20%):往返減少
- 創新性(10%):宏動作準則
Case #14: OpenAPI + AsyncAPI 雙規格併用
Problem Statement(問題陳述)
業務場景:API 既有 REST 端點,又會發佈事件(Webhook/MQ)。單靠 OpenAPI 無法完整描述事件面,導致對接模糊。 技術挑戰:如何同步維護 OAS 與 AsyncAPI?如何避免資訊分裂? 影響範圍:文件完整性、對接效率。 複雜度評級:中
Root Cause Analysis(根因分析)
直接原因:
- 僅用 OAS 描述,缺事件面。
- 事件型別與 payload 未標準化。
- 對接端不清楚訂閱流程。
深層原因:
- 架構層面:EDA 與 REST 共存未納入規範。
- 技術層面:AsyncAPI 生態不熟。
- 流程層面:文件來源未統一。
Solution Design(解決方案設計)
解決策略:OAS 描述 REST;AsyncAPI 描述事件通道/訊息;兩者在單一 Repo 維護,版本對齊;文件站整合。
實施步驟:
- 製作 OAS + AsyncAPI
- 實作細節:OAS 敘述端點;AsyncAPI 敘述事件、topic、payload。
- 所需資源:Swagger Editor、AsyncAPI Studio
- 預估時間:1-2 天
- 文件站整合
- 實作細節:Docs 站同時展示;版本號對齊。
- 所需資源:Docs 產生器
- 預估時間:0.5 天
關鍵程式碼/設定:
# AsyncAPI 片段
asyncapi: 2.6.0
info: { title: Member Events, version: '1.0.0' }
channels:
member/state-changed:
subscribe:
message:
name: StateChanged
payload:
type: object
properties:
entity-id: { type: string }
origin-state: { type: string }
final-state: { type: string }
action: { type: string }
實際案例:state-changed/action-executed 事件完整文件化。 實作環境:OpenAPI、AsyncAPI 實測數據:
- 改善前:整合方無從得知事件細節
- 改善後:REST/事件兩面文件齊全(對接 Q/A 減少)
Learning Points(學習要點) 核心知識點:
- OAS 與 AsyncAPI 定位
- 雙規格共存策略
- 文件版本化
技能要求:
- 必備技能:OAS/AsyncAPI 基礎
- 進階技能:Docs 統整
延伸思考:
- 事件演進與版本控管?
- 不同傳輸協定(HTTP/MQ)的對應?
- 自動產生客戶端(SDK/handlers)?
Practice Exercise(練習題)
- 基礎練習:為 state-changed 寫 AsyncAPI(30 分)
- 進階練習:Docs 站合併(2 小時)
- 專案練習:REST+事件雙規格全覆蓋(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):雙規格具備
- 程式碼品質(30%):一致命名與版本
- 效能優化(20%):文件易查找
- 創新性(10%):自動化產生
Case #15: 用狀態機進行情境驗證(大富翁走盤)
Problem Statement(問題陳述)
業務場景:PO/Stakeholder 口述情境常與設計不一致,直到後期才發現。需在設計期快速「走盤」驗證 FSM 是否涵蓋所有情境。 技術挑戰:如何把情境步驟(誰/做什麼/預期狀態)對映到狀態圖轉移?如何自動化驗證? 影響範圍:需求對齊、設計正確性、返工率。 複雜度評級:低
Root Cause Analysis(根因分析)
直接原因:
- 口頭描述難以驗證。
- 無法可視化轉移路徑。
- 缺少自動化腳本支持。
深層原因:
- 架構層面:未把 FSM 當作契約。
- 技術層面:未建立情境→轉移校驗機制。
- 流程層面:設計會議缺少「跑案例」步驟。
Solution Design(解決方案設計)
解決策略:將情境拆為步驟(Caller/Action/Expected State),用工具(手動或輕量腳本)在 FSM 上逐步遷移並校驗;早期即發現不一致。
實施步驟:
- 情境腳本化
- 實作細節:CSV/JSON 列出 S0-01~S0-04;包含 caller/action/expected。
- 所需資源:Excel/JSON
- 預估時間:0.5 天
- 自動化校驗
- 實作細節:寫小工具讀 FSM 定義與情境,逐步檢查。
- 所需資源:C# 小工具
- 預估時間:0.5 天
關鍵程式碼/設定:
public record Step(string Caller, string Action, string ExpectedState);
public class Fsm {
private readonly Dictionary<(string,string), string> trans;
public Fsm(Dictionary<(string,string),string> t) { trans = t; }
public string Transit(string current, string action) => trans[(current, action)];
}
// 載入步驟,逐步驗證
實際案例:S0-01(Register)→ S0-02(Verify)→ S0-03(Get)→ S0-04(Get-Masked)。 實作環境:C# 小工具/白板手動 實測數據:
- 改善前:需求與設計不一致晚期才爆
- 改善後:設計會議當場發現偏差,快速修正(會議效率提升)
Learning Points(學習要點) 核心知識點:
- FSM 作為對齊工具
- 情境腳本化
- 校驗自動化
技能要求:
- 必備技能:基本資料結構
- 進階技能:工具化與視覺化
延伸思考:
- 可否從 FSM 生成 API 測試?
- 與合約測試結合?
- GUI 可視化展示路徑?
Practice Exercise(練習題)
- 基礎練習:為 3 個步驟寫校驗(30 分)
- 進階練習:讀 mermaid 自動構圖與校驗(2 小時)
- 專案練習:全情境走盤工具(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):所有步驟可驗
- 程式碼品質(30%):資料結構清晰
- 效能優化(20%):易擴展多情境
- 創新性(10%):圖形化展示
Case #16: 分離 Entity/Repository/Service,避免過度依賴 ORM
Problem Statement(問題陳述)
業務場景:現行將商業邏輯寫進 ORM 實體或 Controller,難以單測與重用。需進行責任分離,保留 Entity 資料導向,將邏輯置於 Service。 技術挑戰:如何從 static/class method 遷移到 repository/service 層?如何保持測試與可替換性? 影響範圍:可維護性、可測性、耦合度。 複雜度評級:中
Root Cause Analysis(根因分析)
直接原因:
- 邏輯分散在 Controller/Entity。
- 測試需依賴 DB/框架。
- 難以替換儲存層。
深層原因:
- 架構層面:分層責任未落實。
- 技術層面:未定義抽象介面。
- 流程層面:未以測試驅動設計。
Solution Design(解決方案設計)
解決策略:定義 IMemberRepository 與 IMemberService;Entity 只存資料/輕微驗證;業務邏輯在 Service;Controller 調用 Service。
實施步驟:
- 定義抽象介面
- 實作細節:IMemberRepository(Get/Save/Find);IMemberService(動作)。
- 所需資源:C#
- 預估時間:0.5 天
- 實作與注入
- 實作細節:EF Repo、Service 封裝 FSM;DI 注入。
- 所需資源:ASP.NET Core、EF
- 預估時間:1 天
關鍵程式碼/設定:
public interface IMemberRepository {
MemberEntity? Get(int id);
void Save(MemberEntity entity);
}
public interface IMemberService {
MemberEntity Register(string email);
bool Verify(int id, string code);
}
// Controller 僅呼叫 Service
實際案例:由 Service 封裝 Verify/Restrict 等轉移。 實作環境:ASP.NET Core、EF Core 實測數據:
- 改善前:單測困難、邏輯散亂
- 改善後:Service 層可單測,重用性提升(測試覆蓋率上升)
Learning Points(學習要點) 核心知識點:
- 分層設計
- Repository/Service 模式
- 依賴反轉
技能要求:
- 必備技能:介面/DI
- 進階技能:測試友善設計
延伸思考:
- DDD 與輕量 Service 的取捨?
- Domain Event 位置?
- TransactionScript vs DomainModel?
Practice Exercise(練習題)
- 基礎練習:定義 Repo/Service 介面(30 分)
- 進階練習:Service 單元測試(2 小時)
- 專案練習:重構一組端點至 Service(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):介面齊全
- 程式碼品質(30%):耦合度低
- 效能優化(20%):可測性高
- 創新性(10%):可替換儲存層
Case #17: OAuth2 使用者同意與撤銷流程設計
Problem Statement(問題陳述)
業務場景:需要整合第三方登入/授權(類似 Google→小米畫面)。使用者在授權/撤銷時需建立信任,並保證撤權立即生效。 技術挑戰:如何設計同意畫面資訊、最小化 scope、撤銷與資料刪除流程? 影響範圍:使用者信任、法遵、品牌。 複雜度評級:高
Root Cause Analysis(根因分析)
直接原因:
- 同意畫面資訊不足(用途不清)。
- Scope 過大。
- 撤銷/刪除不可驗證。
深層原因:
- 架構層面:未規劃授權/撤權設計。
- 技術層面:OAuth2/PKCE/Refresh token 管理不足。
- 流程層面:缺少「刪除我的資料」機制。
Solution Design(解決方案設計)
解決策略:Authorization Code + PKCE;嚴格最小化 scope(READ/REGISTER…);撤權 API 與 token 黑名單;資料刪除 Webhook 通知;同意畫面清楚列示。
實施步驟:
- Flow 與 scope 設計
- 實作細節:PKCE;短生命 Access Token;最小 scopes。
- 所需資源:OAuth2 供應商
- 預估時間:1 天
- 撤權與刪除
- 實作細節:/revoke 與黑名單;刪除事件通知。
- 所需資源:Auth Server、API
- 預估時間:1 天
關鍵程式碼/設定:
POST /oauth/revoke
Content-Type: application/x-www-form-urlencoded
token={access_or_refresh_token}&token_type_hint=access_token
實際案例:第三方登入後僅授 READ;撤銷即時生效,合作方接收刪除 webhook。 實作環境:OAuth2(Auth0/Azure AD B2C/自架)、API 實測數據:
- 改善前:使用者疑慮大、撤權不即時
- 改善後:告知明確、撤權即刻,投訴下降(客服回饋)
Learning Points(學習要點) 核心知識點:
- OAuth2/PKCE
- Scope 最小化
- 撤權與刪除機制
技能要求:
- 必備技能:OAuth2 配置
- 進階技能:Token 黑名單/撤銷流程
延伸思考:
- 與多方整合時的信任鍊?
- 刪除資料審計與證明?
- 長期授權的風險控制?
Practice Exercise(練習題)
- 基礎練習:設定 PKCE Flow(30 分)
- 進階練習:實作 /revoke 與 webhook(2 小時)
- 專案練習:同意畫面設計與文件化(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):Flow/撤權完整
- 程式碼品質(30%):安全配置正確
- 效能優化(20%):Token 壽命合理
- 創新性(10%):透明化溝通設計
Case #18: 查詢/列舉風險與速率限制
Problem Statement(問題陳述)
業務場景:若提供批量查詢或無 id 的查詢端點,可能被外部掃描列舉,導致資料外洩風險。需控管 QUERY/EXPORT 類操作。 技術挑戰:如何限制查詢範圍、速率、授權並監控異常? 影響範圍:資料安全、服務穩定。 複雜度評級:中
Root Cause Analysis(根因分析)
直接原因:
- 無 Query scope 區分。
- 無速率限制。
- 無異常監控/告警。
深層原因:
- 架構層面:對列舉風險認知不足。
- 技術層面:未善用中介層限流。
- 流程層面:未建立風控規則。
Solution Design(解決方案設計)
解決策略:為批量查詢單獨設計 QUERY/EXPORT scope;加入 RateLimiting;必要時加入 Captcha/Proof-of-Work;監控列舉行為。
實施步驟:
- Scope 與端點設計
- 實作細節:查詢端點需 QUERY scope。
- 所需資源:OAS
- 預估時間:0.5 天
- 限流與監控
- 實作細節:固定/令牌桶限流;異常模式告警。
- 所需資源:ASP.NET RateLimiting、監控
- 預估時間:0.5 天
關鍵程式碼/設定:
builder.Services.AddRateLimiter(_ => _.AddFixedWindowLimiter("query", opt => {
opt.PermitLimit = 30; opt.Window = TimeSpan.FromMinutes(1); opt.QueueLimit = 0;
}));
[EnableRateLimiting("query")]
[Authorize(Policy="Scope.QUERY")]
[HttpGet("api/members:search")]
public IActionResult Search([FromQuery]SearchDto q) { ... }
實際案例:批量匯出需 EXPORT scope 並受強限流,產出以 webhook 通知。 實作環境:ASP.NET Core RateLimiting、監控 實測數據:
- 改善前:列舉風險無防護
- 改善後:查詢受 scope 與限流控制,異常告警(風險降低)
Learning Points(學習要點) 核心知識點:
- 列舉風險
- Scope/限流/監控
- 非同步匯出
技能要求:
- 必備技能:限流
- 進階技能:風控監控
延伸思考:
- 如何引入動態風險評分?
- 長時間作業設計回呼模式?
- 離線匯出避免即時壓力?
Practice Exercise(練習題)
- 基礎練習:為查詢端點加限流(30 分)
- 進階練習:設 EXPORT + webhook 完成匯出(2 小時)
- 專案練習:建立查詢風控與告警(8 小時)
Assessment Criteria(評估標準)
- 功能完整性(40%):scope/限流/監控
- 程式碼品質(30%):配置清晰
- 效能優化(20%):資源保護有效
- 創新性(10%):匯出架構設計
案例分類
- 按難度分類
- 入門級(適合初學者)
- Case #2 OOP→REST 對應
- Case #7 降級 POC
- Case #8 Mock-First
- Case #10 NoSQL Entity 設計(基礎)
- Case #15 狀態機走盤
- 中級(需要一定基礎)
- Case #1 Contract-First 並行開發
- Case #3 FSM 取代 CRUD
- Case #5 角色與授權策略
- Case #6 Scope 設計收斂
- Case #12 Masked 視圖
- Case #13 組合動作
- Case #14 OAS+AsyncAPI
- Case #18 查詢/列舉風險
- 高級(需要深厚經驗)
- Case #4 原子性與並發控制
- Case #9 事件與 Webhook 安全
- Case #11 密碼不可逆儲存與流程
- Case #16 分層與可測設計
- Case #17 OAuth2 同意/撤權
- 入門級(適合初學者)
- 按技術領域分類
- 架構設計類:#1 #3 #6 #10 #14 #15 #16
- 效能優化類:#4 #18
- 整合開發類:#8 #9 #12 #13 #14 #17
- 除錯診斷類:#15(設計驗證)、#4(併發問題)
- 安全防護類:#5 #9 #11 #12 #17 #18
- 按學習目標分類
- 概念理解型:#2 #3 #6 #10 #14 #15
- 技能練習型:#7 #8 #11 #12 #13
- 問題解決型:#1 #4 #5 #9 #16 #18
- 創新應用型:#13 #14 #17
案例關聯圖(學習路徑建議)
- 建議先學:
- #2(OOP→REST 對應)→ #3(FSM 取代 CRUD)→ #15(用 FSM 驗證情境)
- 這三者建立共同語言與設計思維
- 中期學習(串接流程):
- #1(Contract-First)→ #8(Mock-First)→ #6(Scope 收斂)→ #5(授權策略)
- 並補 #10(Entity 設計)、#16(分層)
- 進階議題(安全與穩定):
- #4(原子性並發)→ #9(Webhook/事件)→ #11(密碼與重設)
- 加上 #12(Masked 視圖)、#18(查詢風險)
- 規格與整合深化:
- #14(OAS+AsyncAPI)→ #13(組合動作)→ #17(OAuth2 同意/撤權)
依賴關係摘要:
- #3 依賴 #2 的對應概念
- #5/#6 依賴 #3 的動作與角色盤點
- #4 依賴 #3 的狀態轉移點
- #8 依賴 #1 的合約
- #12 依賴 #10 的資料視圖設計
- #14 依賴 #9 的事件定義
- #17 依賴 #5/#6 的 scope 策略
完整學習路徑(由淺入深): #2 → #3 → #15 → #1 → #8 → #6 → #5 → #10 → #16 → #4 → #9 → #11 → #12 → #18 → #14 → #13 → #17
以上案例與路徑可直接用於實戰工作坊、專案演練與人員評估。