1. 程式人生 > >為什麼RESTful很糟糕?

為什麼RESTful很糟糕?

週六晚上,RESTful發明人羅伊悄悄來到了咖啡館,他想看看自己引以為傲的RESTful到底用得怎麼樣。 

(RESTful的故事參見《RPC發展簡史》)

靠著門的那張桌子有一幫人,他們居然還在討論老掉牙的Java RMI,似乎遇到了什麼技術難題。 

看來無論是什麼技術,都會有非常古老的遺留系統需要維護,真是苦逼的程式設計師啊, 羅伊感慨。 

這邊的一群人在討論Google的Protobuf , 看來在序列化這一塊兒已經有了長足的進展,都可以實現跨語言序列化了。 

再往裡邊走,終於有人討論RESTful了! 羅伊心中一陣激動。 

只聽到一個人說道:“我們領導剛開始強制用RESTful的面向資源的風格,大家還挺新奇的,可是用著用著,我們就‘退回’到那種面向函式的方式了。”  

“是啊是啊,我還是更習慣傳統的RPC方式,更加直觀,尤其是用了Dubbo以後,面向函式的風格不要太爽。”  

羅伊心中有點失望。 

RESTful的硬傷

“其實吧,RESTful有個硬傷,你們發現沒有?”  有個叫做格拉夫的人突然來了一句。 

“什麼硬傷?” 

“我給你們舉個例子,” 格拉夫說道,“比如我有兩個資源,一個叫做Article ,一個叫做User。” 

“這很正常啊,對資源可以增刪改查。” 羅伊說道,他的話也引起了周圍人的附和。 

“聽我說完,我現在要開發一個手機端,要展示一個文章的列表,假設介面原型是這個樣子:” 

“第一步,我需要獲得這些文章列表,可以這麼做  GET /articles ” 

“這也沒問題啊!不就是一個普通的獲取資源表示的方式嗎?” 人群中有人說道。 

可是羅伊敏銳地發現,介面中需要一個“作者頭像”, 很明顯,這個作者頭像並不在Article這個資源中儲存, 而是在User中。 

在返回的結果中只有author_id, 如果想要獲得作者頭像,需要對返回的文章列表做迴圈,取出author_id ,然後在通過/users這個資源進行查詢。 

當羅伊把這個查詢展示出來以後,周圍人群就炸了鍋:“有多少個文章,就額外發出多少次查詢,這怎麼行?! 實際中肯定不能這麼幹!” 

還有人說:“我們僅僅需要頭像資訊(avatar_url),你返回這麼多亂七八糟的gender, age,last_login_time幹嘛! ”  

“這RESTful真是糟糕啊!”

羅伊有點尬尷,沒想到我的RESTful會存在這麼兩個問題: 

1.  傳送請求過多 (對每個文章,都得額外查詢作者資訊) 

2.  太多的額外資訊(其實只想要avatar_url這個欄位) 

想到此處,羅伊的心一下子就沉了下去,怎麼解決這個問題呢? 

 

中間層

老祖宗教導我們: 電腦科學領域的任何問題都可以通過增加一個間接的中間層來解決!  

這個中間層是什麼? 羅伊想到了前後端還沒有分離的時候,頁面都是在後端渲染的,程式設計師會建立一個VO(View Object),在這個VO中,會把介面展示所需要的資訊都封裝起來,然後再發給JSP去使用。 

在RESTful當中,也可以搞一個類似VO的資源出來啊, 想到此處,他對格拉夫說道:“你為什麼不搞一個HotArticle這樣的資源出來呢? ” 

這個HotArticle可以把文章和作者做個組合,只返回那些介面需要的資料。 

格拉夫說道:“這樣可不好, 這個HotArticle資源相當於和介面深度綁定了。介面如果要變化,這個HotArticle也得變化,很麻煩啊。”  

羅伊說:“那就一起變化唄,反正兩個是一致的。” 

“不,還有更復雜的情況, 假設介面發生了變化,需要把作者頭像替換成文章的封面圖, 這時候怎麼辦呢?” 

羅伊說:“我明白你的意思,不就是說要同時支援老的手機端和新的手機端嗎?簡單,有兩種方案。” 

(1) 複用HotArticle, 保留原來的avatar_url, 新增一個新的欄位 article_img_url, 不同的手機端各取所需。 

(2) 給HotArticle做個新版本。老的手機端用老版本,新的手機端用新版本。  

“第一種方案好!很簡單!” 人群中有人說道。 

“好啥啊,如果手機介面持續變化,你用第一種辦法,那個HotArticle很快就成了垃圾堆了,要是沒有準確無誤的文件,都不知道哪個欄位被誰使用!”  

“第二種方案會搞出很多版本來,假設要改個公共的東西,比如要增加一個文章的閱讀數,那豈不是所有的都得改? ” 

可見兩種辦法各有優劣, 在應對手機端介面持續變化時都有問題,都不完美。這也是後端資源和前端介面繫結後的惡果啊。 

 

靈活查詢

羅伊陷入了沉思: 能不能讓手機端按需查詢呢?  

伺服器端保持最簡單的Article 和 User的概念,把他們看成兩張表,手機端發出像SQL 那樣的查詢,把自己需要的給查出來,最好能類似於Join的功能。 

想到此處,他就寫了這麼一個SQL : 

select a.id, a.title, a.abstract, a.liked_count, u.avatar_url from Article a , User u where a.author_id = u.id 

看到這個圖,人群轟然叫好:“還是SQL大法好!” 

只有格拉夫冷冷地說道:“SQL的侷限性太大, 比如我還要把作者的朋友同時顯示到手機端,這SQL就不好寫了,文章和作者是一一對應的,但是作者的朋友可能有多個,這樣SQL的結果集中就會有重複的文章id , title , abstract了。” 

羅伊說:“那你說怎麼辦?”  

“關係模型在表示這樣的關聯的時候,非常不方便,我發明了一個新的模型和新的查詢語言, 大家看看吧。” 

 

古怪的查詢

格拉夫展示了一個查詢的方法: 

大家猛地一看, 這個查詢太古怪了啊,這是什麼語法? 

雖然古怪,卻非常實用,精確地描述了這個需求:我需要一個id 為11的使用者, 把他的name, age, avatar_url等欄位給我取過來,其他欄位就不用發過來了。 

查詢結果也是標準的JSON格式,和要查詢的內容一一對應,非常容易理解。 

羅伊問道:“這也沒啥啊,你怎麼解決之前的問題?” 

格拉夫又展示了一個查詢,這一次複雜了一些: 

 “看到沒有? 這次表示一個article列表,每個article元素裡邊有id, title, abstract,liked_count等欄位。 還有一個特殊欄位叫做author,相當於在article中嵌套了一個元素,這個author元素還有一個欄位叫做avatar_url。 ” 

眾人一看,覺得非常有意思,用這種方式完美地解決了之前的問題。 

只需要一次查詢,文章和作者的頭像一起就發回來了,更重要的是,沒有什麼亂七八糟的額外資訊。 

如果想加上作者的朋友資訊,可以把查詢改成下面這個樣子,非常靈活。 

看到此處,羅伊就明白了幾分,這是一種新的查詢方式,不同於關係資料庫的SQL, 也不同於RESTful, 很明顯,後端的資料模型也得發生變化。 

他問道:“你後端的資料模型難道是圖Graph嗎?”  

格拉夫讚道:“被你看出來了,真是厲害,為了支援這樣的查詢,在後臺的資料模型就是一張圖:”

 “根據這張圖,我就可以查找出任意的資料了,從Article找到作者, 從作者找到相關的朋友......, 只要你把關聯做好,沒有什麼做不到的。” 

“那些Article, User型別及其屬性是不是也得明確地定義下來?” 羅伊又問道。 

格拉夫對羅伊投去讚歎的目光, 說道:“沒錯,可以這麼定義。” 

一目瞭然,大家都非常喜歡! 

“這個新的查詢語言叫什麼名字?” 

“我叫格拉夫(Graph),所以這個查詢語言叫做GraphQL!”