0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

到底什么樣的REST才是最佳REST?

OSC開(kāi)源社區(qū) ? 來(lái)源:OSC開(kāi)源社區(qū) ? 2023-01-17 10:14 ? 次閱讀

說(shuō)起 REST API,小伙伴們多多少少都有聽(tīng)說(shuō)過(guò),但是如果讓你詳細(xì)介紹一下什么是 REST,估計(jì)會(huì)有很多人講不出來(lái),或者只講出來(lái)其中一部分。

今天松哥就來(lái)和大家一起來(lái)聊一聊到底什么是 REST,順便再來(lái)看下 Spring HATEOAS 的用法。

1. REST 成熟模型

首先關(guān)于 REST,有一個(gè)大佬 Leonard Richardson 為 REST 定義了一個(gè)成熟度模型,他一共定義了四個(gè)不同的層次,分別如下:

Level0:Web 服務(wù)單純的使用 HTTP 作為數(shù)據(jù)傳輸方式,本質(zhì)上就是遠(yuǎn)程方法調(diào)用,常見(jiàn)的 SOAP 和 RPC 基本上都屬于這一類(lèi)。

Level1:在這一級(jí)別上,引入了資源的概念,服務(wù)端的每一個(gè)資源,都有一個(gè)對(duì)應(yīng)的操作地址。

Level2:在這一級(jí)別上,我們引入了不同的 HTTP 請(qǐng)求方法來(lái)描述不同的操作,例如 GET 表示查詢、POST 表示插入、PUT 表示更新、DELETE 表示刪除,并且使用 HTTP 的狀態(tài)碼來(lái)表示不同的響應(yīng)結(jié)果。一般來(lái)說(shuō),大家在日常的接口開(kāi)發(fā)中,基本上都能做到這一層級(jí)。但是這還不是最佳結(jié)果。

Level3:按照 Leonard Richardson 的意思,這一層級(jí)的 REST 基于 HATEOAS(Hypertext As The Engine Of Application State),在這一級(jí)別上,除了返回資源的 JSON 之外,還會(huì)額外返回一組 Link,這組 Link 描述了對(duì)于該資源可以做哪些操作,以及具體的該怎么做。

在日常的開(kāi)發(fā)中,我們一般都是只實(shí)現(xiàn)到 Level2 這一層級(jí),真正做到 Level3 的估計(jì)很少,不過(guò)雖然在工作中一般不會(huì)做到 Level3 這一層級(jí),但是,我相信很多小伙伴應(yīng)該是見(jiàn)過(guò) Level3 層級(jí)的 REST 是啥樣子的,特別是看過(guò) vhr 視頻的小伙伴,松哥在其中講過(guò),通過(guò) Spring Data Jpa+Spring Rest Repositories 實(shí)現(xiàn)的 CURD 接口,其實(shí)就是一個(gè)達(dá)到了 Level3 層級(jí)的 REST。

2. Spring HATEOAS

那么接下來(lái)我先用 Spring HATEOAS 寫(xiě)一個(gè)簡(jiǎn)單的 REST,然后結(jié)合這個(gè)案例來(lái)和小伙伴們聊一聊到底 Spring HATEOAS 有何不一樣的地方。

首先我們創(chuàng)建一個(gè) Spring Boot 工程,引入 Web 和 Spring HATEOAS 依賴,如下:

99163e5a-95bf-11ed-bfe3-dac502259ad0.png

創(chuàng)建好之后,我們首先創(chuàng)建一個(gè) User 實(shí)體類(lèi):

publicclassUserextendsRepresentationModel{
privateIntegerid;
privateStringusername;
privateStringaddress;
//省略getter/setter
}

注意這個(gè) User 實(shí)體類(lèi)需要繼承自 RepresentationModel,以方便后續(xù)添加不同的 Link(以前舊的版本需要繼承自 ResourceSupport)。

接下來(lái)寫(xiě)一個(gè)簡(jiǎn)單的測(cè)試接口。

查詢所有用戶:

@RestController
@RequestMapping("/users")
publicclassUserController{

@GetMapping
publicCollectionModellist(){
Listlist=newArrayList<>();
Useru1=newUser();
u1.setId(1);
u1.setUsername("javaboy");
u1.setAddress("www.javaboy.org");
u1.add(WebMvcLinkBuilder.linkTo(UserController.class).slash(u1.getId()).withSelfRel());
list.add(u1);
Useru2=newUser();
u2.setId(2);
u2.setUsername("itboy");
u2.setAddress("www.itboyhub.com");
u2.add(WebMvcLinkBuilder.linkTo(UserController.class).slash(u2.getId()).withSelfRel());
list.add(u2);
CollectionModelusers=CollectionModel.of(list);
users.add(WebMvcLinkBuilder.linkTo(UserController.class).withRel("users"));
returnusers;
}
}

關(guān)于這個(gè)接口,我來(lái)說(shuō)幾點(diǎn):

首先,對(duì)于這種返回一個(gè)集合或者數(shù)組的情況,返回的類(lèi)型都是 CollectionModel。

把集合弄好之后(正常應(yīng)該去數(shù)據(jù)庫(kù)中查詢,我這里省事直接創(chuàng)建了),通過(guò) CollectionModel.of(list) 方法去獲取一個(gè) CollectionModel 對(duì)象。

對(duì)于每一個(gè) user 對(duì)象,我都添加了一個(gè) Link 對(duì)象,WebMvcLinkBuilder.linkTo(UserController.class).slash(u1.getId()).withSelfRel() 表示生成當(dāng)前對(duì)象的訪問(wèn)鏈接。

WebMvcLinkBuilder.linkTo(UserController.class).withRel("users") 表示訪問(wèn)所有數(shù)據(jù)的鏈接。

好了,這個(gè)接口寫(xiě)完之后,我們?cè)L問(wèn)看下:

993a43ea-95bf-11ed-bfe3-dac502259ad0.png

可以看到,返回的每一個(gè) user 對(duì)象中,都有一個(gè)鏈接表示如何單獨(dú)訪問(wèn)這個(gè)對(duì)象。最下面還有一個(gè)訪問(wèn)所有對(duì)象的鏈接。

對(duì)于上面這個(gè)案例,可能有小伙伴會(huì)質(zhì)疑,難道我們從數(shù)據(jù)庫(kù)中查詢出來(lái)的 List 集合都要遍歷一遍,然后給每一個(gè) User 添加一個(gè) Link 嗎?其實(shí)不必,添加 Link 這個(gè)事可以直接在 User 類(lèi)中完成,如下:

publicclassUserextendsRepresentationModel{
privateIntegerid;
privateStringusername;
privateStringaddress;

publicUser(Integerid){
super(WebMvcLinkBuilder.linkTo(UserController.class).slash(id).withSelfRel());
this.id=id;
}
//省略getter/setter
}

可以看到,直接在構(gòu)造方法中完成即可。此時(shí)接口里就不用那么復(fù)雜了,如下:

@GetMapping
publicCollectionModellist(){
Listlist=newArrayList<>();
Useru1=newUser(1);
u1.setUsername("javaboy");
u1.setAddress("www.javaboy.org");
list.add(u1);
Useru2=newUser(2);
u2.setUsername("itboy");
u2.setAddress("www.itboyhub.com");
list.add(u2);
CollectionModelusers=CollectionModel.of(list);
users.add(WebMvcLinkBuilder.linkTo(UserController.class).withRel("users"));
returnusers;
}

那么對(duì)于根據(jù) ID 來(lái)查詢用戶的需求,我們也應(yīng)該給一個(gè)接口如下:

@RestController
@RequestMapping("/users")
publicclassUserController{

@GetMapping("/{id}")
publicEntityModelgetOne(@PathVariableIntegerid)throwsNoSuchMethodException{
Useru=newUser(id);
u.setUsername("javaboy");
u.setAddress("深圳");
u.add(Link.of("http://localhost:8080/users/"+id,"getOne"));
Linkusers=WebMvcLinkBuilder.linkTo(UserController.class).withRel("users");
u.add(users);
Linklink=WebMvcLinkBuilder.linkTo(UserController.class).slash(u.getId()).withSelfRel();
u.add(link);
Methodmethod=UserController.class.getMethod("getOne",Integer.class);
Linklink2=WebMvcLinkBuilder.linkTo(method,id).withSelfRel();
u.add(link2);
returnEntityModel.of(u);
}
}

關(guān)于這個(gè)接口,我說(shuō)如下幾點(diǎn):

如果返回類(lèi)型是一個(gè)對(duì)象的話,需要使用 EntityModel 類(lèi)型。

搞好返回的對(duì)象之后,通過(guò) EntityModel.of(u) 方法可以獲取到目標(biāo)數(shù)據(jù)類(lèi)型。

這個(gè)地方,為了給小伙伴們演示不同的 Link 添加方式,我寫(xiě)了好多個(gè)(單純?yōu)榱搜菔静煌?Link 添加方式):

Link.of("http://localhost:8080/users/"+id, "getOne") 這種是自己純手工去生成當(dāng)前對(duì)象的訪問(wèn)鏈接,很明顯這不是一個(gè)很好的方案。當(dāng)前對(duì)象的訪問(wèn)鏈接建議使用上文中提到的方式。

WebMvcLinkBuilder.linkTo(UserController.class).withRel("users") 這個(gè)是生成當(dāng)前這個(gè) Controller 的訪問(wèn)鏈接,一般就是訪問(wèn)所有用戶對(duì)象的鏈接。

WebMvcLinkBuilder.linkTo(UserController.class).slash(u.getId()).withSelfRel() 前文已經(jīng)用過(guò)了,不多說(shuō)了,實(shí)際應(yīng)用中建議使用這種。

也可以根據(jù)某一個(gè)方法自動(dòng)生成,像這樣 WebMvcLinkBuilder.linkTo(method, id).withSelfRel(),這個(gè)是生成某一個(gè)具體方法的訪問(wèn)鏈接。

好了,現(xiàn)在我們來(lái)看下這個(gè)接口生成的 JSON,如下:

994bfaae-95bf-11ed-bfe3-dac502259ad0.png

生成的這段 JSON 我將之標(biāo)記為了三部分:

第一部分,self,就是自身的訪問(wèn)鏈接,這三個(gè)鏈接分別是 User 的構(gòu)造方法,以及前面提到的 3.3 和 3.4 的方法生成的。

第二部分,getOne 這個(gè),是前面 3.1 中提到的方法生成的。

第三部分,users 這個(gè),是前面提到的 3.2 方法生成的。

當(dāng)然,其實(shí)這塊還有很多其他的生成鏈接的玩法,但是我就不一一介紹了,小伙伴們可以參考官方文檔:

https://docs.spring.io/spring-hateoas/docs/current/reference/html

從上面 Spring HATEOAS 中返回的 JSON 我們大致上可以看到它的特點(diǎn):

當(dāng)我們使用了 Spring HATEOAS,此時(shí),客戶端就會(huì)通過(guò)服務(wù)端返回的 Link Rel 來(lái)獲取請(qǐng)求的 URI(如果沒(méi)有使用 Spring HATEOAS,則客戶端訪問(wèn)的 URI 都是提前在客戶端硬編碼的),現(xiàn)在我們就可以做到服務(wù)端在不破壞客戶端實(shí)現(xiàn)的情況下動(dòng)態(tài)的完成 URI 的修改,從而進(jìn)一步解耦客戶端和服務(wù)端。

簡(jiǎn)而言之,現(xiàn)在客戶端能干什么事情,在服務(wù)端返回的 JSON 中都會(huì)告訴客戶端,客戶端從服務(wù)端返回的 JSON 中獲取到請(qǐng)求的 URL,然后直接執(zhí)行即可。如果這個(gè)請(qǐng)求地址發(fā)生變化的話,客戶端也會(huì)及時(shí)拿到最新的地址。

可能上面的例子小伙伴們感受還不是很明顯,我再給大家看一段 JSON:

{
"tracking_id":"666",
"status":"WAIT_PAYMENT",
"items":[
{
"name":"book",
"quantity":1
}
],
"_Links":{
"self":{
"href":"http://localhost:8080/orders/666"
},
"cancel":{
"href":"http://localhost:8080/orders/666"
},
"payment":{
"href":"http://localhost:8080/orders/666/payments"
}
}
}

這是電商系統(tǒng)下單之后等待支付的過(guò)程中返回的 JSON,這里的 links 給出了三個(gè):

self:訪問(wèn)這個(gè)鏈接可以查看當(dāng)前訂單信息(GET 請(qǐng)求)。

cancel:訪問(wèn)這個(gè)鏈接可以取消當(dāng)前訂單(DELETE 請(qǐng)求)。

payment:訪問(wèn)這個(gè)鏈接可以支付當(dāng)前訂單(POST 請(qǐng)求)。

這個(gè)例子就很直白了,就是在返回的 JSON 中,直接告訴你接下來(lái)能做哪些操作,對(duì)應(yīng)的 URL 分別是什么,前端拿到之后直接操作,如果這些操作路徑發(fā)生了變化,前端也會(huì)立馬拿到最新的路徑。

這就是 Spring HATEOAS 的好處。總之一句話,Spring HATEOAS 提倡在響應(yīng)返回的 Link 中給出對(duì)該資源接下來(lái)操作的 URL。這種方式解耦了服務(wù)端 URI,也可以讓客戶端開(kāi)發(fā)者更容易地探索 API。

3. REST 的優(yōu)缺點(diǎn)

雖然我們現(xiàn)在都鼓勵(lì)設(shè)計(jì) REST 風(fēng)格的 API,然而 REST 也不全是優(yōu)點(diǎn),事物總是具有兩面性,REST 的優(yōu)缺點(diǎn)分別如下。

3.1 優(yōu)點(diǎn)

首先,REST 足夠簡(jiǎn)單,有一定 Web 開(kāi)發(fā)經(jīng)驗(yàn)的小伙伴都可以快速上手 REST。

REST 風(fēng)格的接口測(cè)試起來(lái)也非常方便,利用瀏覽器自帶的一些 REST 插件或者是 POSTMAN 之類(lèi)的工具,就可以非常方便的實(shí)現(xiàn) REST 接口的測(cè)試。

不需要中間代理,簡(jiǎn)化了系統(tǒng)的結(jié)構(gòu)。

HTTP 對(duì)防火墻比較友好。

3.2 缺點(diǎn)

REST 只支持請(qǐng)求-響應(yīng)通信方法,不支持服務(wù)端推送消息到客戶端。

給請(qǐng)求取一個(gè)合適的名字比較困難,特別是有多個(gè)相類(lèi)似的接口時(shí),例如有多個(gè)添加接口、多個(gè)更新接口等。

由于沒(méi)有中間代理,所以請(qǐng)求/響應(yīng)的時(shí)候,服務(wù)端和客戶端都必須在線。

好啦,跟小伙伴們聊了 REST 和 Spring HATEOAS,感興趣的小伙伴可以去試試哦~

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 接口
    +關(guān)注

    關(guān)注

    33

    文章

    8257

    瀏覽量

    149957
  • API
    API
    +關(guān)注

    關(guān)注

    2

    文章

    1461

    瀏覽量

    61492
  • 模型
    +關(guān)注

    關(guān)注

    1

    文章

    3032

    瀏覽量

    48363
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    335

    瀏覽量

    14259
  • REST
    +關(guān)注

    關(guān)注

    0

    文章

    32

    瀏覽量

    9387

原文標(biāo)題:到底什么樣的REST才是最佳REST?

文章出處:【微信號(hào):OSC開(kāi)源社區(qū),微信公眾號(hào):OSC開(kāi)源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    War3Rest.exe下載

    War3Rest.exe
    發(fā)表于 10-04 07:46 ?0次下載
    War3<b class='flag-5'>Rest</b>.exe下載

    REST學(xué)習(xí)

    學(xué)習(xí)REST必備
    發(fā)表于 07-05 15:22 ?15次下載

    REST、gRPC、GraphQL及WebHook的對(duì)比和選型

    首先REST--Resource Representational State Transfer, 中文直譯就是資源在網(wǎng)絡(luò)中以某種表現(xiàn)形式進(jìn)行狀態(tài)轉(zhuǎn)移。
    發(fā)表于 06-13 10:34 ?2398次閱讀

    REST端口支持構(gòu)建動(dòng)態(tài)REST請(qǐng)求來(lái)使用RESTful API網(wǎng)絡(luò)

    REST端口支持構(gòu)建動(dòng)態(tài)REST請(qǐng)求來(lái)使用RESTful API網(wǎng)絡(luò)服務(wù)。 概覽 REST端口暴露了一個(gè)簡(jiǎn)單的接口來(lái)為REST請(qǐng)求構(gòu)建頭、授權(quán)、主體和HTTP方法。請(qǐng)求體可以在端口配置
    的頭像 發(fā)表于 01-17 09:11 ?4670次閱讀

    REST API是什么,如何使用REST端口

    API是Application Programming Interface(應(yīng)用程序接口)的縮寫(xiě),它是拿來(lái)描述一個(gè)類(lèi)庫(kù)的特征或是如何去運(yùn)用它。按照目前比較主流的分法,可以分為REST API和非
    的頭像 發(fā)表于 02-17 18:00 ?9010次閱讀
    <b class='flag-5'>REST</b> API是什么,如何使用<b class='flag-5'>REST</b>端口

    yii2-rest-rbac Yii2權(quán)限管理RBAC rest接口

    ./oschina_soft/gitee-yii2-rest-rbac.zip
    發(fā)表于 06-30 09:35 ?0次下載
    yii2-<b class='flag-5'>rest</b>-rbac Yii2權(quán)限管理RBAC <b class='flag-5'>rest</b>接口

    如何使用Spring構(gòu)建REST服務(wù)(一)

    關(guān)于 REST 如何適應(yīng)微服務(wù)世界還有一個(gè)更大的討論,但是——對(duì)于本教程——讓我們看看構(gòu)建 RESTful 服務(wù)。
    的頭像 發(fā)表于 07-28 15:59 ?831次閱讀

    使用vRealize Automation REST API置備虛擬機(jī)以進(jìn)行軟件開(kāi)發(fā)

      本文面向希望使用 vRealize Automation REST API 以編程方式配置和管理 vRealize Automation 的軟件工程師和應(yīng)用程序開(kāi)發(fā)人員。本文還提供了用于置備藍(lán)圖
    的頭像 發(fā)表于 12-02 11:34 ?656次閱讀

    使用REST框架控制您的MKR1000

    電子發(fā)燒友網(wǎng)站提供《使用REST框架控制您的MKR1000.zip》資料免費(fèi)下載
    發(fā)表于 12-12 11:21 ?0次下載
    使用<b class='flag-5'>REST</b>框架控制您的MKR1000

    使用REST使用Meadow和MAUI遠(yuǎn)程控制伺服

    電子發(fā)燒友網(wǎng)站提供《使用REST使用Meadow和MAUI遠(yuǎn)程控制伺服.zip》資料免費(fèi)下載
    發(fā)表于 01-31 09:26 ?0次下載
    使用<b class='flag-5'>REST</b>使用Meadow和MAUI遠(yuǎn)程控制伺服

    具有HTTP Rest通信的機(jī)器人(Pytobot)

    電子發(fā)燒友網(wǎng)站提供《具有HTTP Rest通信的機(jī)器人(Pytobot).zip》資料免費(fèi)下載
    發(fā)表于 06-14 11:11 ?0次下載
    具有HTTP <b class='flag-5'>Rest</b>通信的機(jī)器人(Pytobot)

    SANnav管理門(mén)戶 REST API參考手冊(cè)

    電子發(fā)燒友網(wǎng)站提供《SANnav管理門(mén)戶 REST API參考手冊(cè).pdf》資料免費(fèi)下載
    發(fā)表于 09-01 15:02 ?0次下載
    SANnav管理門(mén)戶 <b class='flag-5'>REST</b> API參考手冊(cè)

    IDEA REST Client使用教程

    REST真香之前,postman(chrome的一款插件)確實(shí)是一個(gè)非常不錯(cuò)的選擇,具有完備的REST Client功能和請(qǐng)求歷史記錄功能。但是當(dāng)使用了IDEA REST之后,postman就可以丟了,因?yàn)?/div>
    的頭像 發(fā)表于 09-24 14:56 ?888次閱讀
    IDEA <b class='flag-5'>REST</b> Client使用教程

    REST的6大指導(dǎo)原則

    1. 前言 REST 全稱為 :Resource Representational State Transfer. 是一種分布式超媒體系統(tǒng)( distributed hypermedia
    的頭像 發(fā)表于 10-09 14:27 ?1278次閱讀

    使用 Splashtop REST API 簡(jiǎn)化遠(yuǎn)程管理

    Splashtop 的 REST API 可無(wú)縫提高生產(chǎn)力和簡(jiǎn)化集成,使用戶能夠自動(dòng)執(zhí)行任務(wù)、定制工作流程,輕松與現(xiàn)有系統(tǒng)集成。
    的頭像 發(fā)表于 06-25 17:28 ?178次閱讀
    使用 Splashtop <b class='flag-5'>REST</b> API 簡(jiǎn)化遠(yuǎn)程管理