從Rest到Graphql
引言
開局兩張圖,內容全靠編~
。嗯,我知道肯定很多人沒看過。所以我做一個總結,其實囉裡八嗦了一篇文章,就是想說一下現在的大型網際網路專案一般是如下兩種架構之一
- 前後端半分離架構
- 前後端分離架構
區別分離和半分離的標誌在於 Controller
層由不由前端控制, Controller
在前端手裡,前端手裡握著組裝資料的邏輯,那就是前後端分離!否則就是半分離!
那麼,半分離和分離的架構是長下面這樣的

ps
:中小型公司慎重,不要玩前後端分離架構!前端工作量賊大!
那麼用上了前後端分離架構後,後端的API一般會按照Restful風格來設計!ResultFul推薦每個URL能操作具體的資源,而且能準確描述伺服器對資源的處理動作,通常伺服器對資源支援get/post/put/delete/等,用來實現資源的增刪改查。前後端分離的架構下,這些api-url是對接的橋樑,採用ResultFul介面地址含義才更清晰、見名知意。
那麼,在實踐RestFul風格的API的有一個致命的缺陷,是神馬類?嗯,帶著你的疑惑開始本文
正文
RestFul的缺陷
假設,此時我們有兩個資源分別是 Book
和 Author
,這兩個資源對應的ER圖如下

相應的API有
POST /books
GET /books/{id}
POST /authors
GET /authors/{id}
我們有一個需求,需要查詢id=1的圖書資訊!
那我們的請求地址是這樣的
GET /books/1
返回結果是這樣的
[{ "id": 1, "bookname": Harry Potter, "price": 56.00, "author_id": 2 }]
這時候前端MM拿到這個結果後,傻了眼!這裡怎麼能直接返回 author_id
呢,難道直接把 author_id
顯示在介面上麼?不可能啊,介面上要顯示的是 author_name
才行!
OK,那麼在這種情況下,有兩種方式可以解決問題!
(1) 跟後端溝通,讓他增加一個介面
嗯,我們複習一下什麼是VO物件。
VO(View Object):檢視物件,用於展示層,它的作用是把某個指定頁面(或元件)的所有資料封裝起來。
那可以讓後端封裝一個介面,後端幫你把資料拼裝好,提供API如下
GET /bookVOs/1
這樣直接返回的結果就是
[{ "id": 1, "bookname": Harry Potter, "price": 56.00, "author_name": J. K. Rowling }]
當然,因為你這是臨時讓後端加介面,可能會有如下情形產生

OK,回到正題,這樣做的缺點主要有兩個
- 前後端強耦合在一起,前端介面發生變動,後端VO物件跟著一起變
- 如果BookVO物件在手機端、PC端、APP端的顯示內容都不一樣,你可能在專案中會有BookPcVO類、BookH5VO類、BookAppVO類,VO類大大膨脹!
(2) 自己做適配
這個也很簡單,前端獲得結果後,取出 author_id: 2
這條記錄,然後再去呼叫地址
GET /authors/2
得到結果,然後進行組裝顯示!
當然,這個時候會有如下情形產生(這就是我注孤生的原因!)

當然,這種做法的缺點也很明顯
- 返回了一堆前端並不需要的資料
- 徒增前後端的互動次數
ok,通過上面的描述,大家應該能體會到Rest的缺點: REST介面時返回的資料格式、資料型別都是後端預先定義好的,如果返回的資料格式並不是呼叫者所期望的,呼叫者在處理上比較麻煩!
那麼,有沒有辦法讓前端自定靈活的使用查詢語句,自己想撈什麼資料就撈什麼資料呢?
有的,那就是Graphql!
Graphql的出現
Graphql其實要這樣理解
Graphql=grap(圖)+query+lanage
是一種基於圖的查詢語言!那麼,這張圖長什麼樣?
OK,首先你要宣告一下,你的圖結構,嗯,用的就是Graphql的語法啦,像下面這樣
type Book { id: Int bookname: String authors: [Author] } type Author { id: Int name: String age: String } type Query { getBook(id: Int): Book } schema { query: Query }
對上面的語句進行一下解釋。這裡一共聲明瞭三個類 Book
、 Author
、 Query
!其實, Book
和 Author
大家都可以猜都出來是指啥,需要注意的一點是 authors: [Author]
這個地方,用了一個 []
的語法,這代表 Author
是一個數組!
唯一需要說明的是, Query
是什麼鬼?
Query
在這兒表明了該型別是這張圖的入口,也就是根節點!我們的查詢必須從根節點裡的屬性開始!這裡我為了便於說明,只列了一個屬性,也就是 getBook
,該屬性是入口,必須從入口開始查!
那麼,生成的圖是長下面這樣的

這裡還有一個 Resolver
的概念,就是說, GragphQL
解析到 getBook
方法的時候,方法體內容是啥?是在 Resovler
中定義的,我這裡就不貼配置了,感興趣的可以自己去官網閱讀!
OK,接下來就是第二個問題,怎麼查?
根據上面我說的,只能從根開始,根是 getBook
,那語句怎麼寫呢?如下所示

那你想加個欄位,顯示作者名字呢?直接像下面這麼寫

採用這種語法結構,後端的資料模型就變成了一張圖,前端可以定製化自己的輸出結構,同時可以減少前後端的溝通成本,提高靈活性!
一些疑問
(1)java語言中,對Graphql的支援如何?
在java中,有個jar包為 graphql-java-tools
提供了對Java的支援。
另外,考慮到現在大多是springboot專案,有大神封裝好了starter包,供你們的springboot專案使用!
只需在專案中引入
<!-- graphql --> <dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-spring-boot-starter</artifactId> <version>4.0.0</version> </dependency> <dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java-tools</artifactId> <version>4.3.0</version> </dependency>
即可讓你的springboot專案擁有graphql的功能,非常方便!
(2)這樣做不會加重前後端工作量麼?
說句實在話,摸著良心說,前期確實加重了前後端的工作量!
對前端而言:需要去了解 Graphql
的語法!
對後端而言:不僅需要了解 Graphql
的語法,還需要去編寫 Schema
和 Resovler
!
所以短期內,確實增加了工作量!但是從長遠來看,同時降低了前後端的工作量!第一,前端不用瞭解後端的資料結構,GraphQL自己生成可互動式的介面文件,前端可以自己測試呼叫
第二,後端不用在編寫什麼介面文件,GrapQL自動幫你生成,用起來非常舒心!
總結
本文介紹了Rest的缺點,以及Graphql的基本知識,希望大家有所收穫!