我有一个Spring Data Rest应用程序,它发送了一个作为Rest端点公开的实体。
我的主要实体是Listing
,它包括Item
的列表:
@Entity
@Data
@AllArgsConstructor
@RequiredArgsConstructor
public class Listing extends RepresentationModel<Listing> {
@Id
@GeneratedValue
private Long id;
private int type;
private String name;
private String description;
.
.
.
@JsonIgnore
@OneToMany(mappedBy = "listing")
private List<Item> items;
}
@Entity
@Data
@AllArgsConstructor
@RequiredArgsConstructor
public class Item extends RepresentationModel<Item> {
@Id
@GeneratedValue
private Long id;
private String title;
private int quantity;
private float price;
.
.
.
@ManyToOne
@JoinColumn(name="listing_id")
public Listing listing;
}
每个实体都有自己的Spring数据存储库:
public interface ItemRepository extends CrudRepository<Item, Long> {
}
public interface ListingRepository extends CrudRepository<Listing, Long> {
}
如果我从http://<IP>:<PORT>/listings
中获取所有列表,正如预期的那样,我会得到以下json:
curl "http://192.168.99.100:8080/listings"
{
"_embedded" : {
"listings" : [ {
"type" : 0,
"name" : "one",
"description" : "this is one",
"_links" : {
"self" : {
"href" : "http://192.168.99.100:8080/listings/1"
},
"listing" : {
"href" : "http://192.168.99.100:8080/listings/1"
},
"items" : {
"href" : "http://192.168.99.100:8080/listings/1/items"
}
}
},
.
.
.
如果我创建了一个自定义控制器,thoug:
@RestController
public class CustomController{
@Autowired
private ListingRepository repository;
@GetMapping("/matches")
public Iterable<Listing> getMatches() {
Iterable<Listing> listings = repository.findAll();
return listings;
}
.
.
.
调用http://<IP>:<PORT>/matches
(理论上应该返回与listings
端点相同的结果(,我得到以下json:
[
{
"id": 1,
"type": 0,
"name": "one",
"description": "this is one",
"links": []
},
.
.
.
也就是说,有两个区别:(1(我有一个";链接与"链接"_链接";字段中的/匹配端点中的(2;链接";总是空的。如何获得更一致的结果?
以下是我的pom.xml的相关部分:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
.
.
.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
.
.
.
</dependencies>
注意:在Listing
实体中,我必须将@JsonIgnore
放入items集合,因为我得到了一个无限的输出,其中一个列表包括items,每个列表都包括listing,再次包括items等。
(2,最重要("链接";总是空的。
@RestController
的手动使用使开发人员能够直接控制端点的工作方式。在这种情况下,spring
不能仅仅接管并实现另一个链接工作流。您必须提供用于在每个响应中创建链接的代码。
您需要添加依赖项
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
然后将控制器修改为
@GetMapping("/matches")
public Iterable<EntityModel<Listing>> getMatches() {
Iterable<Listing> listings = repository.findAll();
return Stream.of(listings).map(listing ->
EntityModel.of(listing,
linkTo(methodOn(CustomController.class).findOne(id)).withSelfRel()
).collect(Collectors.toList())
return listings;
}
如果你仔细检查,你会明白
linkTo(methodOn(CustomController.class).findOne(id)).withSelfRel()
为要从该控制器检索的每个特定CCD_ 10创建链接。为了将其提供给用户,还应该从同一控制器公开这样的端点。否则就没有任何意义了。因此,在同一控制器中,您还需要提供
@GetMapping("/listings/{id}")
EntityModel<Listing> findOne(@PathVariable Long id) {
Listing listing = repository.findById(id) //
.orElseThrow(() -> new RunntimeException(id));
return EntityModel.of(listing, //
linkTo(methodOn(CustomController.class).findOne(id)).withSelfRel()
);
}
以上只是关于如何为每个Listing
响应提供1个链接的示例,用户可以使用该链接直接从控制器检索特定的Listing
。了解了这一点,您现在可以构建您期望交付的所有链接。
另一方面,当您插入spring-data-rest
时,默认情况下,所有这些功能都是由spring
提供的,就像一个黑盒,您可以有限地进行配置。当您使用普通控制器时,您仍然有hateoas-support
,但功能需要由您手动实现,按照您期望的方式工作。