閒談軟體設計:休息時間

閱讀時間約 16 分鐘
看到標題是否覺得很奇怪?休息時間,如果改成 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 的。
為什麼會看到廣告
52會員
102內容數
這是從 Medium 開始的一個專題,主要是想用輕鬆閒談的方式,分享這幾年軟體開發的心得,原本比較侷限於軟體架構,但這幾年的文章不僅限於架構,也聊不少流程相關的心得,所以趁換平台,順勢換成閒談軟體設計。
留言0
查看全部
發表第一個留言支持創作者!
Spirit的沙龍 的其他內容
閒談軟體設計:例外處理
閱讀時間約 11 分鐘
閒談軟體設計:Plug-in
閱讀時間約 14 分鐘
閒談軟體設計:Repository
閱讀時間約 13 分鐘
閒談軟體設計:Query Object
閱讀時間約 6 分鐘
你可能也想看
從我的孕期怪事談起,閒聊人生的軟弱時刻。最近身旁有幾位正在懷孕、或剛生產完的朋友,讓我想起自己在懷孕期間印象最深刻的三件「怪事」,其中又以第三件事最誇張。
Thumbnail
avatar
兩寶媽的生活滋味與成長
2022-11-06
閒談-社區大樓觀景第一排公設圖書室,我們都要試著跟鄰居友好相處不知道大家在買房之前是不是都會參考親朋好友的意見,或是上網看一些買房注意事項,有時候考慮了這塊就忘了那塊,考慮的那塊又忘了這塊.......
Thumbnail
avatar
紗綺創作小屋
2022-08-16
閒談塔西佗陷阱(Tacitus Trap)塔西佗陷阱(Tacitus Trap),一個得名於古羅馬歷史學家塔西佗的政治學理論,意指倘若公權力失去其公信力,無論如何發言或是處事,社會均將給予其負面評價。 當然信著恆信、不信者恆不信,這就是真實的人生。
Thumbnail
avatar
whitman
2022-01-01
【閒談】關於《花漾女子》的想法雜燴 關於片名   台灣片名《花漾女子》,原文片名《Promising Young Woman》,台灣譯名將時間定格在悲劇發生前,而原文片名則進一步帶我們看見另一個可能性結果
Thumbnail
avatar
矮行星
2021-03-29
閒談金融業法令遵循-(一)工作待遇與定位前幾年因為身體的關係,當了幾年的律師逃兵,當時開了之前的事務所以後,一時間也沒有特別想要做甚麼事情,所以就邊讀一點書、早晚運動一下,剛好聽到當年同梯朋友進去金融業工作,因此也抱著嘗(ㄊㄠˊ)試(ㄅㄧˋ)的心態,找了份銀行法令遵循的工作
Thumbnail
avatar
jay.C
2021-01-08
閒談 那天我問隊友,怎樣才算是一部小說呢?按字數計算嗎? 他說:「故事內起承轉合都有,就算」 所以我大膽地按著他的標準,將自己寫過的故事,粗略整理出一個明細。
Thumbnail
avatar
于小晨
2020-11-18
閒談纏論與李彪 纏中說禪,本名李彪,專欄筆名木子,其人是中國股市比較早期的操盤手,所以他比較熟悉a股的市場情況。他以“纏中說禪”為筆名,從2002年開始寫博客,直到2008年癌症病重停更,期間寫下了不少文章。而博客文章中最為著名的就是他的“教你炒股票”系列文章,他在這個系列裡講到的炒股理論和方法被粉絲稱為“纏論
avatar
釋深明
2020-07-30
閒談末代皇帝電影:婚姻篇婚姻是人生大事,對溥儀尤其如此,因為如果皇帝大婚,就代表溥儀可以脫離眾多便宜老媽的束縛而得以親政。 但詭異的是,這個可以讓他脫離便宜老媽掌控的婚姻,卻還是要由便宜老媽進行主導並且居中角力......
Thumbnail
avatar
金哲毅
2020-05-29
閒談末代皇帝電影:童年篇上次我提到:溥儀就是個死小孩。其實這不能全怪溥儀,而要怪詭異的宮廷教育及生活制度......
Thumbnail
avatar
金哲毅
2020-05-29
閒談末代皇帝電影:登基篇近期電影「末代皇帝」重新修復上映。 為了推坑這部經典之作,本人決定以溥儀本身的自傳《我的前半生》為主要基底,和大家談一些電影中礙於篇幅或是藝術改編,而不容易察覺或是沒有呈現的真實歷史。
Thumbnail
avatar
金哲毅
2020-05-29