閒談軟體設計:休息時間

更新於 發佈於 閱讀時間約 17 分鐘
看到標題是否覺得很奇怪?休息時間,如果改成 REST time 有沒有一種突然豁然開朗的感覺呢?每次看到 REST 就讓我想起以前念研究所和老師一起想論文題目時,曾提過國外常會玩這種文字遊戲,像是將 Representational State Transfer 變成一個很簡單的單字 REST,但我的東西不管怎麼想,卻想不出什麼有趣的東西 Orz

前言

會想寫 REST,是過去在第一間公司工作時,曾聽到當時的架構師對著團隊說:『不要以為用了 JAX-RS 就是 Restful Web Services,你們 URL 命名根本就亂七八糟』,剛進去的時候,確實只是簡單地看了一下 Wikipedia 的說明,就開始用 JAX-RS 的 annotation 開發後台的 API,也沒多想什麼,所以後來自己在訂 Restful 的 API 總是特別小心,像是複數型的名詞、避免動詞、只使用 HTTP method (GET、POST、PUT 和 DELETE) 等等。

後來換工作,討論 API 時,當只有我特別這麼堅持時,總覺得好像是我比較龜毛似的,但 API 至少還是符合 Restful 風格,後來比較不是前線開發者後,等到 API 會議結束,看結果,幾乎所有的 API 都把動詞放在 URL 上了,曾經詢問了一下原因,但似乎沒引起什麼共鳴,後來想想算了,只要不對外宣稱這是 Restful API 其實也沒什麼不行。

半年後換工作,又從 iOS/Android 工程師變回後端工程師了,由於是我主導整個 API 的設計,在風格上沒什麼問題,但還是得和其他同事解說為什麼 API 是這樣命名的,但說的還是 Wikipedia 上的那一套,直到最近看完《建構微服務》後,才在思考要不要找出原始的論文來讀讀呢?

趁著中秋連假,把《Architectural Styles and the Design of Network-based Software Architectures》抓下來看,老實說,有點震撼到,像是使用 HTTP method 這件事其實根本就沒出現在論文中。

論文導讀

既然看都看了,就簡單和大家分享一下看完這篇論文的心得 (若我理解有誤歡迎更正) 吧!這篇論文用 Chapter 1 整章的篇幅,在說明怎麼描述一個軟體架構,過去在畫架構圖時,總是畫上幾個方塊和線條,然後就開始跟其他人說明,嚴謹一點就用 UML 的部署圖描述,從來也沒去想過要怎麼描述一個軟體架構。

A software architecture is defined by a configuration of architectural elements — components, connectors, and data — constrained in their relationships in order to achieve a desired set of architectural properties.

如上所述,論文倒是很清楚地用三個元素來描述一個軟體架構:

  • A component is an abstract unit of software instructions and internal state that provides a transformation of data via its interface. (也就是平常架構圖上的那些方塊)
  • A connector is an abstract mechanism that mediates communication, coordination, or cooperation among components. (圖上連接方塊的線)
  • A datum is an element of information that is transferred from a component, or received by a component, via a connector. (在元件之間傳遞的資料)

聽完可能有人覺得,啊~ 不就廢話,我平常就這樣描述軟體架構啊,但這一章花了不少篇幅探討這三個元素是否足夠,這倒是我過去不曾想過的。最後,一個軟體架構風格則是一組限制,限制上述元素的角色或功能與關係,任何架構只要符合這些限制就能稱作是某種軟體架構風格,而 REST 是由六個限制所組成的軟體架構風格。

An architectural style is a coordinated set of architectural constraints that restricts the roles/features of architectural elements and the allowed relationships among those elements within any architecture that conforms to that style.

Chapter 2 則是探討網路應用程式 (network-based application) 的架構,我覺得很有價值的是條列了七種網路應用程式架構的 properties:Performance、Scalability、Simplicity、Modifiability、Visibility、Portability 和 Reliability,也就是一般軟體工程書中稱之為 non-functional requirement attributes,並說明為什麼選這七種做為評估架構的依據。並在 Chapter 3 中使用這些 properties 評估了共 20 種 network-based application architectures,合計 7 類的軟體架構風格,值得一看。

Chapter 4 討論設計 Web Architecture 的需求與會遭遇的問題,在繼續之前,可能要想一下為什麼是 WWW 架構,不就是網頁應用程式嗎?如果仔細看 Wikipedia 上對 WWW 的解釋

The World Wide Web (WWW) is an information space where documents and other web resources are identified by URLs, interlinked by hypertext links, and can be accessed via the Internet.

只要任何能以超連結串起來的資訊空間就能稱作是 WWW,沒有一定要用 HTTP 協定,也沒有說一定是 HTML,因此,論文提出一種分散式超媒體系統的軟體架構風格能滿足三個假設,這就不細討論三個假設,這是大多數論文的必備要素,說完一堆人就睡著了,基本上,那個方案就是 REST。

Chapter 5 描述 REST 是如何從無開始對 WWW 加入六種限制所形成的一種軟體架構風格。因為是限制,所有描述會變成:

  • 必須是 Client-Server,用意為 separation of concerns
  • 必須是 Stateless,request 與 request 之間是獨立的,server 不需要記住狀態以處理下個 request
  • 必須是可 Cache 的,但那些內容可以 cache 是由 client 與 server 協調的
  • 必須是 Uniform Interface,這一點很重要
  • 必須是 Layered System,基本上就是用 layer 隱藏實作的細節,client 只需要知道 uniform interface 即可
  • 可以是 Code-On-Demand,這是 optional 的限制,所以我說『可以是』,基本上是可以用這個方式讓 client 用取得程式碼的方式擴充功能。

為了達成 uniform interface,論文對介面設計提出四個限制:

  • identification of resources
  • manipulation of resources through representations
  • self-descriptive messages
  • hypermedia as the engine of application state

剛剛不是說軟體架構可以用 component、connector 和 data 定義嗎?那 REST 風格的軟體架構呢?有的。

首先看 data,所有可被命名的資訊都是 resource,並給予 identifier (滿足 identification of resources),根據 client 的能力與需求 (透過 control data 描述),給予不同的呈現方式 (representations),像是 JSON、XML 等,並透過呈現方式操作資源,而不是直接操作資源,(滿足 manipulation of resources through representations),由於實際的資源與呈現方式可以相同也可以不同,所以 resource identifier 也可以視為資源與呈現方式 mapping (轉換) 的一種識別。resource 可以透過 representation metadata 與 resource metadata 加以描述,讓 client 可以自行決定如何解讀取得的 resources (滿足 self-descriptive messages)。

data 以 connector (client, server, cache, resolver, tunnel) 提供一致的方式存取,透過 identifier 存取不同的 resources (representation) 及轉換,完成目的 (滿足 hypermedia as the engine of application state)。component (origin server, gateway, proxy, user agent) 透過 connector 溝通,實際上,資源源如何儲存和怎麼處理則是隱藏在 component 中,可以直接提供 representation 或再透過 connector 與其他 component 請求 representation,但介面是一致的。

Chapter 6 則是經驗與評估,描述作者在參與 URI 與 HTTP/1.1 制定時,如何用 REST 找出問題並確定這些擴充沒有違反限制,個人認為有幾個重點:

  • 資源的 identifier 應該鮮少變動
  • 資源不是一個儲存物件,而是一種抽象的應對,一個 identifier 對應到一種抽象應對的實作
  • 辨識使用者的資訊不應包含在 representation 裡的 URI 中
  • HTTP 無法完整描述所有 REST 的語意
  • 使用 HTTP 本身的 header 描述 representation 及提供 metadata 與 control data
  • REST 不是 HTTP,HTTP 也不是 RPC
  • 對 resource 的操作,client 和 server 要有一致的認知

Chapter 6 之後便是結論了。

回到 RESTful API

為什麼要花這麼長的篇幅介紹論文的內容呢?主要是,在設計 Restful API 時,有時候會有些猶豫,像是:

  • token 或 session ID 應該放在 query parameter 還是 HTTP header 中?這有點 tricky,雖說 URI 上不該帶有使用者資訊,但若 resource representation 中的 URI 不夾帶使用者資訊,而是在發送當下才夾帶,算是有違反嗎?不過,就安全性來說,query parameter 有時候會在 log 時被記錄下來,所以算是盡量避免的另一個原因。
  • API 版號應該出現在 URI 中還是 HTTP header 中?以 REST API Versioning 的描述兩種皆可,但看完論文後哪種較好呢?

當初在思考這些問題時,在網路上爬了很多文章和討論,但其實不管哪種作法都有,也有各自的擁護者,但等看完這論文後,我覺得我當初的選擇 (都用 HTTP header) 是比較接近論文的原意 (URI 不夾帶使用者資訊,並盡可能保持不變動,由 client 與 server 透過 control data 協調 representation),而自己對 URL 的命名,除了不太使用 controller 之外,也符合一般大多數的最佳實務

至於剛開始說的 URL 中該不該有動詞?以最佳實務來說,可以,用來代表概念性的 controller 資源,但不會用動詞描述 CRUD,因為 HTTP method 本身已經有這個語意了,個人認為 controller 這個概念性資源是對 HTTP 方法的一種擴充 (見論文的 Chapter 6),畢竟,完全用狀態與名詞描述行為確實不容易,以登入與登出這兩個行為來說,若觀察大多數的 API 會發現幾乎都是直接用 login/logout 這兩個動詞放在 URI 上,我當時也是想了很久,才以登入前和登入後狀態的差異,找到一個我覺得合適的名詞,取代用動詞放在 URI 上的做法。但之後是不是堅持不使用 controller 呢?我覺得還是視語意,挑選合適的方法比較重要。最重要的是,是否違反上述的限制,只要不違反就能稱作是 Restful API 了

相較於 SOAP、RPC 或更早的 RMI,Restful API 幾乎是目前最熱門的 API 設計風格,若想知道怎樣朝 Restful API 風格邁進,可以參考 Martin Fowler 在《Richardson Maturity Model — steps toward the glory of REST》文中所提的方式,逐步演進成為 Restful API:

  • level 0 就是什麼規範都沒有,或是把 HTTP 當成 RPC 使用的階段
  • level 1 定義 resource,讓系統能以 resource 描述狀態
  • level 2 善用 HTTP 動詞 (方法:GET、POST、PUT 和 DELETE)
  • level 3 能以超媒體的方式控制流程,或是從 response 中的資訊可以知道接下來可以做什麼

能做到 level 2 我就覺得很好了,想要做到漂亮的 level 3,若使用 XML 作為 resource 的 representation,那已經有一些標準可以遵循,但若是使用 JSON,目前我所知的有 Hypertext Application LanguageJSON API 兩種描述方式,哪種較好,我覺得只要能滿足 REST 的限制,可以依照自己的喜好使用。

結語

所以大家的 API 能到 level 幾呢?其實到 level 3 也不代表 API 設計的好,API 的設計牽涉到很多層面,就如同論文 Chapter 6 中也探討很多要考慮的因素,希望這篇長文,可以讓大家在設計 Restful API 時有更多的想法湧出。最後覆上一個有趣的文章讓大家思考一下,您平常是如何看待 API 的。




留言
avatar-img
留言分享你的想法!
Spirit-avatar-img
發文者
2023/10/15
閒談軟體設計:Developer eXperience提及了這篇文章,趕快過去看看吧!
Spirit-avatar-img
發文者
2023/07/28
閒談軟體設計:Client Server提及了這篇文章,趕快過去看看吧!
avatar-img
Spirit的沙龍
53會員
104內容數
這是從 Medium 開始的一個專題,主要是想用輕鬆閒談的方式,分享這幾年軟體開發的心得,原本比較侷限於軟體架構,但這幾年的文章不僅限於架構,也聊不少流程相關的心得,所以趁換平台,順勢換成閒談軟體設計。
Spirit的沙龍的其他內容
2024/03/23
這篇文章探討了在軟體開發中的技術債可能來自哪些原因,以及如何自動化偵測與修復技術債。作者透過分享不同情境下的技術債選擇,提供了對於技術債的思考與建議,針對開發人員在需要做出無奈的技術決策時,提供了一些建議。此外,還提供了一些在做出技術決策時的方法,如保留抽象層和避免vendor lock-in。
Thumbnail
2024/03/23
這篇文章探討了在軟體開發中的技術債可能來自哪些原因,以及如何自動化偵測與修復技術債。作者透過分享不同情境下的技術債選擇,提供了對於技術債的思考與建議,針對開發人員在需要做出無奈的技術決策時,提供了一些建議。此外,還提供了一些在做出技術決策時的方法,如保留抽象層和避免vendor lock-in。
Thumbnail
2024/03/09
今天來聊個最近很夯的主題 DDD,但不是 DDD 的本尊 Domain Driven Design,而是無所不在的 Database Driven Design,Database Driven Design 不是不好,只是你的模型容易變成貧血模型,邏輯都集中在 service 層等等。
Thumbnail
2024/03/09
今天來聊個最近很夯的主題 DDD,但不是 DDD 的本尊 Domain Driven Design,而是無所不在的 Database Driven Design,Database Driven Design 不是不好,只是你的模型容易變成貧血模型,邏輯都集中在 service 層等等。
Thumbnail
2024/03/02
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
Thumbnail
2024/03/02
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
Thumbnail
看更多
你可能也想看
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
REST(REpresentational State Transfer)是一種WWW的Web架構,常被使用於JSON或XML的Web服務,而符合REST原則的系統就稱為RESTful。REST API(RESTful API)是一種符合REST的API應用。 方法說明 GET:向伺服器取得
Thumbnail
REST(REpresentational State Transfer)是一種WWW的Web架構,常被使用於JSON或XML的Web服務,而符合REST原則的系統就稱為RESTful。REST API(RESTful API)是一種符合REST的API應用。 方法說明 GET:向伺服器取得
Thumbnail
大家好!隨著微服務和前後端分離的架構日益普及,RESTful API 已經成為 Web 開發的核心。透過 Gin,我們可以輕鬆地設計和實現高效能的 API。今天,我們就來探討如何在 Gin 中設計優雅且實用的RESTful API
Thumbnail
大家好!隨著微服務和前後端分離的架構日益普及,RESTful API 已經成為 Web 開發的核心。透過 Gin,我們可以輕鬆地設計和實現高效能的 API。今天,我們就來探討如何在 Gin 中設計優雅且實用的RESTful API
Thumbnail
每次看到 REST 就讓我想起以前念研究所和老師一起想論文題目時,曾提過國外常會玩這種文字遊戲,像是將 Representational State Transfer 變成一個很簡單的單字 REST,但我的東西不管怎麼想,卻想不出什麼有趣的東西 Orz
Thumbnail
每次看到 REST 就讓我想起以前念研究所和老師一起想論文題目時,曾提過國外常會玩這種文字遊戲,像是將 Representational State Transfer 變成一個很簡單的單字 REST,但我的東西不管怎麼想,卻想不出什麼有趣的東西 Orz
Thumbnail
設計時的考量主要有:(1) App 是 Internet App,在考量 UI 體驗和網路頻寬的消耗,多數資料需以某種形式儲存部分資料在行動裝置上;(2) 因此會需要同步伺服器端和行動裝置端之間資料狀態;(3) 但行動裝置網路的穩定性不如一般網路可靠,要有足夠的自動化測試驗證正常的流程與異常的流程。
Thumbnail
設計時的考量主要有:(1) App 是 Internet App,在考量 UI 體驗和網路頻寬的消耗,多數資料需以某種形式儲存部分資料在行動裝置上;(2) 因此會需要同步伺服器端和行動裝置端之間資料狀態;(3) 但行動裝置網路的穩定性不如一般網路可靠,要有足夠的自動化測試驗證正常的流程與異常的流程。
Thumbnail
這裡會針 GraphQL 與 RESTful API 這兩者介面所需要做的事情來比較其應用的場景。
Thumbnail
這裡會針 GraphQL 與 RESTful API 這兩者介面所需要做的事情來比較其應用的場景。
Thumbnail
你想像有一個黑盒子,它會跟你說輸入一個input,而會得出什麼Output,而你要把這個Input傳遞給黑盒子是透過http的方式
Thumbnail
你想像有一個黑盒子,它會跟你說輸入一個input,而會得出什麼Output,而你要把這個Input傳遞給黑盒子是透過http的方式
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News