api
hello,大家好,我是楊楊!如今在我們後端開發程式員的工作中,寫API接口就是家常便飯了。小編最近也是老寫接口,寫着寫着讓我産生了用它來作為創作靈感。好,話不多說,今天就來聊聊接口(API)設計規範。
當設計一個軟體系統時,接口(API)的設計是至關重要的一環。一個好的接口設計可以增強系統的可維護性、可擴充性和可重用性。
設計API意味着提供有效的接口,可以幫助API使用者更好地了解、使用和內建,同時幫助人們有效地維護它。每個産品都需要使用手冊,API也不例外。
在API領域,可以将設計視為伺服器和用戶端之間的協定進行模組化。API協定可以幫助内部和外部的利益相關者了解應該做什麼,以及如何更好地協同工作來建構一個出色的API。
當下最好遵循RESTful API設計原則
RESTful API是一種基于HTTP協定的API設計風格。它将資源作為基本的概念,并通過HTTP動詞(GET、POST、PUT、DELETE等)來表示對資源的操作。在設計RESTful API時,需要遵循以下原則:
- 使用合适的HTTP方法來表示資源的操作,如GET表示擷取資源,POST表示建立資源,PUT表示更新資源,DELETE表示删除資源。
- 使用URI來表示資源的位置,URI應該是短小且易于了解的,如/api/users。
- 使用HTTP狀态碼來表示請求的結果,如200表示請求成功,400表示請求參數有誤,404表示請求的資源不存在等。
- 使用HTTP頭部來表示附加資訊,如Content-Type表示請求和響應的資料類型,Authorization表示授權資訊等。
使用一緻的命名規範
在設計接口時,需要使用一緻的命名規範來表示資源、參數和操作。這樣可以使接口更易于了解和使用。以下是一些命名規範的建議:
- 資源名應該使用複數形式,如/users表示多個使用者資源。
- 使用動詞+名詞的方式來表示操作,如GET /users表示擷取使用者資源。
- 參數名應該使用小寫字母和下劃線分隔符,如user_id。
- 使用适當的縮寫和單詞縮寫來簡化命名,如使用“id”代替“identifier”。
定義清晰的錯誤處理機制
在設計接口時,需要考慮錯誤處理機制。一個好的錯誤處理機制應該包括以下幾個方面:
- 傳回合适的HTTP狀态碼來表示錯誤類型,如400表示請求參數錯誤,401表示未授權,500表示伺服器内部錯誤等。
- 傳回清晰的錯誤資訊,包括錯誤代碼和錯誤描述。
- 定義錯誤碼和錯誤描述的映射關系,友善用戶端進行處理。
确定合适的安全機制
在設計接口時,需要考慮安全機制。合适的安全機制可以保證接口的安全性和可靠性。以下是一些安全機制的建議:
- 使用HTTPS協定來保證資料的安全傳輸。
- 使用OAuth2.0授權機制來保護接口資源,限制未授權通路。
- 對敏感資料進行加密處理,如使用者密碼等。
接口更新
接口更新是 API 開發中的一個常見問題,随着業務需求的變化和技術的進步,接口的設計也需要不斷地優化和更新。然而,接口的更新需要謹慎處理,以免影響現有的業務。
在接口更新的過程中,需要考慮以下幾個方面:
- 相容性:新版本的接口應該保持與舊版本的接口相容,以確定現有的應用能夠正常運作。如果新版本的接口與舊版本的接口不相容,那麼就需要在文檔中明确告知使用者,并給出遷移方案。
- 文檔更新:在更新接口的同時,也需要更新相關的文檔。文檔應該包括接口的使用說明、參數清單、傳回值等資訊,并且應該與實際的接口保持一緻。
- 測試驗證:在更新接口之前,需要進行充分的測試驗證。測試可以包括單元測試、內建測試、功能測試等,以確定新版本的接口能夠正确地運作。
- 版本控制:在更新接口時,需要對不同版本的接口進行版本控制。這可以讓使用者明确地知道自己使用的是哪個版本的接口,并且可以讓使用者有機會在自己的時間範圍内遷移到新版本的接口。
總的來說,接口更新是一個複雜的過程,需要在設計階段就充分考慮接口的擴充性和相容性。同時,更新過程中需要進行充分的測試和文檔更新,并進行版本控制,以確定使用者的正常使用。
細節
- 用戶端IP白名單:在接口設計中,可以設定用戶端IP白名單,僅允許指定的IP位址通路該接口,進而增加接口的安全性。
- IP限流:為了防止惡意請求或其他不合法請求,可以通過IP限流的方式限制同一IP在一定時間内通路接口的頻率。
- 請求日志:接口應該記錄請求日志,包括請求的URL、請求參數、請求時間等資訊,以便于後續排查問題或性能優化。
- 接口的幂等性:接口應該具備幂等性,即多次請求同一接口,對于同一資源的操作結果應該保持一緻。這樣可以避免因為重複請求而引起的業務問題。
- 公共參數:接口設計中應該考慮到公共參數,例如token等,在接口請求時應該對公共參數進行校驗,保證接口的安全性。
- 标準響應:接口應該傳回标準的響應結果,包括響應碼、響應消息、響應資料等。并且,響應結果應該符合統一的格式,以友善用戶端進行解析。
- 統一業務碼:接口設計時應該使用統一的業務碼,以區分不同的業務場景和業務操作,友善系統進行跟蹤和日志記錄。
示例
用部分代碼來說明接口規範細節
- 接口的幂等性設計
class Idempotency
{
private $redis; // Redis 執行個體
public function __construct()
{
// 初始化 Redis 連接配接
$this->redis = new Redis();
$this->redis->connect('127.0.0.1', 6379);
}
/**
* 判斷請求是否已處理過
* @param $key string 請求唯一辨別
* @return bool
*/
public function isProcessed($key)
{
// 判斷 Redis 中是否已存在請求辨別
return $this->redis->get($key) === '1';
}
/**
* 處理請求
* @param $key string 請求唯一辨別
* @param $callback callable 回調函數,用于處理實際請求
* @return mixed|null
*/
public function processRequest($key, $callback)
{
// 如果請求已經處理過,直接傳回處理結果
if ($this->isProcessed($key)) {
return $this->redis->get($key . ':response');
}
// 執行回調函數,并記錄請求處理結果
$result = $callback();
$this->redis->set($key . ':response', $result);
$this->redis->set($key, '1');
// 傳回處理結果
return $result;
}
}
// 執行個體化 Idempotency 類
$idempotency = new Idempotency();
// 生成請求唯一辨別
$key = md5(json_encode(['user_id' => 123, 'product_id' => 456]));
// 定義回調函數,用于處理實際請求
$callback = function () {
// 實際處理請求的代碼
// ...
return 'success';
};
// 處理請求,并保證接口的幂等性
$result = $idempotency->processRequest($key, $callback);
- IP限流
常見的限流算法有:
- 漏桶算法 漏桶算法(Leaky Bucket)是一種簡單的限流算法,它通過對請求的速率進行限制來平滑流量。漏桶算法通過一個固定容量的“漏桶”來限制流量。當一個請求到達時,将會被加入到漏桶中。如果漏桶已滿,則請求将被拒絕。如果漏桶未滿,則請求将被從漏桶中取出,同時漏桶中的容量會随着時間的流逝而減少,這樣可以平滑流量。
- 令牌桶算法 令牌桶算法(Token Bucket)是一種基于令牌的限流算法,它通過對請求的速率進行限制來平滑流量。令牌桶算法維護一個固定容量的令牌桶,每個令牌代表着一個請求的權限。當一個請求到達時,将會從令牌桶中取出一個令牌。如果令牌桶中沒有令牌,則請求将被拒絕。令牌桶算法可以通過控制生成令牌的速率和令牌桶中最大容量來限制請求的速率。
- 計數器算法 計數器算法(Counter)是一種基于計數器的限流算法,它通過對請求的數量進行限制來平滑流量。計數器算法維護一個計數器,每當一個請求到達時,計數器的值會加一。當計數器的值超過一個預設的門檻值時,請求将被拒絕。計數器算法可以通過調整門檻值來控制請求的速率。
- 滑動視窗算法 滑動視窗算法(Sliding Window)是一種基于時間視窗的限流算法,它通過對請求的時間間隔進行限制來平滑流量。滑動視窗算法将請求按照時間順序分組,并在每個時間視窗内對請求進行計數。如果一個時間視窗内的請求超過了預設的門檻值,則請求将被拒絕。滑動視窗算法可以通過調整時間視窗的長度和門檻值來控制請求的速率。
基于令牌桶算法的 示例:
class TokenBucket
{
private $capacity; // 桶容量
private $tokens; // 目前令牌數量
private $rate; // 令牌産生速率
private $timestamp; // 上一次令牌産生時間戳
/**
* 構造函數
* @param $capacity int 桶容量
* @param $rate int 令牌産生速率
*/
public function __construct($capacity, $rate)
{
$this->capacity = $capacity;
$this->tokens = $capacity; // 初始化為滿桶
$this->rate = $rate;
$this->timestamp = time();
}
/**
* 擷取令牌,如果擷取不到則傳回 false
* @return bool
*/
public function get()
{
$this->generateTokens(); // 生成令牌
if ($this->tokens > 0) { // 桶中有令牌
$this->tokens--; // 取出一個令牌
return true;
} else { // 桶中沒有令牌
return false;
}
}
/**
* 生成令牌
*/
private function generateTokens()
{
$now = time();
$deltaTime = $now - $this->timestamp;
$this->tokens += $deltaTime * $this->rate;
$this->tokens = min($this->tokens, $this->capacity); // 令牌數量不超過桶容量
$this->timestamp = $now;
}
}
class IpLimiter
{
private $bucket;
/**
* 構造函數
* @param $capacity int 桶容量
* @param $rate int 令牌産生速率
*/
public function __construct($capacity, $rate)
{
$this->bucket = new TokenBucket($capacity, $rate);
}
/**
* 判斷 IP 是否允許通路
* @param $ip string IP 位址
* @return bool
*/
public function allow($ip)
{
$key = 'ip_limiter:' . $ip;
$redis = new Redis();
$redis->connect('127.0.0.1', 6379); // 連接配接 Redis
if ($redis->exists($key)) { // IP 已經通路過
$count = $redis->incr($key); // 增加計數
if ($count > $this->bucket->capacity) { // 計數超過桶容量
return false;
}
} else { // IP 第一次通路
$redis->set($key, 1, $this->bucket->rate); // 設定計數器并設定過期時間為令牌生成間隔
}
return $this->bucket->get(); // 擷取令牌
}
}
基于漏桶算法的示例:
該算法維護一個固定容量的水桶,當請求到來時,将水桶中的水流出一部分,然後判斷是否可以接受該請求。如果目前水桶中的水不足以容納請求,則拒絕該請求。通過設定不同的容量和速率參數,可以實作不同的限流政策
class LeakyBucket {
private $capacity;
private $rate;
private $water = 0;
private $lastLeakTime;
public function __construct($capacity, $rate) {
$this->capacity = $capacity;
$this->rate = $rate;
$this->lastLeakTime = time();
}
public function allowRequest() {
$now = time();
$elapsed = $now - $this->lastLeakTime;
$this->water = max(0, $this->water - $elapsed * $this->rate);
$this->lastLeakTime = $now;
if ($this->water < $this->capacity) {
$this->water++;
return true;
} else {
return false;
}
}
}
這些算法各有優缺點,應根據具體的場景選擇合适的算法來進行 IP 限流。當然,在實際的業務下,接口限流不單隻針對ip來做,還有其它的限流條件,是以,往往我們會定義抽象出不同的限流場景,例如我下面的:
ratelimiter
- 業務碼和http狀态碼
在RESTful API設計中,HTTP狀态碼被廣泛應用來表示請求的處理結果。标準的HTTP狀态碼包含了一系列常見的處理結果,例如200表示請求成功,404表示未找到資源,500表示伺服器内部錯誤等。這樣設計的好處是用戶端和伺服器可以通過HTTP狀态碼快速準确地擷取請求的處理結果,進而進行相應的處理。
除了HTTP狀态碼,定義更明确的業務碼也是非常重要的。業務碼是指在處理業務邏輯時,根據不同的處理結果定義的辨別碼。通過定義明确的業務碼,可以更準确地表示請求的處理結果,并且可以為用戶端提供更具體的錯誤資訊,幫助用戶端更好地處理請求的結果。
在定義業務碼時,可以根據業務邏輯的不同,将業務碼進行分類,例如成功處理、參數錯誤、權限錯誤、系統錯誤等對于每一種業務碼,可以提供對應的錯誤資訊,例如錯誤碼、錯誤資訊等。在API設計中,建議将錯誤資訊封裝成一個标準的響應格式傳回給用戶端。
- 常用http狀态碼
成功處理:
200:請求成功
201:建立成功
202:請求已接收,但未處理完成
參數錯誤:
400:請求的參數不正确
401:缺少必要參數
402:參數格式不正确
403:參數值不在指定範圍内
權限錯誤:
401:沒有權限通路請求的資源
402:需要進行身份驗證
403:已經進行了身份驗證,但是沒有權限進行該操作
404:請求的資源不存在
系統錯誤:
500:伺服器内部錯誤
501:請求的功能尚未實作
502:網關錯誤
503:伺服器暫時不可用
- 業務碼統一設定參考
20000:成功處理
20001:建立/更新資源成功
20002:删除資源成功
20003:擷取資源成功
20004:操作已執行,但沒有任何資料變化
40000:請求參數錯誤
40001:缺少必要參數
40002:參數類型不正确
40003:參數值不在合法範圍内
40004:參數格式不正确
40100:權限錯誤
40101:使用者未登入
40102:使用者沒有足夠的權限
40103:使用者認證資訊無效
50000:系統錯誤
50001:系統繁忙,請稍後重試
50002:系統錯誤,請聯系管理者
50003:資料存儲異常
請注意,這僅是一個示例,業務碼分類定義可以根據實際情況進行調整和擴充(例如:http狀态碼+業務場景【訂單】+場景動作【生成訂單】+動作狀态【成功】)。在應用程式中,這些業務碼可以用來更精确地描述錯誤或狀态,而不是僅使用HTTP狀态碼。同時,HTTP狀态碼可以用來表示請求本身的處理結果,如成功、參數錯誤、權限錯誤、伺服器錯誤等。
在這裡推薦我參考的一篇文章,相信大家會更明白:史上最全的接口(API)設計規範,值得收藏 - 環信
好了,有點啰嗦,今天的就分享到這裡了,下期我将繼續奉獻接口設計,計劃如下:
- 将本期接口限流設計作為單獨的一篇推文來說明
- 用自定義錯誤異常來統一業務碼