知道參數是字串、結果是整數,還不足以正確使用 API。 同樣一段文字,可能因長度、缺值或編碼規則不同而被接受或拒絕; 同樣收到失敗訊號,也不一定代表操作完全沒有改變資料。
本文接續〈介面、契約與責任〉, 說明如何讀懂資料限制、驗證、結果與副作用,不預設程式語言、訊息格式或 HTTP 狀態碼。
查證日期:2026-11-02。官方實例採 JSON Schema Draft 2020-12、Java SE 25, 以及查證當日的 gRPC 官方狀態碼文件;後者是持續更新的文件,未標示固定版本。 本文只查閱文件,未執行程式或服務測試;自創例子是教學規則,不代表既有產品。
型別以外,還有哪些規則
型別描述資料屬於哪一類;使用規則還要說明哪些值有效,以及如何解讀它們。
| 面向 | 需要釐清的問題 |
|---|---|
| 值的範圍 | 整數可以是負數嗎?上下限是否包含邊界? |
| 單位 | 時間數值代表秒還是毫秒?文字長度按位元組還是其他單位計算? |
| 文字表示 | 若輸入是位元組,採什麼編碼解讀?是否保留前後空白? |
| 缺值 | 未提供、明確傳入空值、空字串,分別代表什麼? |
| 與狀態的關係 | 即使資料形式正確,目前是否允許這項操作? |
「必填」與「不接受空值」是不同限制。
例如 JSON Schema Draft 2020-12 的 required 檢查物件是否具有指定名稱的屬性;
屬性值能否為 null,則還要看型別等限制。
因此,屬性存在但值為 null,可以通過 required,卻不符合只接受字串的型別要求。
參考 JSON Schema:required
與type。
這是 JSON 的具體規則;其他介面的缺值表達方式須另讀自己的文件。
用「設定清單標題」貫穿觀察
以下是本站自創的教學例子。假設程式已持有一份可修改的清單,提供這個操作:
設定清單標題(title) → 成功(舊標題, 新標題) 或 失敗(標題無效)
這是概念偽碼,成功、失敗 是兩種可辨識的結果,不指定實際語法或資料格式。
本例只處理記憶體內的一份清單,不涉及檔案、網路或其他程式同時修改它的情況。
| 項目 | 本例規則 |
|---|---|
| 輸入型別 | title 必須提供,且為文字字串;不接受空值,也不自動把數字轉為字串 |
| 有效長度 | 1 到 20 個 Unicode 碼位(code points),包含兩端;本例以碼位數定義長度,不以畫面上的字形數計算 |
| 文字處理 | 保留所有輸入文字,不移除空白、不截短,也不做其他轉換;只有空白的字串仍可有效 |
| 輸入有效 | 將標題設為輸入文字,回傳成功;舊標題是呼叫前的值,新標題是設定後的值 |
| 輸入無效 | 未提供、空值、非字串、長度為 0 或超過 20,均回傳 失敗(標題無效) |
| 狀態影響 | 成功只設定標題,不改變項目內容與順序;上述輸入失敗不改變任何清單資料 |
舊標題與新標題相同也算成功;本例沒有把「必須與原值不同」列為限制。 這些規則都是教學設計的選擇,其他 API 可以採取不同規則。
以下每一列都獨立從標題「待辦」、項目內容相同的清單開始:
| 輸入 | 結果 | 呼叫後的標題 |
|---|---|---|
| 「讀書」 | 成功(「待辦」, 「讀書」) | 「讀書」 |
| 「待辦」 | 成功(「待辦」, 「待辦」) | 「待辦」 |
| 空字串 | 失敗(標題無效) | 「待辦」 |
未提供、空值或數字 123 | 失敗(標題無效) | 「待辦」 |
| 連續 21 個「甲」 | 失敗(標題無效) | 「待辦」 |
驗證發生在哪個邊界
驗證(validation)是檢查輸入是否符合已定義的規則。 在本例,提供者先確認輸入存在、型別與長度都有效,才設定標題。 如果先改了標題,再發現長度不符而回傳失敗,就無法履行「輸入失敗時清單不變」的承諾。
呼叫端也可以先檢查,讓人及早看到錯誤;但當提供者承諾拒絕無效輸入時, 不能只依賴某個畫面的檢查,因為其他程式也可能直接呼叫這個介面。 在靜態型別的程序內 API,部分型別限制可能由編譯器檢查; 是否還需要執行時檢查,要看哪些限制已受保證、哪些輸入仍可能到達該邊界。
驗證與轉換也應區分。本例遇到 21 個碼位會拒絕,而不是截成 20 個。 若要先移除空白再檢查長度,就必須明訂轉換順序;那會是另一套行為。 若某項限制依賴會變動的狀態,只在很早之前檢查,也不足以保證實際修改時仍然成立。
輸出值要連同意義一起讀
本例的成功結果包含兩個字串,但順序與意義不能互換。 「舊標題」取自呼叫前,「新標題」則是這次設定的值;呼叫端不能只看到兩個字串就自行猜測。 同樣地,若某個 API 回傳整數,還要確認它代表數量、位置,或已完成的工作量。
特殊值尤其需要說明。Java SE 25 的 Map.get(key) 在沒有對應項目時回傳 null;
若該 Map 允許儲存 null,回傳 null 也可能代表「該鍵存在,值就是 null」。
文件指出可用 containsKey 區分這兩種情況。
因此不能把所有 API 的 null 都解讀成失敗,或一律解讀成資料不存在。
參考 Java SE 25:Map.get。
失敗的原因與表達方式是兩件事
失敗語意要說明什麼情況算失敗、呼叫端如何辨識,以及失敗後仍有哪些保證。 本例的「標題無效」是已定義的輸入失敗;它不是提供者程式故障的證據。 「預期會遇到的失敗」也不表示一定採用某種語法。
| 表達方式 | 呼叫端如何得知 |
|---|---|
| 結果值 | 本例直接回傳可區分的成功或失敗結果,呼叫端檢查結果種類 |
| 例外 | Java 的 Integer.parseInt(String) 在字串無法解析成 int 時拋出 NumberFormatException,呼叫端依例外機制處理 |
| 協定資訊 | gRPC 以包含狀態碼與錯誤描述的 status 表達呼叫結果;語言函式庫再以自己的介面呈現 |
Java 的具體行為見 Java SE 25:Integer.parseInt; gRPC 的規則見官方狀態碼文件。 這些表達可以在不同層之間轉換;看到語言中的例外,不能直接推定遠端使用了哪種錯誤格式。 可供程式判斷的結果種類、例外型別或狀態碼,與給人閱讀的說明文字,也應分清楚。 除非文件保證文字是固定的判斷依據,否則不宜只比對一句錯誤描述來決定行為。
失敗不一定代表狀態未改變
副作用(side effect)是操作除了產生結果值之外,對可觀察狀態造成的影響, 例如設定標題、改寫檔案或修改集合。它可以是操作的主要目的,並不表示發生了錯誤。
本例明訂輸入失敗時清單不變,呼叫端才有這項保證。
不能把它擴大成「所有 API 只要回報失敗,就一定什麼都沒做」。
例如 gRPC 官方文件明確說明:對會改變狀態的操作,DEADLINE_EXCEEDED 仍可能出現在操作已成功完成之後,
原因可能是成功回應延遲到期限之後才到達。
參考 gRPC:DEADLINE_EXCEEDED。
這時,呼叫端知道自己沒在期限內取得成功結果,卻不能只憑這個狀態碼判定服務沒有執行操作。 閱讀失敗規則時,要確認文件是否保證不變、可能已改變,或無法從目前結果確定。 如何確認遠端結果、是否能重試,以及如何避免重複影響,見逾時、重試與冪等性。
相關文件
- 介面、契約與責任:回看提供者與使用者的責任,以及成功與失敗路徑的承諾。
- 相容性與演進:接著理解輸入、輸出與失敗規則改變時,如何影響既有使用者。
- API 共通知識索引:查看閱讀順序、HTTP 分支與延伸議題。