这是 REST 解析的最后一部分。在前面的部分中:
创建项目
在本节中,我们将使用 Spring Boot 创建一个小型 RESTful 应用程序。我们的应用程序将在分析最后部分的示例中的客户端上实现 CRUD(创建、读取、更新、删除)操作。首先,我们通过菜单File -> New -> Project...创建一个新的 Spring Boot 应用程序 ,在打开的窗口中,选择 Spring Initializr 并指定 Project SDK: 单击 Next 按钮。在下一个窗口中,指定 Maven 项目类型,指定 Group 和 Artifact: 单击 Next 按钮。在下一个窗口中,我们需要选择项目所需的 Spring Framework 组件。Spring Web 对我们来说就足够了: 单击“下一步”按钮。接下来,剩下的就是指定项目的名称及其在文件系统中的位置: 单击“完成”按钮。项目已经创建,现在我们可以看到它的结构: IDEA 已经为我们生成了 Maven 构建系统部署描述符 - pom.xml 和主应用程序类:RestExampleApplication
。这是他们的代码:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.javarush.lectures</groupId>
<artifactId>rest_example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rest_example</name>
<description>REST example project</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
休息示例应用:
@SpringBootApplication
public class RestExampleApplication {
public static void main(String[] args) {
SpringApplication.run(RestExampleApplication.class, args);
}
}
创建 REST 功能
我们的应用程序管理客户。所以我们需要做的第一件事就是创建一个客户实体。这将是一个 POJO 类。让我们model
在包内创建一个包com.javarush.lectures.rest_example
。model
让我们在包中创建一个类Client
:
public class Client {
private Integer id;
private String name;
private String email;
private String phone;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
该服务将在客户端实现CRUD操作。下一步是创建一个将实现这些操作的服务。在包中com.javarush.lectures.rest_example
我们将创建一个包service
,在其中我们将创建一个接口ClientService
。接口代码如下,带注释:
public interface ClientService {
/**
* Создает нового клиента
* @param client - клиент для создания
*/
void create(Client client);
/**
* returns список всех имеющихся клиентов
* @return список клиентов
*/
List<client> readAll();
/**
* returns клиента по его ID
* @param id - ID клиента
* @return - an object клиента с заданным ID
*/
Client read(int id);
/**
* Обновляет клиента с заданным ID,
* в соответствии с переданным клиентом
* @param client - клиент в соответсвии с которым нужно обновить данные
* @param id - id клиента которого нужно обновить
* @return - true если данные были обновлены, иначе false
*/
boolean update(Client client, int id);
/**
* Удаляет клиента с заданным ID
* @param id - id клиента, которого нужно удалить
* @return - true если клиент был удален, иначе false
*/
boolean delete(int id);
}
接下来我们需要创建该接口的实现。现在它将充当客户端存储库Map<Integer, Client>
。卡密钥将是客户端 ID,值将是客户端本身。这样做是为了不让示例因使用数据库的具体细节而超载。然而,将来我们将能够编写接口的不同实现,在其中可以连接真实的数据库。在包中,service
我们将创建该接口的实现ClientService
:
@Service
public class ClientServiceImpl implements ClientService {
// Хранorще клиентов
private static final Map<Integer, Client> CLIENT_REPOSITORY_MAP = new HashMap<>();
// Переменная для генерации ID клиента
private static final AtomicInteger CLIENT_ID_HOLDER = new AtomicInteger();
@Override
public void create(Client client) {
final int clientId = CLIENT_ID_HOLDER.incrementAndGet();
client.setId(clientId);
CLIENT_REPOSITORY_MAP.put(clientId, client);
}
@Override
public List<Client> readAll() {
return new ArrayList<>(CLIENT_REPOSITORY_MAP.values());
}
@Override
public Client read(int id) {
return CLIENT_REPOSITORY_MAP.get(id);
}
@Override
public boolean update(Client client, int id) {
if (CLIENT_REPOSITORY_MAP.containsKey(id)) {
client.setId(id);
CLIENT_REPOSITORY_MAP.put(id, client);
return true;
}
return false;
}
@Override
public boolean delete(int id) {
return CLIENT_REPOSITORY_MAP.remove(id) != null;
}
}
该注解@Service
告诉Spring这个类是一个服务。这是一种特殊类型的类,其中实现了应用程序的一些业务逻辑。随后,由于这个注释,Spring 将使用依赖注入在需要的地方为我们提供该类的实例。现在是时候创建控制器了。一个特殊的类,我们在其中实现处理客户端端点 (URI) 请求的逻辑。为了更清楚,我们将分部分创建这个类。首先,让我们创建类本身并引入对它的依赖ClientService
:
@RestController
public class ClientController {
private final ClientService clientService;
@Autowired
public ClientController(ClientService clientService) {
this.clientService = clientService;
}
}
让我们澄清一下注释: @RestController - 告诉 Spring 该类是一个 REST 控制器。那些。这个类将实现处理客户端请求的逻辑 @Autowired - 告诉Spring需要在这个地方注入一个依赖项。我们将接口传递给构造函数ClientService
。我们之前用注释标记了该服务的实现@Service
,现在 Spring 将能够将此实现的实例传递给控制器构造函数。接下来,我们将逐步实现各个控制器方法来处理CRUD操作。让我们从创建操作开始。为此,我们编写一个方法create
:
@PostMapping(value = "/clients")
public ResponseEntity<?> create(@RequestBody Client client) {
clientService.create(client);
return new ResponseEntity<>(HttpStatus.CREATED);
}
让我们看一下这个方法: @PostMapping(value = "/clients")
- 这里我们的意思是这个方法处理发送到 /clients 地址的 POST 请求,该方法返回ResponseEntity<?>
。ResponseEntity
- 用于返回响应的特殊类。使用它,我们可以稍后将 HTTP 状态代码返回给客户端。该方法接受一个参数@RequestBody Client client
,该参数的值是从请求正文中替换的。摘要谈到了这一点 @RequestBody
。在方法体内,我们对先前创建的服务调用 create 方法,并将其传递给参数中接受的客户端控制器。ResponseEntity
然后我们通过创建一个新对象并将所需的枚举值传递给它来返回状态 201 Created HttpStatus
。接下来我们实现操作Read
:首先我们实现获取所有可用客户端列表的操作:
@GetMapping(value = "/clients")
public ResponseEntity<List<Client>> read() {
final List<Client> clients = clientService.readAll();
return clients != null && !clients.isEmpty()
? new ResponseEntity<>(clients, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
让我们开始分析: @GetMapping(value = "/clients")
- 一切都与注释类似@PostMapping
,只是现在我们处理 GET 请求。这次我们返回ResponseEntity<List<Client>>
,只是这一次,除了 HTTP 状态之外,我们还会返回响应正文,这将是一个客户端列表。在 Spring REST 控制器中,所有 POJO 对象以及作为响应主体返回的 POJO 对象的集合都会自动序列化为 JSON,除非另有明确指定。这非常适合我们。在该方法内部,使用我们的服务,我们可以获得所有客户的列表。接下来,如果列表不为 null 或空,我们将ResponseEntity
使用该类返回客户端列表和 HTTP 状态 200 OK。否则,我们只返回 HTTP 状态 404 Not Found。接下来,我们将实现通过 id 接收客户端的功能:
@GetMapping(value = "/clients/{id}")
public ResponseEntity<Client> read(@PathVariable(name = "id") int id) {
final Client client = clientService.read(id);
return client != null
? new ResponseEntity<>(client, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
新的一点是我们现在有了一个路径变量。在 URI 中定义的变量。value = "/clients/{id}"
。我们用花括号表示它。在方法参数中,我们int
使用注释将其接受为变量@PathVariable(name = "id")
。此方法将接受 uri 形式的请求/clients/{id}
,其中{id}
可以是任何数值。该值随后被传递给变量int id
——方法参数。在正文中,我们使用我们的服务接收对象Client
并接受id
。然后,通过与列表类比,我们返回 200 OK 状态和对象本身Client
,或者简单地返回 404 Not Found 状态(如果id
系统中没有具有此状态的客户端)。剩下的就是实现两个操作——更新和删除。以下是这些方法的代码:
@PutMapping(value = "/clients/{id}")
public ResponseEntity<?> update(@PathVariable(name = "id") int id, @RequestBody Client client) {
final boolean updated = clientService.update(client, id);
return updated
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
@DeleteMapping(value = "/clients/{id}")
public ResponseEntity<?> delete(@PathVariable(name = "id") int id) {
final boolean deleted = clientService.delete(id);
return deleted
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
这些方法本质上没有什么新内容,因此我们将跳过详细描述。唯一值得一提的是,该方法update
处理 PUT 请求(注解@PutMapping
),该方法delete
处理 DELETE 请求(注解DeleteMapping
)。这是完整的控制器代码:
@RestController
public class ClientController {
private final ClientService clientService;
@Autowired
public ClientController(ClientService clientService) {
this.clientService = clientService;
}
@PostMapping(value = "/clients")
public ResponseEntity<?> create(@RequestBody Client client) {
clientService.create(client);
return new ResponseEntity<>(HttpStatus.CREATED);
}
@GetMapping(value = "/clients")
public ResponseEntity<List<Client>> read() {
final List<client> clients = clientService.readAll();
return clients != null && !clients.isEmpty()
? new ResponseEntity<>(clients, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
@GetMapping(value = "/clients/{id}")
public ResponseEntity<Client> read(@PathVariable(name = "id") int id) {
final Client client = clientService.read(id);
return client != null
? new ResponseEntity<>(client, HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
@PutMapping(value = "/clients/{id}")
public ResponseEntity<?> update(@PathVariable(name = "id") int id, @RequestBody Client client) {
final boolean updated = clientService.update(client, id);
return updated
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
@DeleteMapping(value = "/clients/{id}")
public ResponseEntity<?> delete(@PathVariable(name = "id") int id) {
final boolean deleted = clientService.delete(id);
return deleted
? new ResponseEntity<>(HttpStatus.OK)
: new ResponseEntity<>(HttpStatus.NOT_MODIFIED);
}
}
结果,我们的项目结构如下所示:
启动和测试
main
要运行我们的应用程序,只需运行类中的方法即可RestExampleApplication
。为了测试 RESTful Web 服务,您需要下载新软件)事实是,从常规浏览器发送 GET 请求非常容易,但对于 POST、PUT 和 DELETE,常规浏览器是不够的。不用担心:您可以使用 Postman 发送任何 HTTP 请求。你可以在这里下载。下载并安装后,我们开始测试我们的应用程序。为此,请打开程序并创建新请求: 单击左上角的“新建”按钮。接下来,选择请求: 下一步,为其命名并保存。现在让我们尝试向服务器发送 POST 请求并创建第一个客户端: 我们以这种方式创建多个客户端。然后我们将请求类型改为GET并发送给服务器:
一般结果
恭喜:我们已经涵盖了 REST 主题。所有材料都很庞大,但我们希望它对您有用:-
我们了解了 REST 是什么。
-
我们熟悉了 REST 的历史。
-
我们讨论了这种架构风格的局限性和原则:
- 将架构引入客户端-服务器模型;
- 缺乏条件;
- 缓存;
- 界面的一致性;
- 层;
- 按需代码(可选限制)。
-
我们研究了 REST 提供的优势
-
我们详细研究了服务器和客户端如何使用 HTTP 协议进行交互。
-
让我们仔细看看请求和响应。他们的组件被拆卸。
-
最后,我们继续练习,并在 Spring Boot 中编写了我们自己的小型 RESTful 应用程序。我们甚至学习了如何使用 Postman 程序来测试它。
家庭作业
请尝试以下操作:- 按照上面的描述,创建您自己的 Spring Boot 项目并在其中实现与讲座中相同的逻辑。重复一切 1 合 1。
- 启动它。应用。
- 下载并设置 Postman(或任何其他用于发送请求的工具,甚至是curl)。
- 按照讲座中描述的相同方式测试 POST 和 GET 请求。
- 自己测试 PUT 和 DELETE 请求。
GO TO FULL VERSION