首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

使用 Spring HATEOAS 开发 REST 服务(2)

使用 Spring HATEOAS 开发 REST 服务(2)

示例本文将通过一个完整的示例来说明 HATEOAS。该示例是一个常见的待办事项的服务,用户可以创建新的待办事项、进行编辑或标记为已完成。该示例中包含的资源如下:
  • 用户:应用中的用户。
  • 列表:待办事项的列表,属于某个用户。
  • 事项:具体的待办事项,属于某个列表。
应用提供相关的 REST 服务来完成对于列表和事项两个资源的 CRUD 操作。
Spring HATEOAS如果 Web 应用基于 Spring 框架开发,那么可以直接使用 Spring 框架的子项目 HATEOAS 来开发满足 HATEOAS 约束的 Web 服务。本文的示例应用基于 Java 8 和使用 Spring Boot 1.1.9 来创建,Spring HATEOAS 的版本是 0.16.0.RELEASE。
基本配置满足 HATEOAS 约束的 REST 服务最大的特点在于服务器提供给客户端的表达中包含了动态的链接信息,客户端通过这些链接来发现可以触发状态转换的动作。Spring HATEOAS 的主要功能在于提供了简单的机制来创建这些链接,并与 Spring MVC 框架有很好的集成。对于已有的 Spring MVC 应用,只需要一些简单的改动就可以满足 HATEOAS 约束。对于一个 Maven 项目来说,只需要添加  中的依赖即可。
清单 1. Spring HATEOAS 的 Maven 依赖声明
1
2
3
4
5
<dependency>
  <groupId>org.springframework.hateoas</groupId>
  <artifactId>spring-hateoas</artifactId>
  <version>0.16.0.RELEASE</version>
</dependency>




资源REST 架构中的核心概念之一是资源。服务器提供的是资源的表达,通常使用 JSON 或 XML 格式。在一般的 Web 应用中,服务器端代码会对所使用的资源建模,提供相应的模型层 Java 类,这些模型层 Java 类通常包含 JPA 相关的注解来完成持久化。在客户端请求时,服务器端代码通过 Jackson 或 JAXB 把模型对象转换成 JSON 或 XML 格式。  给出了示例应用中表示列表的模型类 List 的声明。
清单 2. 表示列表的模型类 List 的声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Entity
public class List extends AbstractEntity {
   private String name;

   @ManyToOne
   @JsonIgnore
   private User user;

   @OneToMany(mappedBy = "list", fetch = FetchType.LAZY)
   @JsonIgnore
   private Set<Item> items = new HashSet<>();

   protected List() {
   }

   public List(String name, User user) {
       this.name = name;
       this.user = user;
   }

   public String getName() {
       return name;
   }

   public User getUser() {
       return user;
   }

   public Set<Item> getItems() {
       return items;
   }
}




当客户端请求某个具体的 List 类的对象时,服务器端返回如  所示的 JSON 格式的表达。
清单 3. List 类的对象的 JSON 格式的表达
1
2
3
4
{
   "id": 1,
   "name": "Default"
}




在  中,服务器端返回的只是模型类对象本身的内容,并没有提供相关的链接信息。为了把模型对象类转换成满足 HATEOAS 要求的资源,需要添加链接信息。Spring HATEOAS 使用 org.springframework.hateoas.Link 类来表示链接。Link 类遵循 Atom 规范中对于链接的定义,包含 rel 和 href 两个属性。属性 rel 表示的是链接所表示的关系(relationship),href 表示的是链接指向的资源标识符,一般是 URI。资源通常都包含一个属性 rel 值为 self 的链接,用来指向该资源本身。
在创建资源类时,可以继承自 Spring HATEOAS 提供的 org.springframework.hateoas.Resource 类,Resource 类提供了简单的方式来创建链接。  给出了与模型类 List 对应的资源类 ListResource 的声明。
清单 4. 模型类 List 对应的资源类 ListResource 的声明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ListResource extends Resource {
   private final List list;

   public ListResource(List list) {
       super(list);
       this.list = list;
       add(new Link("http://localhost:8080/lists/1"));
       add(new Link("http://localhost:8080/lists/1/items", "items"));
   }

   public List getList() {
       return list;
   }
}




如  所示,ListResource 类继承自 Resource 类并对 List 类的对象进行了封装,添加了两个链接。在使用 ListResource 类之后,服务器端返回的表达格式如  所示。
清单 5. 使用 ListResource 类之后的 JSON 格式的表达
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
   "list": {
       "id": 1,
       "name": "Default"
   },
   "links": [
       {
           "rel": "self",
           "href": "http://localhost:8080/lists/1"
       },
       {
           "rel": "items",
           "href": "http://localhost:8080/lists/1/items"
       }
   ]
}




  的 JSON 内容中添加了额外的 links 属性,并包含了两个链接。不过模型类对象的内容被封装在属性 list 中。这是因为 ListResource 类直接封装了整个的 List 类的对象,而不是把 List 类的属性提取到 ListResource 类中。如果需要改变输出的 JSON 表达的格式,可以使用另外一种封装方式的 ListResource 类,如  所示。
清单 6. 不同封装格式的 ListResource 类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ListResource extends Resource {
   private final Long id;
      private final String name;

   public ListResource(List list) {
       super(list);
       this.id = list.getId();
       this.name = list.getName();
       add(new Link("http://localhost:8080/lists/1"));
       add(new Link("http://localhost:8080/lists/1/items", "items"));
   }

   public Long getId() {
       return id;
   }
      public String getName() {
       return name;
   }
}




对应的资源的表达如  所示。
清单 7. 使用不同封装方式的 JSON 格式的表达
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
   "id": 1,
   "name": "Default",
   "links": [
       {
           "rel": "self",
           "href": "http://localhost:8080/lists/1"
       },
       {
           "rel": "items",
           "href": "http://localhost:8080/lists/1/items"
       }
   ]
}




这两种不同的封装方式各有优缺点。第一种方式的优点是实现起来很简单,只需要把模型层的对象直接包装即可;第二种方式虽然实现起来相对比较复杂,但是可以对资源的表达格式进行定制,使得资源的表达格式更直接。
在代码实现中经常会需要把模型类对象转换成对应的资源对象,如把 List 类的对象转换成 ListResource 类的对象。一般的做法是通过“new ListResource(list)”这样的方式来进行转换。可以使用 Spring HATEOAS 提供的资源组装器把转换的逻辑封装起来。资源组装器还可以自动创建 rel 属性为 self 的链接。  中给出了组装资源类 ListResource 的 ListResourceAssembler 类的实现。
清单 8. 组装资源类 ListResource 的 ListResourceAssembler 类的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ListResourceAssembler extends ResourceAssemblerSupport<List, ListResource> {

   public ListResourceAssembler() {
       super(ListRestController.class, ListResource.class);
   }

   @Override
   public ListResource toResource(List list) {
       ListResource resource = createResourceWithId(list.getId(), list);
       return resource;
   }

   @Override
   protected ListResource instantiateResource(List entity) {
       return new ListResource(entity);
   }
}




在创建 ListResourceAssembler 类的对象时需要指定使用资源的 Spring MVC 控制器 Java 类和资源 Java 类。对于 ListResourceAssembler 类来说分别是 ListRestController 和 ListResource。ListRestController 类在下一节中会具体介绍,其作用是用来创建 rel 属性为 self 的链接。ListResourceAssembler 类的 instantiateResource 方法用来根据一个模型类 List 的对象创建出 ListResource 对象。ResourceAssemblerSupport 类的默认实现是通过反射来创建资源对象的。toResource 方法用来完成实际的转换。此处使用了 ResourceAssemblerSupport 类的 createResourceWithId 方法来创建一个包含 self 链接的资源对象。
在代码中需要创建 ListResource 的地方,都可以换成使用 ListResourceAssembler,如  所示。
清单 9. 使用 ListResourceAssembler 的示例
1
2
3
4
5
//组装单个资源对象
new ListResourceAssembler().toResource(list);

//组装资源对象的集合
new ListResourceAssembler().toResources(lists);




  中的 toResources 方法是 ResourceAssemblerSupport 类提供的。当需要转换一个集合的资源对象时,这个方法非常实用。
返回列表