说起RESTAPI,小伙伴们或多或少都听说过,但是如果让你详细介绍一下REST是什么,估计很多人都说不清楚,或者只能说到一部分.今天宋哥就来和大家聊一聊什么是REST,顺便看看SpringHATEOAS的用法。1.REST成熟度模型首先,关于REST,大佬LeonardRichardson给REST定义了一个成熟度模型。他一共定义了四个不同的层次,如下:Level0:Web服务简单的使用HTTP作为数据传输方式,本质上是一种远程方法调用,常见的SOAP和RPC基本属于这一类。Level1:这一层引入了资源的概念,服务器端的每一个资源都有对应的操作地址。Level2:在这一层,我们引入了不同的HTTP请求方式来描述不同的操作,比如GET表示查询,POST表示插入,PUT表示更新,DELETE表示删除,用HTTP状态码来表示不同的响应结果。一般来说,大家在日常的界面开发中,基本都能做到这个程度。但这并不是最好的结果。Level3:根据LeonardRichardson的说法,这一级别的REST基于HATEOAS(HypertextAsTheEngineOfApplicationState)。在此级别,除了返回资源的JSON之外,还将返回一组额外的链接。这组链接描述了可以对该资源进行哪些操作,以及如何进行操作。在日常开发中,我们一般只做到Level2,Level3估计很少能做到。不过,虽然我们在工作中一般都没有做到Level3,但是相信很多小伙伴应该都看过Level3级别的REST是什么样子的,尤其是看过vhr视频的小伙伴们。宋大哥在里面说通过SpringDataJpa+SpringRestRepositories实现的CURD接口其实是Level3级别的。休息。2.SpringHATEOAS下面我将使用SpringHATEOAS写一个简单的REST,然后通过这个案例和小伙伴们聊聊SpringHATEOAS的区别。首先,我们创建一个SpringBoot项目,引入Web和SpringHATEOAS依赖,如下:创建完成后,我们先创建一个User实体类:publicclassUserextendsRepresentationModel{privateIntegerid;私有字符串用户名;私有字符串地址;//省略getter/setter}注意这个User实体类需要继承自RepresentationModel,方便后续添加不同的Links(旧版本需要继承自ResourceSupport)。接下来写一个简单的测试界面。查询所有用户:@RestController@RequestMapping("/users")publicclassUserController{@GetMappingpublicCollectionModellist(){Listlist=newArrayList<>();用户u1=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);用户u2=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"));回访用户;}}关于这个接口,我说几点:首先,对于返回集合或者数组的情况,返回的类型是IsCollecmotionModel采集完成后(正常情况下应该去数据库查询,为了省事我这里直接创建了),使用CollectionModel.of(list)方法获取一个CollectionModel对象。对于每个用户对象,我都添加了一个Link对象,WebMvcLinkBuilder.linkTo(UserController.class).slash(u1.getId()).withSelfRel()的意思是生成当前对象的访问链接。WebMvcLinkBuilder.linkTo(UserController.class).withRel("users")表示访问所有数据的链接。好了,写完这个接口,我们来访问一下:可以看到在每个返回的用户对象中,都有一个链接指示如何单独访问这个对象。底部还有一个链接可以访问所有对象。对于上面的情况,可能有朋友会疑问,难道我们要遍历从数据库中查询到的List集合,然后给每个User添加一个Link吗?其实大可不必。添加Link可以直接在User类中完成,如下:publicclassUserextendsRepresentationModel{privateIntegerid;私有字符串用户名;私有字符串地址;publicUser(Integerid){super(WebMvcLinkBuilder.linkTo(UserController.class).slash(id).withSelfRel());这个.id=id;}//省略getter/setter}可以看到可以直接在构造方法中完成。这时候接口就不需要那么复杂了,如下:@GetMappingpublicCollectionModellist(){Listlist=newArrayList<>();用户u1=新用户(1);u1.setUsername("javaboy");u1.setAddress("www.javaboy.org");list.add(u1);用户u2=新用户(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;}then对于根据ID查询用户的需求,我们也应该给一个接口如下:@RestController@RequestMapping("/users")publicclassUserController{@GetMapping("/{id}")publicEntityModelgetOne(@PathVariableIntegerid)抛出NoSuchMethodException{Useru=newUser(id);u.setUsername("javaboy");u.setAddress("深圳");u.add(Link.of("http://localhost:8080/使用rs/"+id,"getOne"));Linkusers=WebMvcLinkBuilder.linkTo(UserController.class).withRel("users");u.add(users);链接link=WebMvcLinkBuilder.linkTo(UserController.class).斜杠(u.getId()).withSelfRel();u.add(链接);方法方法=UserController.class.getMethod("getOne",Integer.class);链接link2=WebMvcLinkBuilder.linkTo(方法,id)。withSelfRel();u.add(link2);returnEntityModel.of(u);}}关于这个接口,我说下:如果返回类型是对象,则需要使用EntityModel类型来制作return对象后,可以通过EntityModel.of(u)方法获取目标数据类型,在这个地方,为了给小伙伴们演示添加Link的不同方式,写了几个(只是为了演示不同的方式)addingLink):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,如下:我把生成的json标记为三部分:第一部分self,是自己的访问链接,这三个链接就是User的构造方法,生成通过上述方法3.3和3.4。第二部分getOne是通过上面3.1中提到的方法生成的。第三部分,users,通过前面提到的3.2方法生成。当然,这方面还有很多其他的生成链接的方法,这里就不一一介绍了。朋友们可以参考官方文档:docs.spring.io/spring-hate...上面SpringHATEOAS返回的JSON我们可以大致看出它的特点:当我们使用SpringHATEOAS时,此时客户端会通过服务端返回的LinkRel(如果不使用SpringHATEOAS,客户端访问的URI都是提前在客户端硬编码的),现在我们可以让服务端动态修改URI而不破坏客户端的实现,从而进一步解耦客户端和服务器。简而言之,客户端现在可以做什么将在服务器返回的JSON中告知。客户端可以从服务器返回的JSON中获取请求的URL,直接执行。如果请求地址发生变化,客户端也会及时获取到最新的地址。也许上面的例子对你来说不是很明显,让我再给你看一段JSON:{"tracking_id":"666","status":"WAIT_PAYMENT","items":[{"name":"book","数量":1}],"_Links":{"self":{"href":"http://localhost:8080/orders/666"},"取消":{"href":"http://localhost:8080/orders/666"},"payment":{"href":"http://localhost:8080/orders/666/payments"}}}这是电商系统等待付款后下订单过程中返回的JSON中,这里给出了三个链接:self:访问这个链接可以查看当前订单信息(GET请求)。取消:访问此链接以取消当前订单(DELETE请求)。付款:访问此链接以支付当前订单(POST请求)。这个例子很直接,就是在返回的json中,直接告诉你接下来可以做什么操作,对应的url是什么,前端收到后就可以直接操作了。如果这些操作路径发生变化,前端也会立即获取最新的路径。这就是SpringHATEOAS的好处。总之,SpringHATEOAS提倡在响应返回的Link中给出下一步操作该资源的URL。这种方法解耦了服务器URI,使客户端开发人员更容易探索API。3.REST的优点和缺点虽然我们现在鼓励设计REST风格的API,但是REST并不是所有的优点。事物总是有两个方面的。REST的优点和缺点如下。3.1优点首先REST足够简单,有一定Web开发经验的朋友可以很快上手REST。REST风格的界面也非常方便测试。使用浏览器自带的一些REST插件或者POSTMAN等工具,可以很方便的实现REST接口的测试。不需要中间代理,简化了系统结构。HTTP对防火墙友好。3.2缺点REST只支持请求-响应的通信方式,不支持服务端向客户端推送消息。很难为请求选择一个合适的名称,尤其是在有多个相似接口的情况下,比如多个添加接口,多个更新接口等。由于没有中间代理,服务器和客户端在请求/响应时都必须在线。