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

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

3天内不再提示

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

「Spring」认证安全架构 来源:「Spring」认证安全架构 作者:「Spring」认证安全 2022-07-28 16:01 次阅读

书接上文⬆⬆⬆

是什么让一些东西变得 RESTful?

到目前为止,您拥有一个基于 Web 服务来处理涉及员工数据的核心操作。但这还不足以让事情变得“RESTful”。

  • 漂亮的 URL/employees/3不是 REST。
  • 仅使用GET,POST等不是 REST。
  • 安排好所有的 CRUD 操作不当 REST。

事实上,到目前为止,我们构建的更好地描述为RPC远程过程调用)。那是因为没有办法知道如何与这个服务器交互。如果您今天发布了此内容,您还必须编写文档或在某个地方托管开发人员的门户,其中包含所有详细信息

Roy Fielding 的这一陈述可能会进一步为RESTRPC之间的区别提供线索:

我对将任何基于 HTTP 的接口称为 REST API 的人数感到沮丧。今天的例子是 SocialSite REST API。那就是RPC。它尖叫 RPC。展示的耦合太多了,应该给它一个 X 评级。

要做些什么来使用 REST 架构风格清楚地认识到超文本是一种约束?换句话说,应用程序状态引擎(以及 API)不是由超文本驱动的,那么它就不能是 RESTful 并且不能是 REST API。时期。是否有一些损坏的手册需要修复?

— 罗伊菲尔丁

https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven

在我们的表示中不包括超媒体的副作用是客户端必须硬编码 URI 来导航 API。这导致了与网络电子商务兴起之前相同的脆弱性。这表明我们的 JSON 输出需要一点帮助。

介绍Spring HATEOAS,这是一个 Spring 项目,旨在帮助您编写超媒体驱动的输出。要将您的服务升级为 RESTful,请将其添加到您的构建中:

将 Spring HATEOAS 添加dependencies到pom.xml

org.springframework.bootspring-boot-starter-hateoas复制

这个小型库将为我们提供定义 RESTful 服务的结构,然后以可接受的格式呈现它以供客户使用。

任何 RESTful 服务的一个关键要素是添加指向相关操作的链接。要使您的控制器更加 RESTful,请添加如下链接:

获取单个项目的资源

@GetMapping("/employees/{id}")EntityModel one(@PathVariable Long id) {  Employee employee = repository.findById(id) //      .orElseThrow(() -> new EmployeeNotFoundException(id));  return EntityModel.of(employee, //      linkTo(methodOn(EmployeeController.class).one(id)).withSelfRel(),      linkTo(methodOn(EmployeeController.class).all()).withRel("employees"));}

教程基于 Spring MVC 并使用静态辅助方法WebMvcLinkBuilder来构建这些链接。如果您在项目中使用 Spring WebFlux,则必须改用WebFluxLinkBuilder.

这与我们之前的情况非常相似,但有一些变化:

  • 该方法的返回类型已从 更改EmployeeEntityModelEntityModel是来自 Spring HATEOAS 的通用容器,它不仅包含数据,还包含链接集合。
  • linkTo(methodOn(EmployeeController.class).one(id)).withSelfRel()要求 Spring HATEOAS 建立到EmployeeController'one()方法的链接,并将其标记为自链接。
  • linkTo(methodOn(EmployeeController.class).all()).withRel("employees")要求 Spring HATEOAS 建立到聚合根的链接all(),并将其称为“员工”。

“建立链接”是什么意思?Spring HATEOAS 的核心类型之一是Link. 它包括一个URI和一个rel(关系)。链接是赋予网络权力的东西。在万维网之前,其他文档系统会呈现信息或链接,但正是将文档与这种关系元数据链接在一起,才将网络缝合在一起。

Roy Fielding 鼓励使用使 Web 成功的相同技术构建 API,链接就是其中之一。

如果您重新启动应用程序并查询Bilbo的员工记录,您将得到与之前略有不同的响应:

冰壶更漂亮

当你的 curl 输出变得更复杂时,它可能变得难以阅读。使用这个或其他技巧来美化 curl 返回的 json:

# 指示部分将输出通过管道传输到 json_pp 并要求它使您的 JSON 更漂亮。(或者使用任何你喜欢的工具!)

# v------------------v

curl -v localhost:8080/employees/1 | json_pp

单个员工的 RESTful 表示

{

"id": 1,

"name": "Bilbo Baggins",

"role": "burglar",

"_links": {

"self": {

"href": "http://localhost:8080/employees/1"

},

"employees": {

"href": "http://localhost:8080/employees"

}

}

}

这个解压缩的输出不仅显示了您之前看到的数据元素(idnamerole,而且还显示了一个_links包含两个 URI 的条目。整个文档使用HAL进行格式化。

HAL 是一种轻量级媒体类型,它不仅可以编码数据,还可以编码超媒体控件,提醒消费者注意他们可以导航的 API 的其他部分。在这种情况下,有一个“自我”链接(有点像this代码中的语句)以及一个返回聚合根的链接。

为了使聚合根 ALSO 更加 RESTful,您希望包括顶级链接,同时还包括其中的任何 RESTful 组件。

所以我们把这个

获取聚合根

@GetMapping("/employees")List all() {  return repository.findAll();}

进入这个

获取聚合根

@GetMapping("/employees")CollectionModel> all() {  List> employees = repository.findAll().stream()      .map(employee -> EntityModel.of(employee,          linkTo(methodOn(EmployeeController.class).one(employee.getId())).withSelfRel(),          linkTo(methodOn(EmployeeController.class).all()).withRel("employees")))      .collect(Collectors.toList());  return CollectionModel.of(employees, linkTo(methodOn(EmployeeController.class).all()).withSelfRel());}

哇!曾经的那个方法,repository.findAll()都长大了!不用担心。让我们打开它。

CollectionModel<>是另一个 Spring HATEOAS 容器;它旨在封装资源集合,而不是像EntityModel<>之前那样封装单个资源实体。CollectionModel<>,也可以让您包含链接。

不要让第一个声明溜走。“封装集合”是什么意思?员工收藏?

不完全的。

由于我们谈论的是 REST,它应该封装员工资源的集合。

这就是为什么您获取所有员工,然后将它们转换为EntityModel对象列表的原因。(感谢 Java 8 流!)

如果您重新启动应用程序并获取聚合根,您可以看到它现在的样子。

员工资源集合的 RESTful 表示

{  "_embedded": {    "employeeList": [      {        "id": 1,        "name": "Bilbo Baggins",        "role": "burglar",        "_links": {          "self": {            "href": "http://localhost:8080/employees/1"          },          "employees": {            "href": "http://localhost:8080/employees"          }        }      },      {        "id": 2,        "name": "Frodo Baggins",        "role": "thief",        "_links": {          "self": {            "href": "http://localhost:8080/employees/2"          },          "employees": {            "href": "http://localhost:8080/employees"          }        }      }    ]  },  "_links": {    "self": {      "href": "http://localhost:8080/employees"    }  }}复制

对于提供员工资源集合的聚合根,有一个顶级“自我”链接。“集合”列在“_embedded”部分下方;这就是 HAL 表示集合的方式。

并且集合的每个单独成员都有他们的信息以及相关链接。

添加所有这些链接有什么意义?它使得随着时间的推移发展 REST 服务成为可能。可以维护现有链接,而将来可以添加新链接。新客户可以利用新链接,而旧客户可以在旧链接上维持自己的生命。如果服务被重新定位和移动,这将特别有用。只要保持链接结构,客户端仍然可以找到事物并与之交互。

简化链接创建

在前面的代码中,您是否注意到单个员工链接创建中的重复?为员工提供单个链接以及创建到聚合根的“员工”链接的代码显示了两次。如果这引起了您的关注,很好!有一个解决方案。

简单地说,你需要定义一个将Employee对象转换为EntityModel对象的函数。虽然您可以轻松地自己编写此方法,但在实现 Spring HATEOAS 的
RepresentationModelAssembler接口的道路上也有好处——它将为您完成工作。

进化
/src/main/java/payroll/EmployeeModelAssembler.java

package payroll;import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;import org.springframework.hateoas.EntityModel;import org.springframework.hateoas.server.RepresentationModelAssembler;import org.springframework.stereotype.Component;@Componentclass EmployeeModelAssembler implements RepresentationModelAssembler> {  @Override  public EntityModel toModel(Employee employee) {    return EntityModel.of(employee, //        linkTo(methodOn(EmployeeController.class).one(employee.getId())).withSelfRel(),        linkTo(methodOn(EmployeeController.class).all()).withRel("employees"));  }}复制,>

这个简单的接口有一个方法:toModel(). 它基于将非模型对象 ( Employee) 转换为基于模型的对象 ( EntityModel)。

您之前在控制器中看到的所有代码都可以移到此类中。并且通过应用 Spring Framework 的@Component注解,将在应用程序启动时自动创建汇编程序。

Spring HATEOAS 的所有模型的抽象基类是RepresentationModel. 但是为了简单起见,我建议使用EntityModel作为您的机制来轻松地将所有 POJO 包装为模型。

要利用此汇编器,您只需EmployeeController通过在构造函数中注入汇编器来更改 。

将 EmployeeModelAssembler 注入控制器

@RestControllerclass EmployeeController {  private final EmployeeRepository repository;  private final EmployeeModelAssembler assembler;  EmployeeController(EmployeeRepository repository, EmployeeModelAssembler assembler) {    this.repository = repository;    this.assembler = assembler;  }  ...}

从这里,您可以在单项员工方法中使用该汇编程序:

使用汇编程序获取单项资源

@GetMapping("/employees/{id}")EntityModel one(@PathVariable Long id) {  Employee employee = repository.findById(id) //      .orElseThrow(() -> new EmployeeNotFoundException(id));  return assembler.toModel(employee);}

这段代码几乎是一样的,除了不是在EntityModel这里创建实例,而是将它委托给汇编器。也许这看起来并不多。

在聚合根控制器方法中应用相同的东西更令人印象深刻:

使用汇编程序获取聚合根资源

@GetMapping("/employees")CollectionModel> all() {  List> employees = repository.findAll().stream() //      .map(assembler::toModel) //      .collect(Collectors.toList());  return CollectionModel.of(employees, linkTo(methodOn(EmployeeController.class).all()).withSelfRel());}

同样,代码几乎相同,但是您可以将所有EntityModel创建逻辑替换为map(assembler::toModel). 由于 Java 8 方法引用,插入它并简化您的控制器非常容易。

Spring HATEOAS 的一个关键设计目标是让 The Right Thing™ 变得更容易。在这种情况下:将超媒体添加到您的服务中,而无需对事物进行硬编码。

在这个阶段,您已经创建了一个实际生成超媒体驱动内容的 Spring MVC REST 控制器!不讲 HAL 的客户端可以在使用纯数据时忽略额外的位。使用 HAL 的客户可以浏览您授权的 API。

但这并不是使用 Spring 构建真正的 RESTful 服务所需的唯一内容。

......未完待续......

审核编辑:汤梓红

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

    关注

    0

    文章

    333

    浏览量

    14161
  • REST
    +关注

    关注

    0

    文章

    32

    浏览量

    9358
收藏 人收藏

    评论

    相关推荐

    什么是java spring

    的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring是一个容器,它包含并且管理系统对象
    发表于 09-11 11:16

    如何用ACM简化你的Spring Cloud微服务环境配置管理

    摘要: 本文我们就如何使用阿里云ACM这样的配置管理产品在Spring Cloud中替代Spring Cloud Config帮助简化环境配置管理做一个简单的示例,帮助你理解基于ACM来简化微服务
    发表于 02-02 14:18

    EDAS再升级!全面支持Spring Cloud应用

    摘要: 近日,阿里中间件(Aliware)的企业级分布式应用服务EDAS宣布再次升级,全面支持Spring Cloud应用。点此查看原文:[url=]http://click.aliyun.com
    发表于 02-02 15:20

    使用阿里云ACM简化你的Spring Cloud微服务环境配置管理

    摘要: 本文我们就如何使用阿里云ACM这样的配置管理产品在Spring Cloud中替代Spring Cloud Config帮助简化环境配置管理做一个简单的示例,帮助你理解基于ACM来简化微服务
    发表于 07-04 17:16

    启动Spring Boot项目应用的种方法

    基础。我们知道了Spring Boot是个什么了,那么我们又该如何启动Spring Boot应用呢?这里小编给大家推荐常用的种方法。分别是IDEA编辑器启动、命令启动、java命令jar文件启动。下面
    发表于 01-14 17:33

    Spring Boot嵌入式Web容器原理是什么

    Spring Boot嵌入式Web容器原理Spring Boot的目标是构建“非常容易创建、独立、产品级别的基于Spring的应用”。这些应用是“立即可运行的”。在这个过程中,完全没有
    发表于 12-16 07:57

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

    REST端口支持构建动态REST请求来使用RESTful API网络服务。 概览 REST端口暴露了一个简单的接口来为
    的头像 发表于 01-17 09:11 4394次阅读

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

    /服务器) 模型对资源进行增删改查操作。而其中客户端和服务器是分离的,而知行之桥中的REST端口就是作为REST API中的客户端,对服务
    的头像 发表于 02-17 18:00 7891次阅读
    <b class='flag-5'>REST</b> API是什么,如何使用<b class='flag-5'>REST</b>端口

    Spring REST Docs RESTful服务文档

    ./oschina_soft/spring-restdocs.zip
    发表于 05-24 09:31 1次下载
    <b class='flag-5'>Spring</b> <b class='flag-5'>REST</b> Docs RESTful<b class='flag-5'>服务</b>文档

    Spring认证是什么?

    ,例如:配置、组件扫描、AOP、数据访问和事务、REST、安全、自动配置、执行器、 Spring boot测试等。
    的头像 发表于 07-04 10:19 1080次阅读
    <b class='flag-5'>Spring</b>认证是什么?

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

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

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

    要使用 Web 层次包装您的存储库,您必须使用 Spring MVC。多亏了 Spring Boot,代码基础设施很少。相反,我们可以专注于行动。
    的头像 发表于 07-28 16:00 551次阅读

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

    通过一个额外的库和几行额外的代码,您已将超媒体添加到您的应用程序中。但这并不是使您的服务成为 RESTful 所需的唯一事情。REST 的一个重要方面是它既不是技术堆栈也不是单一标准。
    的头像 发表于 07-28 16:02 515次阅读

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

    到目前为止,您已经使用基本链接构建了一个可进化的 API。为了发展您的 API 并更好地为您的客户服务,您需要接受超媒体作为应用程序状态引擎的概念。
    的头像 发表于 07-28 16:03 595次阅读

    Spring Cloud :打造可扩展的微服务网关

    Spring Cloud Gateway是一个基于Spring Framework 5和Project Reactor的反应式编程模型的微服务网关。它提供了丰富的功能,包括动态路由、请求限流、集成安全性等,使其成为
    的头像 发表于 10-22 10:03 261次阅读
    <b class='flag-5'>Spring</b> Cloud :打造可扩展的微<b class='flag-5'>服务</b>网关