在做任何事前,先確保輸入是可信的。
想像你的每一個函式、每一個 API 端點,都是一座戒備森嚴的堡壘。而傳入的參數,就是試圖通過城門的陌生人。
你的驗證程式碼,就是城門口的衛兵。衛兵的職責不是友善地假設每個陌生人都是好人。
衛兵的職責是保持偏執、充滿懷疑,把每個陌生人都當成潛在的間諜或破壞者來盤問,檢查他們所有的證件。
一個疏忽可能讓惡意攻擊者長驅直入,SQL 注入 (SQL Injection)、跨網站指令碼 (XSS) 等成為重大的安全漏洞。
只有通過了這道嚴格的盤查,這些資料才被允許進入你那乾淨、受信任的堡壘內部。
壞資料進來,壞結果出去。
這段程式碼,就是一座沒有衛兵、城門大開的堡壘。
它天真地相信呼叫者永遠會傳入兩個合法的數字,而且分母永遠不為零。
在實際情況裡會被無情地利用,直到系統在生產環境中崩潰。
// 🔴 臭味而且是危險的那種
// 它盲目地信任輸入,這是在玩火。
function divide(a, b) {
return a / b;
}
// divide(10, 0) -> Infinity
// divide(10, "hello") -> NaN
// divide(null, 2) -> 0 (這是你想要的嗎?)
它推遲了爆炸: 當你傳入 (10, 0)
,divide
函式本身沒有立刻爆炸。它默默地回傳了一個 Infinity
這個鬼東西。這個 Infinity
可能會被傳遞給後續十個函式,直到最後某個地方因為無法處理它而崩潰。屆時,你得回溯一長串的呼叫鏈,才能找到最初的罪魁禍首。
它的錯誤沒有任何幫助: Infinity
或 NaN
這種回傳值,沒有告訴呼叫者他到底做錯了什麼。
它沒有定義契約: 這個函式沒有跟它的使用者立下清晰的契約。它沒有明確說明「我需要什麼」以及「當你不遵守規則時會發生什麼」。
好的程式碼充滿了「健康的偏執」。
它在執行任何核心邏輯之前,會先在入口處設定層層關卡衛兵模式 (保護式敘述) Guard Clauses。
// 🟢 好衛兵:這是一座有嚴謹看守的堡壘。
function divide(a, b) {
// 衛兵 1: 檢查型別證件
if (typeof a !== 'number' || typeof b !== 'number') {
throw new TypeError('Both arguments must be numbers.');
}
// 衛兵 2: 檢查業務規則
if (b === 0) {
throw new RangeError('Divisor cannot be zero.');
}
// --- 通過所有檢查後,才來到受信任的堡壘核心 ---
return a / b;
}
快速失敗,大聲呼救 (Fail Fast, Fail Loud): 一旦發現輸入有問題,程式就立刻、馬上在入口處拋出錯誤,並且用一個極其清晰的錯誤訊息大聲地告訴呼叫者「你做錯了什麼」。這讓除錯變得異常簡單。
主要邏輯的純淨: return a / b;
這行主要邏輯,現在可以 100% 信任 a
和 b
是安全的。它不需要再做任何防禦性檢查了。
清晰的契約: 專業的 API 設計函式契約非常清楚:「你必須給我兩個數字,且第二個不能為零。如果你違反契約,我會立刻把你踢出去,並告訴你原因。」
一個常見的誤區是只在前端做驗證。
客戶端驗證 (前端):主要目的是提升使用者體驗 (UX),提供即時的回饋,避免使用者提交無效的表單。
伺服器端驗證 (後端):主要目的是保障系統安全與資料完整性。
客戶端的驗證可以被輕易繞過,因此伺服器端的驗證是絕對不可或缺的最後防線。
這是兩個相輔相成但不同的概念:
驗證 (Validation):是拒絕不符合規則的資料。
-10
,驗證的職責是直接拋出錯誤。消毒 (Sanitization):是清理可接受但不夠乾淨的資料。
" John Doe "
(後面有空格),消毒的職責是將其處理成 "John Doe"
。處理順序:先驗證,再消毒,最後才使用資料。
與其手寫大量的 if/else
,不如使用 Zod/Joi 這類工具,將驗證規則集中定義為一個「Schema」(綱要)。
可以成立系統的「單一事實來源」,讓驗證更簡單,還能自動生成 TypeScript 型別,並用於自動化測試。
覆蓋案例:必須包含正向 (通過)、反向 (失敗)、邊界值 (null
、空字串、0
、min/max
) 的測試。
驗證訊息:斷言 (Assert) 回傳的錯誤訊息是否精準、符合預期。
基礎檢查:是否已驗證輸入的型別、格式與範圍?
存在性檢查:是否已確保所有必填欄位都存在,且不為 null
或空字串?
安全性考量:目前的檢查是否足以防禦已知的惡意輸入模式(如腳本注入)?
錯誤回饋:回傳的錯誤訊息是否清晰、具體,能引導使用者修正?
預設拒絕:系統是否在處理任何邏輯前,就立即拒絕來源不可信的輸入?
將驗證視為安全的第一道防線,並在處理任何邏輯前執行。
讓錯誤快速、大聲、且清晰可預測,這能簡化除錯。
杜絕髒資料,讓你的核心邏輯只處理乾淨、可信的資料。
永遠不要信任客戶端,伺服器端驗證是絕對必要的。