vlambda博客
学习文章列表

读书笔记《hands-on-full-stack-development-with-spring-boot-2-0-and-react》使用Spring Boot创建REST风格的Web服务

Chapter 3. Creating a RESTful Web Service with Spring Boot

在本章中,我们将首先使用控制器类创建一个 RESTful Web 服务。之后,我们将演示如何使用 Spring Data REST 创建一个 RESTful Web 服务,该服务还自动涵盖所有 CRUD 功能。 我们使用在上一章中创建的数据库应用程序作为起点。

在本章中,我们将研究以下内容:

  • What the RESTful web service is
  • How to create a RESTful web service with Spring Boot
  • How to test the RESTful web service

Technical requirements


前面章节中创建的 Spring Boot 应用程序是必要的。

需要使用 Postman、cURL 或其他合适的工具来使用各种 HTTP 方法传输数据。

Creating a RESTful web service with Spring Boot


Web 服务是使用 HTTP 协议通过 Internet 进行通信的应用程序。有许多不同类型的 Web 服务架构,但所有设计的主要思想都是相同的。在本书中,我们将根据当今非常流行的设计创建一个 RESTful Web 服务。

Basics of REST

REST具象状态转移)是一种架构 style 用于创建网络服务。 REST 不是标准的,但它定义了由 Roy Fielding 定义的set 约束。六个约束如下:

  • Stateless: The server doesn't hold any information about the client state.
  • Client server: The client and server act independently. The server does not send any information without a request from the client.
  • Cacheable: Many clients often request the same resources, therefore it is useful to cache responses in order to improve performance.
  • Uniform interface: Requests from different clients look the same. Clients may be, for example, a browser, a Java application, and a mobile application.
  • Layered system: REST allows us to use a layered system architecture.
  • Code on demand: This is an optional constraint.

统一接口是一个重要的约束,它定义了每个 REST 架构都应该具有以下元素:

  • Identification of resources: There are resources with their unique identifiers, for example, URIs in web-based REST services. REST resources should expose easily understood directory structure URIs. Therefore, a good resource naming strategy is very important.
  • Resource manipulation through representation: When making a request to a resource, the server responds with a representation of the resource. Typically, the format of the representation is JSON or XML.
  • Self descriptive messages: Messages should have enough information that the server knows how to process them.
  • Hypermedia and the Engine of Application State (HATEOAS): Responses can contain links to other areas of service.

我们将在以下主题中开发的 RESTful Web 服务遵循 REST 架构原则。

Creating a RESTful web service

在 Spring Boot 中,所有 HTTP 请求都由控制器类处理。为了能够创建 RESTful Web 服务,首先,我们必须创建一个控制器类。我们将为控制器创建自己的 Java 包:

  1. Activate the root package in the Eclipse Project Explorer and right-click. Select NewPackage from the menu. We will name our new package com.packt.cardatabase.web:
读书笔记《hands-on-full-stack-development-with-spring-boot-2-0-and-react》使用Spring Boot创建REST风格的Web服务

  1. Next, we will create a new controller class in a new web package. Activate the com.packt.cardatabase.webpackage in the Eclipse project explorer and right-click. SelectNewClass from the menu. We will name our class  CarController:
读书笔记《hands-on-full-stack-development-with-spring-boot-2-0-and-react》使用Spring Boot创建REST风格的Web服务

  1. Now, your project structure should look like the following screenshot:
读书笔记《hands-on-full-stack-development-with-spring-boot-2-0-and-react》使用Spring Boot创建REST风格的Web服务

Note

如果您不小心在错误的包中创建了类,您可以在 Eclipse Project Explorer 中的包之间拖放文件。有时,当您进行一些更改时,Eclipse Project Explorer 视图可能无法正确呈现。刷新项目浏览器有帮助(激活 Project Explorer 并按 F5 )。

  1. Open your controller class in the editor window and add the @RestController annotation before the class definition. See the following source code. The @RestController annotation identifies that this class will be the controller for the RESTful web service:
      package com.packt.cardatabase.web;

      import org.springframework.web.bind.annotation.RestController;

      @RestController
      public class CarController { 
      }
  1. Next, we add a new method inside our controller class. The method is annotated with the @RequestMapping annotation, which defines the endpoint that the method is mapped to. Following, you can see the sample source code. In this example, when a user navigates to the /cars endpoint, the getCars() method is executed: 
      package com.packt.cardatabase.web;

      import org.springframework.web.bind.annotation.RestController;

      @RestController
      public class CarController {
        @RequestMapping("/cars")
        public Iterable<Car> getCars() {

        } 
      }

getCars() 方法返回所有汽车对象,然后由 Jackson 库编组为 JSON 对象。

Note

默认情况下,@RequestMapping 处理所有 HTTP 方法(GETPUT POST 等)请求。您可以使用以下 @RequestMapping("/cars", method=GET) 参数定义接受哪种方法。 现在,此方法仅处理来自 /cars 端点的 GET 请求。

  1. To be able to return cars from the database, we have to inject our CarRepository into the controller. Then, we can use the findAll() method that the repository provides to fetch all cars. The following source code shows the controller code:
      package com.packt.cardatabase.web;

      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;

      import com.packt.cardatabase.domain.Car;
      import com.packt.cardatabase.domain.CarRepository;

      @RestController
      public class CarController {
        @Autowired
        private CarRepository repository;

        @RequestMapping("/cars")
        public Iterable<Car> getCars() {
          return repository.findAll();
        }
      }
  1. Now, we are ready to run our application and navigate to localhost:8080/cars. We can see that there is something wrong, and the application seems to be in an infinite loop. That happens due to our one-to-many relationship between the car and owner tables. So, what happens in practice—first, the car is serialized, and it contains an owner that is then serialized, and that, in turn, contains cars that are then serialized... and so on. To avoid this, we have to add the @JsonIgnoreannotation to thecarsfield in the Owner class:
      // Owner.java

      @OneToMany(cascade = CascadeType.ALL, mappedBy="owner")
      @JsonIgnore
      private List<Car> cars;
  1. Now, when you run the application and navigate to localhost:8080/cars , everything should go as expected and you will get all the cars from the database in JSON format, as shown in the following screenshot:
读书笔记《hands-on-full-stack-development-with-spring-boot-2-0-and-react》使用Spring Boot创建REST风格的Web服务

我们已经完成了我们的第一个 RESTful Web 服务,它返回所有的汽车。 Spring Boot 提供了一种更强大的方式来创建 RESTful Web 服务,这将在下一个主题中进行研究。

Using Spring Data REST

Spring Data REST 是 Spring Data 项目的一部分。它提供了一种简单 和快速的方式来使用Spring 实现RESTful Web 服务。首先,使用 Spring Data REST,您必须将以下依赖项添加到 pom.xml 文件中:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>

默认情况下,Spring Data REST 从应用程序中查找所有公共存储库,并为您的实体自动创建 RESTful Web 服务。

您可以在 application.properties 文件中定义服务端点:

spring.data.rest.basePath=/api

现在您可以从 localhost:8080/api 端点访问 RESTful Web 服务。通过调用服务的根端点,它返回可用的资源。 Spring Data REST 在 HAL超文本应用程序语言 )格式。 HAL 格式 提供 一组用于在 JSON 中表达超链接的约定,它使您的 RESTful Web 服务更易于前端开发人员使用:

读书笔记《hands-on-full-stack-development-with-spring-boot-2-0-and-react》使用Spring Boot创建REST风格的Web服务

我们可以看到有汽车和车主实体服务的链接。 Spring Data Rest 服务路径名派生自实体名称。然后该名称将是复数形式且不大写。例如,实体 Car 服务路径名将命名为 cars。配置文件链接由 Spring Data Rest 生成,它包含特定于应用程序的元数据。

现在,我们开始更仔细地检查不同的服务。有多种工具可用于测试和使用 RESTful Web 服务。在本书中,我们使用 Postman,但您可以使用您熟悉的工具,例如 cURL。 Postman 可以作为桌面应用程序 或浏览器插件获取。通过使用 Windows Ubuntu Bash,cURL 也可用于 Windows 10。

如果你使用 cars端点http://localhost:8080/api/cars发出请求class="literal">GET 方法,您将获得所有 汽车的列表,如下图所示:

读书笔记《hands-on-full-stack-development-with-spring-boot-2-0-and-react》使用Spring Boot创建REST风格的Web服务

在 JSON 响应中,您可以看到有一个 cars 数组,每辆汽车都包含汽车特定的数据。所有的汽车还有 "_links" 属性,它是一个链接的集合,通过这些你可以访问汽车本身或获取汽车的所有者。要访问一辆特定的汽车,路径将是 http://localhost:8080/api/cars/{id}

对 http://localhost:8080/api/cars/3/owner 的请求返回汽车的所有者。响应现在包含车主数据、车主链接以及用户拥有的其他 汽车的链接:

读书笔记《hands-on-full-stack-development-with-spring-boot-2-0-and-react》使用Spring Boot创建REST风格的Web服务

Spring Data Rest 服务提供所有 CRUD 操作。下表显示了可用于不同 CRUD 操作的 HTTP 方法:

HTTP 方法

CRUD

GET

读取

POST

创建

PUT/PATCH

更新

删除

删除

接下来,我们将了解如何使用我们的 RESTful Web 服务从 database 中删除汽车。在删除操作中,您必须使用 DELETE 方法和要删除的汽车的链接(http://localhost: 8080/api/cars/{id})。以下截图显示了如何使用 cURL 删除 ID 为4 的一辆汽车。 在删除请求之后,您可以看到现在剩下两辆汽车在数据库:

读书笔记《hands-on-full-stack-development-with-spring-boot-2-0-and-react》使用Spring Boot创建REST风格的Web服务

当我们要向数据库中添加一辆新车时,我们必须使用 POST方法,链接为http://localhost :8080/api/cars.标头必须包含  Content-Type 字段,其值为 Content-Type:application/json,新的汽车对象将嵌入到请求正文中:

读书笔记《hands-on-full-stack-development-with-spring-boot-2-0-and-react》使用Spring Boot创建REST风格的Web服务

响应将发回一个新创建的汽车对象。现在,如果您再次向 http://localhost:8080/api/cars 路径发出 GET 请求,可以看到数据库中存在新车:

读书笔记《hands-on-full-stack-development-with-spring-boot-2-0-and-react》使用Spring Boot创建REST风格的Web服务

要更新实体,我们必须使用 PATCH 方法和我们要更新的汽车的链接  (http://localhost:8080/api/cars/{id})。 标头必须包含 内容-Type 字段的值为Content-Type:application/json 以及带有编辑数据的汽车对象将在请求中给出身体。如果您使用 PATCH,您必须只发送更新的字段。如果您使用 PUT,则必须包含所有要请求的字段。让我们编辑我们在上一个示例中创建的汽车。我们将更改颜色为白色并填写我们留空的寄存器号。

我们还将使用所有者字段将所有者链接到汽车。所有者字段的内容是所有者的链接 (http://localhost:8080/api/owners/{id})。以下屏幕截图显示了 PATCH 请求内容:

读书笔记《hands-on-full-stack-development-with-spring-boot-2-0-and-react》使用Spring Boot创建REST风格的Web服务

使用 GET 请求获取所有汽车后,您可以看到汽车已更新:

读书笔记《hands-on-full-stack-development-with-spring-boot-2-0-and-react》使用Spring Boot创建REST风格的Web服务

在上一章中,我们创建了对存储库的查询。这些查询也可以包含在我们的服务中。要包含查询,您必须将 @RepositoryRestResource 注释添加到存储库类。查询参数使用 @Param 注释进行注释。以下源代码显示了我们的 CarRepository 带有这些注释:

package com.packt.cardatabase.domain;

import java.util.List;

import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource
public interface CarRepository extends CrudRepository <Car, Long> {
  // Fetch cars by brand
  List<Car> findByBrand(@Param("brand") String brand);

  // Fetch cars by color
  List<Car> findByColor(@Param("color") String color);
}

现在,当您向 http://localhost:8080/api/cars 路径发出 GET请求时,您可以看到有一个名为 /search 的新端点。调用 http://localhost:8080/api/cars/search 路径返回以下响应:

读书笔记《hands-on-full-stack-development-with-spring-boot-2-0-and-react》使用Spring Boot创建REST风格的Web服务

从响应中,您可以看到这两个查询现在在我们的服务中可用。以下 URL 演示了如何按品牌获取汽车:

http://localhost:8080/api/cars/search/findByBrand?brand=Ford
读书笔记《hands-on-full-stack-development-with-spring-boot-2-0-and-react》使用Spring Boot创建REST风格的Web服务

Summary


在本章中,我们使用 Spring Boot 创建了一个 RESTful Web 服务。首先,我们创建了一个控制器和一个以 JSON 格式返回所有汽车的方法。接下来,我们使用 Spring Data REST 来获得具有所有 CRUD 功能的功能齐全的 Web 服务。我们介绍了使用我们创建的服务的 CRUD 功能所需的不同类型的请求。最后,我们还包括我们的服务查询。在下一章中,我们将使用 Spring Security 保护我们的后端。

Questions


  1. What is REST?
  2. How can you create a RESTful web service with Spring Boot?
  3. How can you fetch items using our RESTful web service?
  4. How can you delete items using our RESTful web service?
  5. How can you add items using our RESTful web service?
  6. How can you update items using our RESTful web service?
  7. How can you use queries with our RESTful web service?