文章轉(zhuǎn)發(fā)自專業(yè)的Laravel開(kāi)發(fā)者社區(qū),原始鏈接:https://learnku.com/laravel/t/28508
這個(gè)話題在開(kāi)發(fā)社區(qū)里已經(jīng)討論過(guò)一段時(shí)間,人們對(duì)此有不同的看法與觀點(diǎn),那么我應(yīng)該使用哪一個(gè)? 有很多東西需要成長(zhǎng)但富有活力的新成員還是經(jīng)驗(yàn)豐富的老成員? 在此之前讓我們了解下 REST 和 GraphQL吧。
REST 是什么?
REST即表述性狀態(tài)傳遞(英文:Representational State Transfer,簡(jiǎn)稱REST),它符合特定的指南,是 Web API 實(shí)現(xiàn)的約束。是 Roy Fielding 博士在他的博士論文中提出來(lái)的一種軟件架構(gòu)風(fēng)格。它鼓勵(lì)客戶端和服務(wù)器以無(wú)狀態(tài)模式交換信息。 請(qǐng)記住,并非所有 API 都是 REST,但所有 RESTful 服務(wù)都是 API。
GraphQL 是什么?
GraphQL是Web API 的查詢語(yǔ)言。它由Facebook于2012年創(chuàng)建,并于2015年開(kāi)源。它既不是架構(gòu)模式,也不是Web服務(wù)。它是個(gè)中介,用來(lái)查詢從各種數(shù)據(jù)源接收的數(shù)據(jù)。 這些數(shù)據(jù)源可以是數(shù)據(jù)庫(kù)或Web服務(wù)。
多年來(lái),REST已成為Web API的事實(shí)上的標(biāo)準(zhǔn)。 由于它使用了標(biāo)準(zhǔn)的HTTP方法(GET,POST,PUT,DELETE等),隨著互聯(lián)網(wǎng)上的Web應(yīng)用程序的增加,它也得以發(fā)展和普及。 此外,它的語(yǔ)言和平臺(tái)無(wú)關(guān),使其成為創(chuàng)建Web服務(wù)的更好選擇。 因?yàn)槊總€(gè)數(shù)據(jù)都被視為在調(diào)用URL時(shí)要發(fā)送的資源,所以甚至可以使用Web瀏覽器或使用cURL請(qǐng)求來(lái)調(diào)用它。
REST的缺點(diǎn)
雖然REST非常成功,但由于RESTful服務(wù)的規(guī)模和復(fù)雜性不斷增長(zhǎng),因此它的缺點(diǎn)變得非常明顯
1. 多端 (多次數(shù)據(jù)交互)
在 RESTful 服務(wù)中一個(gè) URL 表示一個(gè)資源。因此,當(dāng)要獲取多個(gè)資源時(shí)你必須請(qǐng)求多個(gè)不同的 URL,進(jìn)而帶來(lái)多次數(shù)據(jù)交互。
當(dāng)我們考慮一個(gè)博客應(yīng)用。一篇博客下面有多條評(píng)論的情形。通常我們要調(diào)用的 URL 如下
GET /posts/<postId> - 獲取特定的博客文章
GET /posts/<postId>/comments - 獲取上面博客文章關(guān)聯(lián)的所有評(píng)論
GET /posts/<postId>/comments/<commentId> - 獲取特定博客下的特定評(píng)論
你會(huì)發(fā)現(xiàn)我們要請(qǐng)求的 URL 多了不少。這是因?yàn)閷?shí)體 (這里可以理解為博文和評(píng)論)之間的關(guān)聯(lián)關(guān)系更加復(fù)雜了。隨著應(yīng)用變得越來(lái)越復(fù)雜,管理這些 API 也變得更加困難。
2. 過(guò)度獲取/獲取后 數(shù)據(jù)
有時(shí)候,當(dāng)您請(qǐng)求 API 接口時(shí),您會(huì)獲得不必要的數(shù)據(jù)和相關(guān)數(shù)據(jù),有時(shí)候您無(wú)法獲得足夠的數(shù)據(jù),所以您最終會(huì)進(jìn)行多次往返。 這是 RESTful 服務(wù)中的常見(jiàn)問(wèn)題。 在某些情況下,您可能只需要 2 - 3 個(gè)值,但您可以獲得大約 20 - 25 個(gè)值作為響應(yīng)。 這只會(huì)通過(guò)增加響應(yīng)時(shí)間,導(dǎo)致傳輸大量未使用的數(shù)據(jù)。 在后一種情況下,您獲取的信息可能需要比從單個(gè) URL 獲取的信息要多,因此有必要進(jìn)行多次往返。 這也導(dǎo)致客戶端獲取所有所需數(shù)據(jù)所花費(fèi)的時(shí)間成本增加。
3. API版本控制
API 版本控制是一種遵循的方法,以避免使用響應(yīng)格式的更改來(lái)破壞客戶端應(yīng)用程序。 當(dāng) API 響應(yīng)格式發(fā)生更改時(shí),將創(chuàng)建新版本。 這樣做是為了使生產(chǎn)客戶端應(yīng)用程序可以按預(yù)期運(yùn)行,并為開(kāi)發(fā)人員提供一些休息的時(shí)間來(lái)遷移到新的 API 版本。
但是這個(gè)版本控制是一個(gè)問(wèn)題,因?yàn)楫?dāng)新版本發(fā)布時(shí),它意味著新的 URL。 API 的維護(hù)和使用變得困難,并且經(jīng)常導(dǎo)致重復(fù)的代碼。
4. 弱類型
并非我們從 RESTful 服務(wù)收到的所有數(shù)據(jù)都是強(qiáng)類型的,即它們沒(méi)有正確地給出特定數(shù)據(jù)。 這在記錄 API 時(shí)會(huì)成為問(wèn)題,因?yàn)槲覀儽仨毻ㄟ^(guò)調(diào)用 URL 來(lái)指定客戶端可以期望的數(shù)據(jù)類型。
5. 客戶端被蒙在鼓里
在收到響應(yīng)結(jié)構(gòu)之前,客戶端不知道響應(yīng)結(jié)構(gòu)。所以,客戶端是被蒙在鼓里的。這可能經(jīng)常導(dǎo)致一些錯(cuò)誤和數(shù)據(jù)無(wú)法正確處理,從而降低了消耗 API 的可靠性。
GraphQL 的優(yōu)點(diǎn)
GraphQL 是由 Facebook 發(fā)明的,主要是為了克服 REST 的缺點(diǎn)。
1. 一次請(qǐng)求獲取到所有
一個(gè) GraphQL 服務(wù)只暴露一個(gè)端點(diǎn)以便客戶端能傳輸必要的查詢?nèi)z索數(shù)據(jù)。 使用前面考慮過(guò)的相同示例,讓我們看看 GraphQL 查詢
{
findPost(id: <postId>) {
id
title
content
author
comments {
id
comment
commentedBy
}
}
}
正如你所看到的,我們僅僅通過(guò)單個(gè)請(qǐng)求獲取到了所有必要的數(shù)據(jù)。 所以當(dāng)你想要一個(gè)新字段你只需要將它添加進(jìn)查詢中,它將在響應(yīng)中呈現(xiàn)
2. 強(qiáng)類型
GraphQL 被強(qiáng)類型模式所控制。這些類型既可以是原始的也可以是派生的。強(qiáng)類型系統(tǒng)允許 API 自文檔化,從而使客戶端知道在查詢特定查詢時(shí)會(huì)的到什么響應(yīng)。
3. 客戶端驅(qū)動(dòng)
GraphQL 提供了一種聲明式語(yǔ)法,以便客戶端精確地指定它們所需的字段。 這消除了由于客戶端根據(jù)模式向 GraphQL 服務(wù)器聲明其數(shù)據(jù)需求而導(dǎo)致數(shù)據(jù)冗余和不充分的可能性。
4. API 演變
因?yàn)樵?GraphQL 中一切都是模式(schema)驅(qū)動(dòng), 新增字段不會(huì)影響現(xiàn)存字段,而且 GraphQL 還為廢棄字段提供 @deprecated 注釋,所以對(duì) GraphQL 的擴(kuò)展并不是問(wèn)題。 這就消除了 API 版本控制的需要。
5. 傳輸層不可知
這是 GraphQL 的一個(gè)非常棒的優(yōu)點(diǎn)。 API 服務(wù)器可以通過(guò)類似 HTTP, HTTPS, WebSockets, TCP, UDP 等協(xié)議進(jìn)行信息交換。 這是因?yàn)?GraphQL 甚少關(guān)心信息如何在客戶端與服務(wù)器之間進(jìn)行交換。
GraphQL 的缺點(diǎn)
哇,GraphQL 很棒,這是一個(gè)眾所周知的事實(shí)。但是世界上的任何東西都是有缺陷的,GraphQL 也無(wú)法置身事外。
1. 緩存功能不成熟
GraphQL 不支持瀏覽器和移動(dòng)手機(jī)緩存,這一點(diǎn)區(qū)別于使用本地 HTTP 緩存機(jī)制的 RESTful 服務(wù),因此我們要為實(shí)現(xiàn) GraphQL 緩存付出額外努力。雖然有 Relay 這樣的工具提供了一些緩存支持,但是它們還沒(méi)有 RESTful 服務(wù)使用的緩存機(jī)制成熟。
2. 檢驗(yàn)與錯(cuò)誤報(bào)告
RESTful 服務(wù)利用 HTTP 狀態(tài)代碼來(lái)處理可能遇到的不同錯(cuò)誤。對(duì)開(kāi)發(fā)人員來(lái)說(shuō),這使得 APIs 的檢驗(yàn)變得非常簡(jiǎn)單和輕松。但是使用 GraphQL 服務(wù)總是返回 200 OK 響應(yīng)。一個(gè)典型的 GraphQL 錯(cuò)誤消息是這樣的,狀態(tài)碼為 200 OK 。
{
errors: [
{
message: 'Some error occurred'
}
]
}
這使得處理錯(cuò)誤場(chǎng)景非常困難,并且使得檢驗(yàn)過(guò)程非常麻煩。
3. 暴露模式和資源攻擊
和RESTful服務(wù)不同,GraphQL服務(wù)要求客戶端必須知道要查詢的數(shù)據(jù)模式。 如果您將API暴露給第三方,則基本上暴露了您的內(nèi)部數(shù)據(jù)結(jié)構(gòu)。 必須非常小心,因?yàn)榭蛻舳瞬挥煤芨叩拇鷥r(jià)就可以發(fā)起連接查詢,這可能會(huì)導(dǎo)致服務(wù)器上的拒絕服務(wù)(DoS)攻擊。
4. 安全 - 身份驗(yàn)證和授權(quán)
GraphQL社區(qū)仍然對(duì)如何處理GraphQL服務(wù)的安全部分感到困惑。仍然沒(méi)有集成身份驗(yàn)證和授權(quán)的原生解決方案。它通常被抽象到業(yè)務(wù)邏輯層來(lái)授權(quán)用戶,但是我們是否真的必須解析和驗(yàn)證一個(gè)未經(jīng)驗(yàn)證的用戶的查詢?nèi)匀皇荊raphQL領(lǐng)域中的一個(gè)問(wèn)題。
5. N + 1 次查詢問(wèn)題
在RESTful服務(wù)中,很容易記錄執(zhí)行的SQL查詢并進(jìn)一步優(yōu)化它。但在GraphQL的情況下,解析性質(zhì)是動(dòng)態(tài)的,因此很難獲得精確的查詢并進(jìn)一步優(yōu)化它。有時(shí),字段解析器可能會(huì)導(dǎo)致N+1次查詢問(wèn)題和復(fù)雜的連接操作。但是Facebook正在開(kāi)發(fā)像DataLoader這樣的工具來(lái)解決這個(gè)確切的問(wèn)題。所以,也許在未來(lái),這不會(huì)是一個(gè)不利因素。
6. 年輕的生態(tài)
GraphQL在這個(gè)API生態(tài)系統(tǒng)中非常像一個(gè)嬰兒,這意味著隨時(shí)可能會(huì)出點(diǎn)問(wèn)題以及破壞性更改,因此在使用與GraphQL相關(guān)的任何庫(kù)和模塊時(shí),我們必須非常細(xì)心。
總結(jié)
首先我要說(shuō)GraphQL只是一種工具,REST是一種架構(gòu)模式。如果說(shuō)用GraphQL取代REST,那就大錯(cuò)特錯(cuò)了。但是在這個(gè)微服務(wù)的時(shí)代,我們將API分離并創(chuàng)建到原子級(jí)別,我們可以利用這兩個(gè)方面的優(yōu)勢(shì),因?yàn)椴](méi)有銀彈。
GraphQL服務(wù)將性能作為首要任務(wù),而RESTful服務(wù)則保持可靠性。
GraphQL節(jié)點(diǎn)可以通過(guò)現(xiàn)有的RESTful服務(wù)作為節(jié)點(diǎn)公開(kāi),比如/ graphql,它可以作為運(yùn)行GraphQL查詢的網(wǎng)關(guān),同時(shí)也可以為某些場(chǎng)景維持REST節(jié)點(diǎn)。
在某些場(chǎng)景中, 使用 GraphQL 會(huì)更好, 也有一些場(chǎng)景中 REST 必然更好。因此在說(shuō)哪一個(gè)更好之前, 需要分析一下所涉及的需求和數(shù)據(jù), 才知道哪個(gè)更適合。