0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

OSC开源社区 来源:OSC开源社区 2023-01-17 10:14 次阅读

说起 REST API,小伙伴们多多少少都有听说过,但是如果让你详细介绍一下什么是 REST,估计会有很多人讲不出来,或者只讲出来其中一部分。

今天松哥就来和大家一起来聊一聊到底什么是 REST,顺便再来看下 Spring HATEOAS 的用法。

1. REST 成熟模型

首先关于 REST,有一个大佬 Leonard Richardson 为 REST 定义了一个成熟度模型,他一共定义了四个不同的层次,分别如下:

Level0:Web 服务单纯的使用 HTTP 作为数据传输方式,本质上就是远程方法调用,常见的 SOAP 和 RPC 基本上都属于这一类。

Level1:在这一级别上,引入了资源的概念,服务端的每一个资源,都有一个对应的操作地址。

Level2:在这一级别上,我们引入了不同的 HTTP 请求方法来描述不同的操作,例如 GET 表示查询、POST 表示插入、PUT 表示更新、DELETE 表示删除,并且使用 HTTP 的状态码来表示不同的响应结果。一般来说,大家在日常的接口开发中,基本上都能做到这一层级。但是这还不是最佳结果。

Level3:按照 Leonard Richardson 的意思,这一层级的 REST 基于 HATEOAS(Hypertext As The Engine Of Application State),在这一级别上,除了返回资源的 JSON 之外,还会额外返回一组 Link,这组 Link 描述了对于该资源可以做哪些操作,以及具体的该怎么做。

在日常的开发中,我们一般都是只实现到 Level2 这一层级,真正做到 Level3 的估计很少,不过虽然在工作中一般不会做到 Level3 这一层级,但是,我相信很多小伙伴应该是见过 Level3 层级的 REST 是啥样子的,特别是看过 vhr 视频的小伙伴,松哥在其中讲过,通过 Spring Data Jpa+Spring Rest Repositories 实现的 CURD 接口,其实就是一个达到了 Level3 层级的 REST。

2. Spring HATEOAS

那么接下来我先用 Spring HATEOAS 写一个简单的 REST,然后结合这个案例来和小伙伴们聊一聊到底 Spring HATEOAS 有何不一样的地方。

首先我们创建一个 Spring Boot 工程,引入 Web 和 Spring HATEOAS 依赖,如下:

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

创建好之后,我们首先创建一个 User 实体类:

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

注意这个 User 实体类需要继承自 RepresentationModel,以方便后续添加不同的 Link(以前旧的版本需要继承自 ResourceSupport)。

接下来写一个简单的测试接口。

查询所有用户:

@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;
}
}

关于这个接口,我来说几点:

首先,对于这种返回一个集合或者数组的情况,返回的类型都是 CollectionModel。

把集合弄好之后(正常应该去数据库中查询,我这里省事直接创建了),通过 CollectionModel.of(list) 方法去获取一个 CollectionModel 对象。

对于每一个 user 对象,我都添加了一个 Link 对象,WebMvcLinkBuilder.linkTo(UserController.class).slash(u1.getId()).withSelfRel() 表示生成当前对象的访问链接。

WebMvcLinkBuilder.linkTo(UserController.class).withRel("users") 表示访问所有数据的链接。

好了,这个接口写完之后,我们访问看下:

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

可以看到,返回的每一个 user 对象中,都有一个链接表示如何单独访问这个对象。最下面还有一个访问所有对象的链接。

对于上面这个案例,可能有小伙伴会质疑,难道我们从数据库中查询出来的 List 集合都要遍历一遍,然后给每一个 User 添加一个 Link 吗?其实不必,添加 Link 这个事可以直接在 User 类中完成,如下:

publicclassUserextendsRepresentationModel{
privateIntegerid;
privateStringusername;
privateStringaddress;

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

可以看到,直接在构造方法中完成即可。此时接口里就不用那么复杂了,如下:

@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;
}

那么对于根据 ID 来查询用户的需求,我们也应该给一个接口如下:

@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);
}
}

关于这个接口,我说如下几点:

如果返回类型是一个对象的话,需要使用 EntityModel 类型。

搞好返回的对象之后,通过 EntityModel.of(u) 方法可以获取到目标数据类型。

这个地方,为了给小伙伴们演示不同的 Link 添加方式,我写了好多个(单纯为了演示不同的 Link 添加方式):

Link.of("http://localhost:8080/users/"+id, "getOne") 这种是自己纯手工去生成当前对象的访问链接,很明显这不是一个很好的方案。当前对象的访问链接建议使用上文中提到的方式。

WebMvcLinkBuilder.linkTo(UserController.class).withRel("users") 这个是生成当前这个 Controller 的访问链接,一般就是访问所有用户对象的链接。

WebMvcLinkBuilder.linkTo(UserController.class).slash(u.getId()).withSelfRel() 前文已经用过了,不多说了,实际应用中建议使用这种。

也可以根据某一个方法自动生成,像这样 WebMvcLinkBuilder.linkTo(method, id).withSelfRel(),这个是生成某一个具体方法的访问链接。

好了,现在我们来看下这个接口生成的 JSON,如下:

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

生成的这段 JSON 我将之标记为了三部分:

第一部分,self,就是自身的访问链接,这三个链接分别是 User 的构造方法,以及前面提到的 3.3 和 3.4 的方法生成的。

第二部分,getOne 这个,是前面 3.1 中提到的方法生成的。

第三部分,users 这个,是前面提到的 3.2 方法生成的。

当然,其实这块还有很多其他的生成链接的玩法,但是我就不一一介绍了,小伙伴们可以参考官方文档:

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

从上面 Spring HATEOAS 中返回的 JSON 我们大致上可以看到它的特点:

当我们使用了 Spring HATEOAS,此时,客户端就会通过服务端返回的 Link Rel 来获取请求的 URI(如果没有使用 Spring HATEOAS,则客户端访问的 URI 都是提前在客户端硬编码的),现在我们就可以做到服务端在不破坏客户端实现的情况下动态的完成 URI 的修改,从而进一步解耦客户端和服务端。

简而言之,现在客户端能干什么事情,在服务端返回的 JSON 中都会告诉客户端,客户端从服务端返回的 JSON 中获取到请求的 URL,然后直接执行即可。如果这个请求地址发生变化的话,客户端也会及时拿到最新的地址。

可能上面的例子小伙伴们感受还不是很明显,我再给大家看一段 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"
}
}
}

这是电商系统下单之后等待支付的过程中返回的 JSON,这里的 links 给出了三个:

self:访问这个链接可以查看当前订单信息(GET 请求)。

cancel:访问这个链接可以取消当前订单(DELETE 请求)。

payment:访问这个链接可以支付当前订单(POST 请求)。

这个例子就很直白了,就是在返回的 JSON 中,直接告诉你接下来能做哪些操作,对应的 URL 分别是什么,前端拿到之后直接操作,如果这些操作路径发生了变化,前端也会立马拿到最新的路径。

这就是 Spring HATEOAS 的好处。总之一句话,Spring HATEOAS 提倡在响应返回的 Link 中给出对该资源接下来操作的 URL。这种方式解耦了服务端 URI,也可以让客户端开发者更容易地探索 API。

3. REST 的优缺点

虽然我们现在都鼓励设计 REST 风格的 API,然而 REST 也不全是优点,事物总是具有两面性,REST 的优缺点分别如下。

3.1 优点

首先,REST 足够简单,有一定 Web 开发经验的小伙伴都可以快速上手 REST。

REST 风格的接口测试起来也非常方便,利用浏览器自带的一些 REST 插件或者是 POSTMAN 之类的工具,就可以非常方便的实现 REST 接口的测试。

不需要中间代理,简化了系统的结构。

HTTP 对防火墙比较友好。

3.2 缺点

REST 只支持请求-响应通信方法,不支持服务端推送消息到客户端。

给请求取一个合适的名字比较困难,特别是有多个相类似的接口时,例如有多个添加接口、多个更新接口等。

由于没有中间代理,所以请求/响应的时候,服务端和客户端都必须在线。

好啦,跟小伙伴们聊了 REST 和 Spring HATEOAS,感兴趣的小伙伴可以去试试哦~

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接口
    +关注

    关注

    33

    文章

    8474

    浏览量

    150772
  • API
    API
    +关注

    关注

    2

    文章

    1481

    浏览量

    61776
  • 模型
    +关注

    关注

    1

    文章

    3140

    浏览量

    48673
  • spring
    +关注

    关注

    0

    文章

    338

    浏览量

    14299
  • REST
    +关注

    关注

    0

    文章

    32

    浏览量

    9395

原文标题:到底什么样的REST才是最佳REST?

文章出处:【微信号:OSC开源社区,微信公众号:OSC开源社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    War3Rest.exe下载

    War3Rest.exe
    发表于 10-04 07:46 0次下载
    War3<b class='flag-5'>Rest</b>.exe下载

    REST学习

    学习REST必备
    发表于 07-05 15:22 15次下载

    REST、gRPC、GraphQL及WebHook的对比和选型

    首先REST--Resource Representational State Transfer, 中文直译就是资源在网络中以某种表现形式进行状态转移。
    发表于 06-13 10:34 2449次阅读

    REST端口支持构建动态REST请求来使用RESTful API网络

    REST端口支持构建动态REST请求来使用RESTful API网络服务。 概览 REST端口暴露了一个简单的接口来为REST请求构建头、授权、主体和HTTP方法。请求体可以在端口配置
    的头像 发表于 01-17 09:11 4780次阅读

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

    API是Application Programming Interface(应用程序接口)的缩写,它是拿来描述一个类库的特征或是如何去运用它。按照目前比较主流的分法,可以分为REST API和非
    的头像 发表于 02-17 18:00 9173次阅读
    <b class='flag-5'>REST</b> API是什么,如何使用<b class='flag-5'>REST</b>端口

    yii2-rest-rbac Yii2权限管理RBAC rest接口

    ./oschina_soft/gitee-yii2-rest-rbac.zip
    发表于 06-30 09:35 0次下载
    yii2-<b class='flag-5'>rest</b>-rbac Yii2权限管理RBAC <b class='flag-5'>rest</b>接口

    如何使用Spring构建REST服务(一)

    关于 REST 如何适应微服务世界还有一个更大的讨论,但是——对于本教程——让我们看看构建 RESTful 服务。
    的头像 发表于 07-28 15:59 885次阅读

    使用vRealize Automation REST API置备虚拟机以进行软件开发

      本文面向希望使用 vRealize Automation REST API 以编程方式配置和管理 vRealize Automation 的软件工程师和应用程序开发人员。本文还提供了用于置备蓝图
    的头像 发表于 12-02 11:34 720次阅读

    使用REST框架控制您的MKR1000

    电子发烧友网站提供《使用REST框架控制您的MKR1000.zip》资料免费下载
    发表于 12-12 11:21 0次下载
    使用<b class='flag-5'>REST</b>框架控制您的MKR1000

    使用REST使用Meadow和MAUI远程控制伺服

    电子发烧友网站提供《使用REST使用Meadow和MAUI远程控制伺服.zip》资料免费下载
    发表于 01-31 09:26 0次下载
    使用<b class='flag-5'>REST</b>使用Meadow和MAUI远程控制伺服

    具有HTTP Rest通信的机器人(Pytobot)

    电子发烧友网站提供《具有HTTP Rest通信的机器人(Pytobot).zip》资料免费下载
    发表于 06-14 11:11 0次下载
    具有HTTP <b class='flag-5'>Rest</b>通信的机器人(Pytobot)

    SANnav管理门户 REST API参考手册

    电子发烧友网站提供《SANnav管理门户 REST API参考手册.pdf》资料免费下载
    发表于 09-01 15:02 0次下载
    SANnav管理门户 <b class='flag-5'>REST</b> API参考手册

    IDEA REST Client使用教程

    REST真香之前,postman(chrome的一款插件)确实是一个非常不错的选择,具有完备的REST Client功能和请求历史记录功能。但是当使用了IDEA REST之后,postman就可以丢了,因为
    的头像 发表于 09-24 14:56 994次阅读
    IDEA <b class='flag-5'>REST</b> Client使用教程

    REST的6大指导原则

    1. 前言 REST 全称为 :Resource Representational State Transfer. 是一种分布式超媒体系统( distributed hypermedia
    的头像 发表于 10-09 14:27 1477次阅读

    使用 Splashtop REST API 简化远程管理

    Splashtop 的 REST API 可无缝提高生产力和简化集成,使用户能够自动执行任务、定制工作流程,轻松与现有系统集成。
    的头像 发表于 06-25 17:28 249次阅读
    使用 Splashtop <b class='flag-5'>REST</b> API 简化远程管理