Spring Data REST不一致的自定义控制器要点



我有一个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等。

CustomController中/匹配端点中的

(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,但功能需要由您手动实现,按照您期望的方式工作。

最新更新