四步驟設計 Web API 規格文件 | 實踐 RESTful API

RESTful API 設計流程,共四步驟:

  1. 設計資源的 URL 路徑
  2. 將 API 操作對應到 HTTP Method
  3. 設定 Status Code
  4. 展開 Request 與 Response

提供設計範例一步步完成。

▍第一步:設計資源的 URL 路徑

  1. 定義資源
  2. 遵循命名規則,設計URL
    RESTful API Endpoint Naming Strategies: Best Practices
    • API 的 URI 請勿使用駝峰命名法 ,應始終為小寫。
    • 使用連字符 " - " 而不是底線分隔多個單字。
    • 不要縮寫,URI 中的單字要使用完整且有意義的形式。
    • 如果API 接受變數,則應使用駝峰式命名法表示它,並括在一組大括號 { } 內。
      e.g., https://localhost:2024/api/co-agents?emailAdress={emailAdress}&firstName={firstName}
    • 使用正斜線表示層次關係。
    • 資源名稱使用名詞,而不是動詞。
    • 應該使用複數名詞來表示集合。

下方以包含多種契約的線上簽名系統為例子。

資源名稱資源路徑
E-signing/esigning
E-signing's Appointment To Sell Letters/esigning/appointment-to-sell-letters
E-signing's Co-broke Letters/esigning/co-broke-letters

▍第二步:將 API 操作對應到 HTTP Method

主要的 HTTP Method:

HTTP Method說明
 
安全性
(註1)
冪等性
(註2)
說明
 
GET回覆請求資料
Read
SafeIdempotent具有冪等性,相同的查詢請求,不管查詢幾次都應該得到相同的結果。
POST創建資源或其他應用
Create 
Unsafe 不具冪等性,相同的建立請求,每次執行,都會建立一個新的資源。
PUT取代現有資源
Update 
UnsafeIdempotent具冪等性,每次執行更新請求,皆使用同樣的資料覆蓋指定的資源,所以結果不變。
PATCH更新資源
Update 
Unsafe 不具冪等性,但可以以冪等的方式發出請求。
DELETE刪除資源
Delete 
UnsafeIdempotent具冪等性,刪除資源後,無論重複多少次刪除,也不會改變結果。

註1:

關於安全性,RFC 7231 - Hypertext Transfer Protocol (HTTP/1.1) 中關於Safe Methods的說明:

Request methods are considered "safe" if their defined semantics are essentially read-only; i.e., the client does not request, and does not expect, any state change on the origin server as a result of applying a safe method to a target resource.  Likewise, reasonable use of a safe method is not expected to cause any harm, loss of property, or unusual burden on the origin server.

簡單的說,Safe method 不應該造成資料的改變。

也就是不應該用 Safe method 去改資料。

而這也代表著 Safe method 只提供讀取資料或讀取狀態行為(不改資料,換句話說就是只能讀取)。
( 參考 HTTP 的 Safe method 還有 Idempotent method

註2:

Idempotent 冪等性:是HTTP方法的屬性,表示不管執行多少次操作請求,都將產生相同的結果。
( 參考 Idempotency Key:原理與實測 )

 

下方以同業合作契約 Co-broke Letters舉例。

用 HTTP Method 做同業合作契約的 CRUD:

Create → 用 POST
Read → 用 GET
Update → 用 PATCH or PUT
Delete → 用 DELETE

資源路徑
URL
操作名稱
Action Name
HTTP Method說明請求參數
Request
回應
Response
/esigning/co-broke-lettersCreateCobrokeLetterPOST建立同業合作線上簽名契約createRequest找到這筆資料的URL, cobrokeLetterId, response

201 Created 已建立
400 Bad Request 錯誤的要求
/esigning/co-broke-letters/
{cobrokeLetterId}
UpdateCobrokeLetterPATCH更新同業合作契約cobrokeLetterId、updateRequest204 NoContent 無內容 (成功不回傳內容)
400 Bad Request 錯誤的要求
404 Not Found 找不到
/esigning/co-broke-letters/
{cobrokeLetterId}
GetCobrokeLetterGET取得一份同業合作契約cobrokeLetterIdcobrokeLetter

200 OK 成功 (回傳內容)
404 Not Found 找不到
/esigning/co-broke-letters/
conditions?keyWord={keyWord}&
signStatus={signStatus}
SearchCobrokeLettersGET依請求參數搜尋 e.g.,關鍵字、簽名狀態…Query String:
keyWord、signStatus
cobrokeLetter[]

200 OK 成功 (回傳內容)
/esigning/co-broke-letters/
{cobrokeLetterId}
DeleteCobrokeLetterDELETE刪除同業合作契約cobrokeLetterId204 NoContent 無內容 (成功不回傳內容)
404 Not Found 找不到

▍第三步:設定 Status Code

上方同業合作契約表格範例已經放入 Status Code。

常見的 Status Code:

  • 2XX(成功類)OK請求被成功接收並處理完成。
    • 200:OK。請求成功並回應。
    • 201:Created。請求建立新增資源成功。
    • 204:NoContent。請求成功,沒有回覆任何內容,通常用於刪除的內容。
  • 3XX(重新定向/轉址)Redirection需要進一步行動才能完成請求。
    • 301:成功轉址
  • 4 XX(客戶端錯誤)Client Error該請求包含錯誤語法或無法滿足。
    • 400:Bad Request。錯誤的要求。
    • 403:Forbidden。伺服器拒絕請求,權限不夠、授權不夠。
    • 404:Not Found。請求的URL或URI不存在,打錯網址,或資源不存在。
    • 415:Unsupported Media Type。不支援的媒體類型,用了不符合的content-type
      e.g.,只收Json格式,不收html。所以收到415。
      1. 可以先懷疑:Header 裡面的 content-type 帶入錯了 
      2. 或是自己寫的 Header和 Payload 的 content-type 不吻合
  • 5XX(伺服器錯誤)Server Error:伺服器因為發生錯誤或例外狀況(Exception) 而無法完成請求 (Request)
    • 500:Internal Sever Error。內部伺服器錯誤,無法處理請求。

▍第四步:展開 Request 與 Response

完成 API 規格總表後,接著將每一列的操作,展開細節。

首先,將總表的第一列操作 (如下表) 的細節寫在下方。

資源路徑
URL
操作名稱
Action Name
HTTP Method說明請求參數
Request
回應
Response
/esigning/co-broke-lettersCreateCobrokeLetterPOST建立同業合作線上簽名契約createRequest找到這筆資料的URL, cobrokeLetterId, response

201 Created 已建立
400 Bad Request 錯誤的要求

重點是讓看文件的人好懂,能準確寫出需要的 API 功能。

下方示範的是我目前的寫法與習慣,給大家一個參考。

再找出自己能有效率製作、並與團隊能有效率溝通的文件格式。

POST  ~/esigning/co-broke-letters

功能:建立同業合作契約資料

Action Name:CreateCobrokeLetter

Request 請求參數:createRequest

Body 參數名稱是否必填資料類型說明
CreateDateVDateTime建立契據日期
CreateUserIdVstring建立契據人編號
CommunityName string社區名字
CoAgentDataIdVint同業資料Id

請求範例:格式 json

POST https://localhost:40088/api/esign/cobroke-letters
Content-Type: application/json

{
  "CreateDate": "2023-10-17",
  "CreateUserId": "39513",
  "CommunityName": "Tiffany Kiara",
  "CoAgentDataId": "5"
}

Response 回傳參數:

參數名稱資料類型說明
Idint同業合作契約Id

回傳情境:

  • 成功:
    • 201 Created
      回傳可以得到此筆資料的URL(往下讀會找到GET的URL⭐)、此筆記錄建立後得到的同業合作契約Id
{
	"Url":".../esigning/co-broke-letters/{cobrokeLetterId}",
	"Id":"1"
}
  • 失敗:
    • 500 Internal Sever Error
      API本人掛了 > 回傳錯誤訊息
       
    • 400 Bad Request
      errorMessage:格式不符合
{
  "CreateUserId": "CreateUserId must Has 6 characters.",
  "Detail": "CreateUserId must Has 6 characters.",
  "Instance": "api/esign/cobroke-letters"
}

完成一個了!接下來以此類推。

讓我繼續推下去~

PATCH  ~/esigning/co-broke-letters/{id}

功能:更新同業合作契約資料

Action Name:UpdateCobrokeLetter

Request 請求參數:idupdateRequest

Body 參數名稱是否必填資料類型說明
IdVint同業合作契約Id
CreateDateVDateTime建立契據日期
CreateUserIdVstring建立契據人編號
CommunityName string社區名字
CoAgentDataIdVint同業資料Id

請求範例:格式 json

PATCH https://localhost:40088/api/esign/cobroke-letters/1
Content-Type: application/json

{
  "CreateDate": "2023-10-17",
  "CreateUserId": "395135",
  "CommunityName": "Mont Kiara",
  "CoAgentDataId": ""
}

Response 回傳參數:NoContent

回傳情境:

  • 成功:
    • 204 NoContent
      成功,不回傳內容。
  • 失敗:
    • 500 Internal Sever Error
      API本人掛了 > 回傳錯誤訊息
       
    • 400 Bad Request
      errorMessage:格式不符合
{
  "CreateUserId": "CoAgentDataId is required.",
  "Detail": "CoAgentDataId is required.",
  "Instance": "api/esign/cobroke-letters"
}

GET  ~/esigning/co-broke-letters/{id}

⭐這個URL就是前面POST建立契約時,201建立成功要回傳的URL。

功能:取得一份同業合作契約

Action Name:GetCobrokeLetter

Request 請求參數:id

Body 參數名稱是否必填資料類型說明
IdVint同業合作契約Id

請求範例:格式 json

GET https://localhost:40088/api/esign/cobroke-letters/1

Response 回傳參數:cobrokeLetter

回傳情境:

  • 成功:
    • 200 OK 成功
      回傳Id為1的cobrokeLetter資料內容。
{
  "Id":"1",
  "CreateDate": "2023-10-17",
  "CreateUserId": "395135",
  "CommunityName": "Tiffany Kiara",
  "CoAgentDataId": "5"
}
  • 失敗:
    • 404 Not Found 找不到

GET  ~/esigning/co-broke-letters/conditions?createUserId={createUserId}&
communityName={communityName}

功能:依請求參數搜尋

Action Name:SearchCobrokeLetters

Request 請求參數:Query String

Body 參數名稱是否必填資料類型說明
CreateUserId string建立契據人編號
CommunityName string社區名字

請求範例:格式 json

GET https://localhost:40088/api/esign/cobroke-letters/conditions?createUserId={395135}&communityName={Kiara}
  • 如果 Query String 都沒放內容,我會讓系統篩選出全部的同業合作契約。
  • 名稱類的參數 e.g.,communityName,我會讓系統模糊搜尋。

Response 回傳參數:cobrokeLetter[]

回傳情境:

  • 成功:
    • 200 OK
      回傳cobrokeLetter[] 同業合作契約的資料集合。

DELETE~/esigning/co-broke-letters/{id}

功能:刪除同業合作契約

Action Name:DeleteCobrokeLetter

Request 請求參數:id

Body 參數名稱是否必填資料類型說明
IdVint同業合作契約Id

請求範例:格式 json

DELETE https://localhost:40088/api/esign/cobroke-letters/1

Response 回傳參數:NoContent

回傳情境:

  • 成功:
    • 204 NoContent
      成功,不回傳內容。
  • 失敗:
    • 404 Not Found 找不到

 

將 Request 和 Response 展開細節就完成了API設計了!

之後會有一篇說明如何驗證請求 Validate Request,當 Request不符合規則時就會顯示錯誤訊息提醒使用者。

以上步驟就完成設計 Web API 規格文件了😊


再看一個網路上買書的例子,使用購物車 Cart 與書 Book。

參考《Web API設計原則: API與微服務傳遞價值之道》

 

資源路徑
URL
操作名稱
Action Name
HTTP Method說明請求參數
Request
回應
Response
/books/conditions?
categoryId={categoryId}&
releaseDate={releaseDate}
searchBooksGet依請求參數搜尋 e.g.,分類、出版日期…Query String
categoryIdreleaseDate
Book[]

200
/books/{bookId}viewBookGet檢視一本書的詳情bookIdBook

200, 404
/carts/{cartId}/itemsaddBookToCartPost將書籍加入用戶購物車cartIdCart

201, 400
/carts/{cartId}/items/{cartItemId}removeBookFromCartDelete將書籍自用戶的購物車移除cartItemIdCart

204, 404
/carts/{cartId}clearCartDelete將用戶購物車的全部品項移除cartIdCart

204, 404
/carts/{cartId}viewCartGet檢視當前購物車的品項cartIdCart

200, 404

參考資料

James Higginbotham ─《Web API設計原則: API與微服務傳遞價值之道》一書

RESTful API Endpoint Naming Strategies: Best Practices

謝謝觀看,此為新手的學習筆記整理,若有錯誤,煩請指正🙏