Link Search Menu Expand Document

本章內容與「Part 2-3 Web API 設計實作」有些重複,可以先去看 3. 建立 API 伺服器

Web APIs 設計

學習目的

  • 目標讀者: 應用程式初學開發者,特別是 Application Developer
  • 目的: 了解 Web APIs 該如何設計,有哪些設計上的考量。讓前後端合作起來能夠更順利。
    • 後端工程師可以根據 Models 有哪些資料,就設計和實作出好用的 APIs
    • 前端工程師(Mobile)可以指引後端設計師提供更好用的 API,甚至開出規格,碰到新的 APIs 也可以很快了解。
  • 設計 Web APIs 的用途包括:
    • 提供給手機 iOS, Android 應用程式的 Web API
    • 提供 JavaScript MVC application 的 Web API
    • 建立 API 平台,開放 APIs 給第三方開發者使用

REST 基礎

什麼是 REST?

  • 為何要設計 Web APIs? 為什麼採用 REST?
  • 目前的主流風格(Style): REST
  • REST 滿足以下條件
    • Client-Server 使用者端/伺服器端的架構
    • Cacheable 伺服器端的 Response 可以支援快取
    • Layered 伺服器端可以有分層的架構,例如 Reserve-proxy Server 或 Load balancer
    • Stateless 不同的API呼叫之間是沒有前後文關係的,具有無狀態性。因為網路的不穩定性 1. 每次的 Request 必須帶有足夠的資訊讓伺服器端可以處理,例如帶有 token 可以讓伺服器識別是哪一個用戶 2. 每一個 Request 操作都必須是 atomic 的,不應該用兩個 requests 去完成一個應該一起完成或一起失敗的動作,就像 DB 的 transaction 一樣。
    • Uniform interface 一致的 API 介面:由 Resource 名詞 (即 URI) 搭配有限的 Verb 動作 (即 HTTP Method),加上不同的 Representation 格式 (即 Content Types) 所組成
  • Richardson Maturity Model 將 REST 分成成熟度 Level 0、Level 1 - Resources、Level 2 - HTTP Verbs、Level 3 - Hypermedia Controls。常見的 API REST 成熟度會在 Level 2~3 之間。

REST API 介面基礎

簡明RESTful API設計要點

用 URL 當作 Resource,搭配不同 HTTP Verb GET (讀取) POST (新增) PATCH (更新) PUT (取代) DELETE (刪除)
/events list events create an event bulk update N/A bulk delete
/events/123 event N/A if exists update event, else error if exists update event, else create it delete
  • 根據 REST 風格,一個 HTTP Request 由「HTTP Method 動詞 + URL 名詞 + Content Types 格式」所組成
  • 不過實務上也可能混用 POST 的 [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call) 風格,雖然不符合嚴格的 REST 定義,但也算常見的設計。例如
    • POST /topics/bulk_update
    • POST /topics/1/subscribePOST /topics/1/unsubscribe

URL

  • Rails 偏好用複數當作名詞
  • 關聯關係喜歡用 nested resource
    • /events/{event_id}/attendees
  • 發揮想像力,API 幾乎都可以具象出一個 Resource 名詞來進行操作,例如:
    • POST /groups/1/add_user?user_id=2 變成 POST /memberships?group_id=1&user_id=2
    • POST /groups/1/remove_user?user_id=2 變成 DELETE /memberships/{membership_id}
  • GET 可設計搭配 ? query string 過濾和排序結果
    • GET /events?order=count&filter=public

Request Method

  • 操作的兩種性質:
    • Safe: 是否會修改到伺服器資料
    • Idempotent: 是否可以重傳(retry)
  • 常用 HTTP Methods 有:
    • GET 讀取
    • POST 新增或其他操作
    • DELETE 刪除
    • PATCH (部分)修改
    • PUT (整個)替換
SAFE?IDEMPOTENT?
GETYY
POSTNN
PATCHNN
PUTNY
DELETENY

HTTP Status Code

Response Format

  • JSON 目前最流行的格式
  • XML
  • Binary Format
    • 例如 http://msgpack.org/ 和 https://github.com/google/protobuf
  • 如何告訴 server 你希望的格式是什麼? 有兩種方式:
    • 放到 URL 裡面,例如 GET /events.json
    • 放到 Header 裡面 Accept: application/json
      • 這個格式的定義叫做 MIME

Request Format (Client 端用什麼格式送出資料?)

  • 四種常見的POST 提交數據方式
  • application/x-www-form-urlencoded (網頁表單預設,不能上傳檔案)
  • multipart/form-data (如果要包含上傳檔案的話,要用這個)
  • application/json (這不能上傳檔案)

Versioning

  • 相容性議題:API 一旦釋出上線,要改就不是這麼容易了,特別是搭配 Mobile App、Desktop App 等等,客戶端程式需要用戶安裝和手動升級。如果是 Web App 就沒關係。
  • 不是每一次改 API 都需要改這個版號,盡量將 API 可以做成向後相容,就不需要跳版本。例如:
    • 新增一個欄位屬性,是向後相容的
    • 修改屬性名稱、刪除欄位、修改格式等等,會造成不向後相容。client app 抓不到該欄位可能會當機。
  • 版號放 URL 還是 Header ?
    • 簡單來說,放 URL 即可,例如 /api/v1/events

Web API 文件撰寫

如何跟前端開發者合作?

  • 前端包括 iOS、Android、JavaScript 等等開發者
  • 先討論定義 API 的格式,然後先做出回傳假資料的 API 部署上去,這樣就可以分頭做事了
  • 鄉民: API格式應該由前端開,還是後端開?
    • 從後端開 API,會圍繞在 model 開出 CRUD 的 API,容易白做工、而且沒有針對需求做最佳化

如何寫文件

  • 寫文件很重要,這樣才能團隊協同開發
    • 這個 API 是要給其他開發者使用的! API friendly!
      • Request
        • HTTP method
        • HTTP content-type
          • 網頁表單送出是用 application/x-www-form-urlencoded 格式
          • Rails 也可以吃 application/json,可以正常解析成 params 變數
        • URL endpoint
        • parameters
          • type: string? number? array? hash?
          • required or optional
      • Response
        • HTTP code
        • format
        • example
  • 舉例: 拿活動資料
    • GET /api/v1/events
    • JSON Example: { "data": [....] }
  • 舉例: 新增活動資料
    • POST /api/v1/events
    • 參數:
      • name: string
      • status: string ( draft 或 published)
    • 成功: HTTP status 200
      • 範例: { "id": 123 }
    • 失敗: HTTP status 400
    • 範例: …

文件相關工具

透過一些工具的幫忙,我們可以標準化文件的撰寫,進一步產生漂亮的文件格式,甚至產生一些樣本程式碼協助開發和測試:

API 文件範例

Web API 測試

Web APIs 建議由伺服器端撰寫自動化測試,最有效率且能保證正確性。 不負責伺服器端的客戶端開發者,可以用以下方式進行人工測試:

  • Chrome extension: JSON formatter 或 JSON View
  • Chrome extension: advanced-rest-client 或 postman
  • brew install httpie
  • App Store 的 Paw $890 (高級 HTTP client)
  • Insomnia Free app
  • 怎麼將內網的 localhost 讓外部網路可以連線進來呢?

Web API 設計美學

設計技巧

  • HTTP request/response 的天性
    • Stateless: 每個 requests 之間沒有前後文關係,每個 request 都必須帶有完整的資訊讓 server 可以完成操作,例如: 需要帶 token 才能識別使用者
    • Atomic: 不應該用兩個 requests 去完成一個應該一起完成或一起失敗的動作,就像 DB 的 transaction 一樣。
    • 另外,和 function call 相比,API 設計的粒度比較大。因為每次的 request/response 成本比較高。
  • REST 風格 vs. RPC 風格
    • 以 REST 概念為主、RPC 為輔。操作和運算 operate 類型的可用 RPC 概念來設計,例如:
      • POST /calculate
      • POST /translate
      • POST /convert
  • JSON response 第一層用 hash,不要用 array
  • 如何處理分頁?
  • 如何處理空值 null,建議不要完全砍掉,請留 null。例如 { name: "Foo", display_name: null } 而不是 { name: "Foo" }
  • 可以設計支援 Partial response
    • GET /events?fields=id,name,created_at
  • 有些人設計愛用 header (REST 基本教義派會把 metadata 例如 version、token 都放 header 裡面),有些人則喜歡放在 URL 和 body 裡面,用起來比較方便。前者例如 https://developer.github.com/v3/
  • Idempotent Retry 特別是離線應用,搭配 UUID 作 PUT 操作
  • JSON 時間格式: 用 ISO 8601 標準
  • API Rate Limit,如果 API 會開放給外部使用者,通常會制定一個呼叫上限,例如 Github 是每小時 5000 requests。
  • 有沒有什麼設計標準?

API Design Guideline

除了REST 之外,還有什麼其他的設計風格?

  • 傳統 RPC:單一 URL endpoint,將方法呼叫和參數都包在 body 中
  • GraphQL Facebook 開源作品。第一眼看 GraphQL 覺得很強大,不過比起 REST 實作上還是複雜很多。如果用 REST 不覺得寫 ad-hoc query 管理上會痛的話,就沒有需要用 GraphQL 了。
  • Falcor Netflix 開源作品

補充: Webhook API

上述的 REST API 都是從客戶端去伺服器存取資料,但是有時候我們希望反過來,當事件發生的時候,從伺服器端主動推資料過來,這樣我們就可以更即時且更有效率的做資料串接應用。

有些網站就有提供 Webhook 的方式,提供你設定當某事件發生時,從網站 POST 資料到你指定的網址,例如: 你在 Github 專案的 Settings 裡面,可以找到 Webhooks 設定。這可以設定當有人 push code 或發 pull reuqest 時,Github 會將該事件資料 POST 到你指定的網址。

當然,這種方式不適用於移動裝置,因為你的手機並沒有固定的 IP 位址。

Web API 認證設計

登入 token 機制

  • why?
    • 安全性(亂數產生的強度比密碼高、也可以加上 expired 時間)
    • 獨立性(使用者改密碼可以不影響 token,除非手動產生過 token)
  • 使用者用帳號密碼登入後,拿到專用 auth_token,之後每個 request 都必須帶這個 auth_token 參數
  • POST /api/v1/login 帶參數帳號 email 和密碼 password,回傳 token
  • POST /api/v1/logout 帶參數 token,作用是重設一個新 token

如何支援 facebook 登入?

  • 在手機端會自行實作 facebook 登入 (手機跟 Web 共用 facebook app ID),拿到 access_token
  • 需要用一樣的 facebook app ID,這樣 fb user id 才會一樣
  • 用手機端的 facebook access_token 進行登入 POST /api/v1/login
  • 用手機傳來的 access_token 跟 facebook 再確認一次,沒問題再回傳 user_token

其他安全性議題

  • 使用 HTTPS
  • 用 HMAC 簽章 request

API Provider 和 Client APP 認證常見手法

  • API provider 跟 API client 同一家,是 in-house 開發
    • 使用者透過 輸入帳號密碼 即可拿到 token
  • API provider 跟 API client 不同家,是開放 API 平台給別人用
    • 如果 API client app 就是用戶自己,可以直接在網頁上提供每個使用者 access_token,這樣最簡單
    • 如果 API client 是要提供給下游用戶使用:可以提供單一的 App 專用 API Key,或是提供註冊多個 application 拿到 app id,然後使用 OAuth 機制進行授權

Web APIs 參考資料和 Open Data 資料集

參考資料

Open Data 資料集

Open Data 開放資料是近年來的趨勢,政府不應該浪費預算在開發應用程式,而是只要將原始資料整理釋出,使用程式可以讀取的格式(例如XML或JSON),不應該有授權限制也不索取費用。由民間進行應用程式開發:


Copyright © 2010-2022 Wen-Tien Chang All Rights Reserved.