做RESTful開放平臺,一方面其API變動越少,對API調(diào)用者越有利;另一方面,沒有人可以預(yù)測未來,系統(tǒng)在發(fā)展的過程中,不可避免的需要添加新的資源,或者修改現(xiàn)有資源。
因此,改動升級必不可少,但是,作為平臺開發(fā)者,你必須有覺悟:一旦你的API開放出去,有人開始用了,你就不能只管自己Happy了,你對平臺的任何改動都需要考慮對當前用戶的影響。
因此,做開放平臺,你從第一個API的設(shè)計就需要開始API的版本控制策略問題,API的版本控制策略就像是開放平臺和平臺用戶之間的長期協(xié)議,其設(shè)計的好壞將直接決定用戶是否使用該平臺,或者說用戶在使用之后是否會因為某次版本升級直接棄用該平臺。
API的版本控制策略通常有3種模式:
第一種:The Knot
無版本,即平臺的API永遠只有一個版本,所有的用戶都必須使用最新的API,任何API的修改都會影響到平臺所有的用戶。甚至平臺的整個生態(tài)系統(tǒng)。 目前我是沒有見過哪家公司能夠這么硬氣的
第二種:Point-to-Point
點對點,即平臺的API版本自帶版本號,用戶根據(jù)自己的需求選擇使用對應(yīng)的API,需要使用新的API特性,用戶必須自己升級。目前也是我司采用的一個方法。我會著重來講這種方法下的接口版本控制。
第三種:Compatible Versioning
兼容性版本控制,和The Knot一樣,平臺只有一個版本,但是最新版本需要兼容以前版本的API行為。
現(xiàn)實問題
從用戶來講:我不想升級,道理很簡單,浪費精力。
產(chǎn)品:一起以用戶為中心
從這個點來說,第一種和第三種就不能搞定了。第三種為什么不能應(yīng)對這種需求呢,其實我們實際開發(fā)過程中就難免不了出現(xiàn)新舊版本不兼容的問題。舉個例子,用戶發(fā)帖,最開始產(chǎn)品考慮很多,發(fā)帖需要帶發(fā)帖定位,后面發(fā)現(xiàn)用戶都對這種很抵觸,那么后續(xù)發(fā)帖就把這個字段刪掉了。那么我們之前的接口需要傳入這個參數(shù),現(xiàn)在來說的話如果我服務(wù)端只有一個接口,那么后續(xù)的話我們必須冗余一個字段去兼容舊版本,長此以往,就會變成一個不可維護的接口了。
針對上面的論述,首先,API一定得有版本,否則升級對于用戶來說將是噩夢。其次,要做到Compatible Versioning有現(xiàn)實的限制,畢竟API升級時,不可避免的會出現(xiàn)無法兼容老版本的狀況,因此,版本控制需要結(jié)合第二種和第三種模式,即提供一個統(tǒng)一的兼容版本入口,同時對于不能兼容歷史版本的API保留歷史版本,支持用戶能夠調(diào)用到歷史版本的API。另外,對歷史版本的API支持一定要有時間和用戶限制,即老版API支持到一定時間就刪除,新用戶必須使用新版API,否則一個API有10個版本會讓平臺的維護,這是非常痛苦。
傳參操作
不同版本的app我們?nèi)绾蝸磉M行
URI vs Request Parameter vs Media Type
在RESTful API領(lǐng)域,關(guān)于如何做版本控制,目前業(yè)界比較主流的有3種做法:
(一) URI
即在URI中直接標記使用的是哪個版本,無版本號URI默認使用最新版本。如下:
http://xianlinbox/api/customers/1234
http://xianlinbox/api/v3.0/customers/1234
- 好處:
直接可以在URI中直觀的看到API版本,可以直接在瀏覽器的查看各個版本API的結(jié)果. - 壞處:
版本號在URI中破壞了REST的HATEOAS(hypermedia as the engine of application state)規(guī)則。版本號和資源之間并無直接關(guān)系。
(二) Request Parameter
即在每個請求后添加一個version參數(shù),表示請求的是哪個版本。如下:
http://server:port/api/customer/123?version=2
(三) Mdedia Type
即在HTTP請求的header中使用Media Type標記該請求想獲取的資源, 同樣的可以不設(shè)置或設(shè)置通用的Media Type表示最新版本的API。
這種就是在header頭上加版本控制,curl暫時不寫了
后端操作
- 方法一:后臺維護多個版本的接口,每次建立新版本copy整個文件,在新文件上修改。這個方法其實真的很困難,大版本迭代其實沒啥問題,如果是敏捷開發(fā),一到兩個周上一個版本這個問題就很大了。之前我們代碼大概是這個樣子的
├── v1.1
├── v1.12
├── v1.3
└── v1.4
然后內(nèi)部的結(jié)構(gòu)幾乎什么都一樣的。然后在外部的路由文件引入每個版本單獨的路由,如果我開一個新版本,我需要把上個版本的所有的文件copy一份:cp -r v1.4 v.15 。之前的大版本因為改的接口多,所以沒有問題,現(xiàn)在改換開發(fā)模式為敏捷,我們幾乎要管理包引用問題,每個文件夾下近百個文件。所以每個版本迭代是非常讓后臺苦惱的。
- 方法二:目前也是我們公司實踐的一種方式:copy目錄結(jié)構(gòu),將新增,修改的api寫在新版本中,完整copy路由,將修改的和新增的api添加到最新版本。然后將未修改的新版api指向之前的版本的方法。不過目前感覺還是不夠優(yōu)雅,但是按照邏輯來說的話是最優(yōu)解了。
后續(xù)發(fā)展
目前公司有部分業(yè)務(wù)已經(jīng)逐漸步入微服務(wù)了,我們打算進一步探索微服務(wù)的版本控制,看是否還能通過路由帶參數(shù)來控制后臺返回,將再接下來的文章中展示