vlambda博客
学习文章列表

读书笔记《building-a-restful-web-service-with-spring》第一个终端

Chapter 3. The First Endpoint

在上一章中,我们了解了如何设置构建 RESTful Web 服务所需的脚手架。我们现在可以开始实现我们的第一个端点。正如第1章中介绍的,一些基础知识 , Spring Web MVC 提供了以 RESTful 方式创建控制器的必要工具。为了说明它们的使用,我们将依靠我们的示例 Web 服务。更具体地说,我们将开始实现我们服务的 Inventory 组件。

本章将讨论以下主题:

  • 我们示例 Web 服务的 Inventory 组件

  • 用于构建 RESTful 端点的 Spring 构造

  • 运行我们的第一个端点

  • 数据呈现的简要讨论

The Inventory service


我们物业管理服务的核心是房间,这些房间代表客人可以预订的实体房间。它们按类别组织。房间类别是类似房间的逻辑分组。例如,我们可以为所有带双人床的房间设置一个 Double Rooms 类别。房间根据以下代码片段显示属性:

@Entity(name = "rooms")
public class Room {

  private long id;
  private RoomCategory roomCategory;
  private String name;
  private String description;

  @Id
  @GeneratedValue
  public long getId() {
    return id;
  }

  @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.REFRESH}, fetch = FetchType.EAGER)
  public RoomCategory getRoomCategory() {
    return roomCategory;
  }

  @Column(name = "name", unique = true, nullable = false, length = 128)
  public String getName() {
    return name;
  }

  @Column(name = "description")
  public String getDescription() {
    return description;
  }
}

使用 Java Persistence API (JPA) 和 Hibernate,房间有一个自动生成的标识符(感谢 @javax.persistence.Id 和 < code class="literal">@javax.persistence.GeneratedValue 注释):一个唯一且强制性的名称以及可选的 描述。

Note

JPA 不在本书的范围内。关于它的信息可以在 http://en.wikipedia.org/wiki/Java_Persistence_API 找到

此外,使用 @javax.persistence.ManyToOne 注释在类别和房间之间建立一对多关系。

Inventory 服务提供对房间及其类别的访问。该组件提供了几个操作。但是,我们只对本章中的一种操作感兴趣。因此,让我们考虑以下 Java 接口:

public interface InventoryService {

  public Room getRoom(long roomId);

  // other methods omitted for clarity
}

这个服务接口使我们能够通过它的标识符来查找房间。假设我们有这个接口的实现,下一步就是通过我们的 RESTful 接口公开这个操作。以下部分描述了如何去做。

REST and the MVC pattern


Spring Web MVC 模块提供了传统模型视图控制器的实现图案。虽然 REST 不要求使用任何特定的模式,但使用 MVC 模式是很自然的选择,其中 RESTful 资源或模型通过 一个控制器。在我们的例子中,视图将是模型的 JSON 表示。

事不宜迟,让我们看一下我们的第一个端点:

@RestController
@RequestMapping("/rooms")
public class RoomsResource {

  private final InventoryService inventoryService;

  public RoomsResource(InventoryService inventoryService) {
    this.inventoryService = inventoryService;
  }

  @RequestMapping(value = "/{roomId}", method = RequestMethod.GET)
  public RoomDTO getRoom(@PathVariable("roomId") String roomId) {
    RoomDTO room = ...
    // omitted for sake of clarity
    return room;
  }
}

通过使用 @org.springframework.web.bind.annotation.RestController,我们告诉 Spring RoomsResource 是一个控制器。

Tip

传统上,人们会期望这个控制器类被称为RoomController。然而,在 RESTful 架构风格中,核心概念是围绕资源展开的。因此,使用 Resource 后缀更能恰当地体现 REST 原则。

这段代码中另一个需要注意的注解是 @org.springframework.web.bind.annotation.RequestMapping。这将在下一节中讨论。

Note

数据传输对象 (< strong>DTO) 模式在这里被引用(RoomDTO),但我们将在 第 4 章数据表示。它在持久层和表示层之间提供了有用的解耦。

Request mapping

@org.springframework.web.bind.annotation.RequestMapping 注释为 映射传入请求提供了粘合剂 代码中的类和方法。

Path mapping

通常,在类级别,@org.springframework.web.bind.annotation.RequestMapping 注释允许为特定的 资源(或控制器)类的路径。例如,在前面的代码摘录中,RoomsResource 类声明了 顶级请求映射,<代码类="literal">@RequestMapping("/rooms")。使用此注解,该类将处理对路径 rooms 的所有请求。

HTTP method mapping

可以将特定的 HTTP 方法映射到类或 Java 方法。我们的 RoomsResource 公开了一个检索 Room 按标识符。声明如下:

@RequestMapping(value = "/{roomId}", method = RequestMethod.GET)

结合 类级注解,GET 请求使用以下 /rooms/{roomId} 值将映射到 RoomsResource.getRoom()

使用任何其他方法(例如,PUTDELETE)的请求将不会映射到此 Java 方法。

Request parameter mapping

除了路径参数,请求 参数也可以在RestController 类。 @org.springframework.web.bind.annotation.RequestParam 参数提供了将 HTTP 查询参数映射到 Java 方法属性的方法。为了说明参数映射,让我们在资源中添加一个新的查找方法:

@RequestMapping(method = RequestMethod.GET)
public List<RoomDTO> getRoomsInCategory(@RequestParam("categoryId") long categoryId) {
  RoomCategory category = inventoryService.getRoomCategory(categoryId);
  return inventoryService.getAllRoomsWithCategory(category)
  .stream().map(RoomDTO::new).collect(Collectors.toList());
}

对 URL 的请求,例如 http://localhost:8080/rooms?categoryId=1,将由 this 处理 方法,并且 categoryId 方法属性将设置为 1。该方法将以 JSON 格式返回 给定类别的房间列表,如下所示:

[
  {
    "id": 1,
    "name": "Room 1",
    "roomCategoryId": 1,
    "description": "Nice, spacious double bed room with usual amenities"
    }
]

Tip

服务设计者也可以在不使用参数的情况下声明这个端点。例如,URL 模式可以是 /rooms/categories/{categoryId}。这种方法具有改进缓存的额外好处,因为并非所有浏览器和代理都缓存查询参数。请参阅第 6 章性能,了解更多信息有关缓存技术的详细信息。

随着我们在本书的其余部分继续实施我们的物业管理系统,我们将把这些映射结构付诸实践。 第 5 章REST 中的 CRUD 操作,将特别强调如何使用这些注释映射 REST 中的 CRUD(创建、读取、更新和删除)操作。

Tip

前面的代码片段 使用了 Java 的新 Stream API,这是 Java 8 中引入的一些函数式编程优点。在 https://docs.oracle.com/javase /8/docs/api/java/util/stream/Stream.html

Running the service


构建 第 2 章使用 Maven 和 Gradle 构建 RESTful Web 服务,我们可以使用 Spring Boot 快速启动并运行我们的服务。为此,我们需要创建一个主类,如下所示:

package com.packtpub.springrest.inventory;
// imports omitted
@SpringBootApplication
public class WebApplication {

  public static void main(String[] args) {
    SpringApplication.run(new Object[]{WebApplication.class, "inventory.xml"}, args);
    InventoryService inventoryService = context.getBean(InventoryService.class);
  }
}

在您最喜欢的 IDE 中运行此类将启动嵌入式 Tomcat 实例和公开您的 资源。该服务可通过 http://localhost:8080 访问。例如,访问 http://localhost:8080/rooms/1 将返回以下内容:

{
  "id": 1,
  "name": "Room 1",
  "roomCategoryId": 1,
  "description": "Nice, spacious double bed room with usual amenities"
}

Note

我们离开 第 2 章使用 Maven 和 Gradle 构建 RESTful Web 服务,在本例中通过混合自动检测和基于 XML 的策略。通过传递应用程序类和我们的 XML Spring 连接的名称(inventory.xml),Spring Boot 将加载它可以找到的所有带注释的 bean,以及在XML。

A few words on data representation


如果我们不做 任何事情,我们的 DTO 对象就会被 Spring 神奇地转换为 JSON。这要归功于 Spring 对与 Jackson 的数据绑定的支持。 Jackson JSON 处理器提供了一个快速且轻量级的库,用于将 Java 对象映射到 JSON 对象。

Summary


在本章中,我们讨论了示例 RESTful Web 服务的 Inventory 组件并处理了我们的第一个端点。我们探索了如何快速运行和访问这个端点,我们甚至加入了一个额外的端点来按类别列出房间。

在下一章中,我们将更详细地探讨如何控制响应的 JSON 格式,以及 DTO 模式以及为什么它对解耦系统的不同层很有用。