2022就业季|Spring认证教你,如何使用 Spring 构建 REST 服务(四)

简介: 2022就业季|Spring认证教你,如何使用 Spring 构建 REST 服务(四)

书接上文⬆⬆⬆

不断发展的 REST API
通过一个额外的库和几行额外的代码,您已将超媒体添加到您的应用程序中。但这并不是使您的服务成为 RESTful 所需的唯一事情。REST 的一个重要方面是它既不是技术堆栈也不是单一标准。

REST 是架构约束的集合,采用这些约束会使您的应用程序更具弹性。弹性的一个关键因素是,当您对服务进行升级时,您的客户不会遭受停机时间的困扰。

在“过去”的日子里,升级因破坏客户端而臭名昭著。换句话说,升级到服务器需要更新客户端。在当今时代,花费数小时甚至数分钟进行升级的停机时间可能会造成数百万美元的收入损失。

有些公司要求您向管理层提出一个计划,以尽量减少停机时间。过去,您可以在周日凌晨 2:00 进行升级,此时负载最低。但在今天的基于互联网的电子商务中,国际客户在其他时区,这样的策略就没有那么有效了。

基于 SOAP 的服务和基于CORBA 的服务非常脆弱。很难推出可以同时支持新旧客户端的服务器。使用基于 REST 的实践,这要容易得多。特别是使用 Spring 堆栈。

支持对 API 的更改
想象一下这个设计问题:您已经推出了一个具有Employee基于此记录的系统。该系统大受欢迎。你已经把你的系统卖给了无数的企业。突然,需要拆分员工的姓名firstName并lastName出现。

哦哦。没想到。

在您打开课程并用andEmployee替换单个字段之前,请停下来想一想。这会破坏任何客户吗?升级它们需要多长时间。您甚至控制所有访问您服务的客户端吗?namefirstNamelastName

停机时间 = 损失金钱。管理层准备好了吗?

有一个比 REST 早几年的旧策略。

永远不要删除数据库中的列。

— 未知

您始终可以将列(字段)添加到数据库表中。但不要带走一个。RESTful 服务中的原理是相同的。

将新字段添加到您的 JSON 表示中,但不要带走任何字段。像这样:

支持多个客户端的 JSON

{ "id": 1, "firstName": "Bilbo", "lastName": "Baggins", "role": "burglar", "name": "Bilbo Baggins", "_links": { "self": { "href": "http://localhost:8080/employees/1" }, "employees": { "href": "http://localhost:8080/employees" } }}
请注意此格式如何显示firstName, lastName, AND name?虽然它包含重复信息,但其目的是同时支持新老客户。这意味着您可以升级服务器,而无需同时升级客户端。一个可以减少停机时间的好举措。

您不仅应该以“旧方式”和“新方式”显示这些信息,还应该以两种方式处理传入的数据。

如何?简单的。像这样:

处理“旧”和“新”客户的员工记录

package payroll;import java.util.Objects;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.Id;@Entityclass Employee { private @Id @GeneratedValue Long id; private String firstName; private String lastName; private String role; Employee() {} Employee(String firstName, String lastName, String role) { this.firstName = firstName; this.lastName = lastName; this.role = role; } public String getName() { return this.firstName + " " + this.lastName; } public void setName(String name) { String[] parts = name.split(" "); this.firstName = parts[0]; this.lastName = parts[1]; } public Long getId() { return this.id; } public String getFirstName() { return this.firstName; } public String getLastName() { return this.lastName; } public String getRole() { return this.role; } public void setId(Long id) { this.id = id; } public void setFirstName(String firstName) { this.firstName = firstName; } public void setLastName(String lastName) { this.lastName = lastName; } public void setRole(String role) { this.role = role; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Employee)) return false; Employee employee = (Employee) o; return Objects.equals(this.id, employee.id) && Objects.equals(this.firstName, employee.firstName) && Objects.equals(this.lastName, employee.lastName) && Objects.equals(this.role, employee.role); } @Override public int hashCode() { return Objects.hash(this.id, this.firstName, this.lastName, this.role); } @Override public String toString() { return "Employee{" + "id=" + this.id + ", firstName='" + this.firstName + '\'' + ", lastName='" + this.lastName + '\'' + ", role='" + this.role + '\'' + '}'; }}
这个类与以前版本的Employee. 让我们回顾一下变化:

字段name已替换为firstName和lastName。
定义了旧name属性的“虚拟”吸气剂。getName()它使用firstNameandlastName字段来产生一个值。
name还定义了旧属性的“虚拟”设置器, setName(). 它解析传入的字符串并将其存储到适当的字段中。
当然,并非对 API 的每一次更改都像拆分字符串或合并两个字符串一样简单。但是对于大多数场景来说,想出一组转换肯定不是不可能的,对吧?

不要忘记更改预加载数据库的方式(在 中LoadDatabase)以使用这个新的构造函数。

log.info("Preloading " + repository.save(new Employee("Bilbo", "Baggins", "burglar")));

log.info("Preloading " + repository.save(new Employee("Frodo", "Baggins", "thief")));

适当的反应
朝着正确方向迈出的另一个步骤是确保您的每个 REST 方法都返回正确的响应。像这样更新 POST 方法:

处理“旧”和“新”客户端请求的 POST

@PostMapping("/employees")ResponseEntity<?> newEmployee(@RequestBody Employee newEmployee) { EntityModel entityModel = assembler.toModel(repository.save(newEmployee)); return ResponseEntity // .created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()) // .body(entityModel);}复制
新Employee对象像以前一样保存。但是生成的对象使用EmployeeModelAssembler.
Spring MVCResponseEntity用于创建HTTP 201 Created状态消息。这种类型的响应通常包含一个Location响应头,我们使用从模型的自相关链接派生的 URI。
此外,返回已保存对象的基于模型的版本。
通过这些调整,您可以使用相同的端点来创建新的员工资源,并使用遗留name字段:

$ curl -v -X POST localhost:8080/employees -H 'Content-Type:application/json' -d '{"name": "Samwise Gamgee", "role": "gardener"}'
输出如下所示:

POST /员工 HTTP/1.1> 主机:本地主机:8080> 用户代理:curl/7.54.0> 接受: /> 内容类型:应用程序/json> 内容长度:46>< 位置: http://localhost:8080/employees/3< 内容类型:application/hal+json;charset=UTF-8< 传输编码:分块< 日期:格林威治标准时间 2018 年 8 月 10 日星期五 19:44:43<{ “身份证”:3, "firstName": "Samwise", "lastName": "Gamgee", “角色”:“园丁”, "name": "Samwise Gamgee", “_链接”:{ “自己”: { “href”:“ http://localhost:8080/employees/3” }, “雇员”: { "href": "http://localhost:8080/employees" } }}
这不仅使生成的对象在 HAL(name以及firstName/ lastName)中呈现,而且Location标头也填充了
http://localhost:8080/employees/3. 超媒体驱动的客户端可以选择“冲浪”到这个新资源并继续与之交互。

PUT 控制器方法需要类似的调整:

为不同的客户端处理 PUT

@PutMapping("/employees/{id}")ResponseEntity<?> replaceEmployee(@RequestBody Employee newEmployee, @PathVariable Long id) { Employee updatedEmployee = repository.findById(id) // .map(employee -> { employee.setName(newEmployee.getName()); employee.setRole(newEmployee.getRole()); return repository.save(employee); }) // .orElseGet(() -> { newEmployee.setId(id); return repository.save(newEmployee); }); EntityModel entityModel = assembler.toModel(updatedEmployee); return ResponseEntity // .created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()) // .body(entityModel);}复制
然后使用 将操作Employee构建的对象包装到一个对象中。使用该方法,您可以检索由rel创建的。这个方法返回一个必须用方法变成一个的。save()
EmployeeModelAssemblerEntityModelgetRequiredLink()LinkEmployeeModelAssemblerSELFLinkURItoUri

由于我们想要一个比200 OK更详细的 HTTP 响应代码,我们将使用 Spring MVC 的ResponseEntity包装器。它有一个方便的静态方法created(),我们可以在其中插入资源的 URI。HTTP 201 Created是否具有正确的语义值得商榷,因为我们不一定要“创建”新资源。但它预装了一个Location响应头,所以用它运行。

$ curl -v -X PUT localhost:8080/employees/3 -H 'Content-Type:application/json' -d '{"name": "Samwise Gamgee", "role": "ring bearer"}' TCP_NODELAY 设置 连接到 localhost (::1) 端口 8080 (#0)> PUT /employees/3 HTTP/1.1> 主机:本地主机:8080> 用户代理:curl/7.54.0> 接受:/> 内容类型:应用程序/json> 内容长度:49>< HTTP/1.1 201< 位置:http://localhost:8080/employees/3< 内容类型:application/hal+json;charset=UTF-8< 传输编码:分块< 日期:格林威治标准时间 2018 年 8 月 10 日星期五 19:52:56{“身份证”:3,"firstName": "Samwise","lastName": "Gamgee",“角色”:“戒指持有者”,"name": "Samwise Gamgee",“_链接”:{“自己”: {“href”:“http://localhost:8080/employees/3”},“雇员”: {"href": "http://localhost:8080/employees"}}}
该员工资源现已更新,并且位置 URI 已发回。最后,适当地更新 DELETE 操作:

处理 DELETE 请求

@DeleteMapping("/employees/{id}")ResponseEntity<?> deleteEmployee(@PathVariable Long id) { repository.deleteById(id); return ResponseEntity.noContent().build();}复制
这将返回HTTP 204 No Content响应。

$ curl -v -X 删除本地主机:8080/employees/1 TCP_NODELAY 设置 连接到 localhost (::1) 端口 8080 (#0)> 删除 /employees/1 HTTP/1.1> 主机:本地主机:8080> 用户代理:curl/7.54.0> 接受:/>< HTTP/1.1 204< 日期:格林威治标准时间 2018 年 8 月 10 日星期五 21:30:26
对类中的字段进行更改Employee需要与您的数据库团队协调,以便他们可以正确地将现有内容迁移到新列中。

您现在已准备好进行升级,不会干扰现有客户端,而新客户端可以利用这些增强功能!

顺便说一句,您是否担心通过网络发送太多信息?在某些每个字节都很重要的系统中,API 的发展可能需要退居二线。但是在你测量之前不要追求这种过早的优化。

以上就是今天关于Spring的一些讨论,对你有帮助吗?如果你有兴趣深入了解,欢迎到Spring中国教育管理中心留言交流!

相关文章
|
1月前
|
安全 Java 数据库
安全无忧!在 Spring Boot 3.3 中轻松实现 TOTP 双因素认证
【10月更文挑战第8天】在现代应用程序开发中,安全性是一个不可忽视的重要环节。随着技术的发展,双因素认证(2FA)已经成为增强应用安全性的重要手段之一。本文将详细介绍如何在 Spring Boot 3.3 中实现基于时间的一次性密码(TOTP)双因素认证,让你的应用安全无忧。
81 5
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
42 4
|
1月前
|
Java API 数据库
如何使用Spring Boot构建RESTful API,以在线图书管理系统为例
【10月更文挑战第9天】本文介绍了如何使用Spring Boot构建RESTful API,以在线图书管理系统为例,从项目搭建、实体类定义、数据访问层创建、业务逻辑处理到RESTful API的实现,详细展示了每个步骤。通过Spring Boot的简洁配置和强大功能,开发者可以高效地开发出功能完备、易于维护的Web应用。
58 3
|
19天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
95 62
|
17天前
|
人工智能 前端开发 Java
基于开源框架Spring AI Alibaba快速构建Java应用
本文旨在帮助开发者快速掌握并应用 Spring AI Alibaba,提升基于 Java 的大模型应用开发效率和安全性。
基于开源框架Spring AI Alibaba快速构建Java应用
|
17天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
35 2
|
26天前
|
自然语言处理 Java API
Spring Boot 接入大模型实战:通义千问赋能智能应用快速构建
【10月更文挑战第23天】在人工智能(AI)技术飞速发展的今天,大模型如通义千问(阿里云推出的生成式对话引擎)等已成为推动智能应用创新的重要力量。然而,对于许多开发者而言,如何高效、便捷地接入这些大模型并构建出功能丰富的智能应用仍是一个挑战。
101 6
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用
【10月更文挑战第8天】本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,通过 Spring Initializr 创建并配置 Spring Boot 项目,实现后端 API 和安全配置。接着,使用 Ant Design Pro Vue 脚手架创建前端项目,配置动态路由和菜单,并创建相应的页面组件。最后,通过具体实践心得,分享了版本兼容性、安全性、性能调优等注意事项,帮助读者快速搭建高效且易维护的应用框架。
41 3
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用
【10月更文挑战第7天】本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,通过 Spring Initializr 创建 Spring Boot 项目并配置 Spring Security。接着,实现后端 API 以提供菜单数据。在前端部分,使用 Ant Design Pro Vue 脚手架创建项目,并配置动态路由和菜单。最后,启动前后端服务,实现高效、美观且功能强大的应用框架。
41 2
|
2月前
|
Java API 对象存储
微服务魔法启动!Spring Cloud与Netflix OSS联手,零基础也能创造服务奇迹!
这段内容介绍了如何使用Spring Cloud和Netflix OSS构建微服务架构。首先,基于Spring Boot创建项目并添加Spring Cloud依赖项。接着配置Eureka服务器实现服务发现,然后创建REST控制器作为API入口。为提高服务稳定性,利用Hystrix实现断路器模式。最后,在启动类中启用Eureka客户端功能。此外,还可集成其他Netflix OSS组件以增强系统功能。通过这些步骤,开发者可以更高效地构建稳定且可扩展的微服务系统。
56 1