我正在尝试使用 spring-boot-starter-data-rest 使用 Spring Boot 构建一个 RESTful API。有一些实体:帐户、交易、类别和用户 - 只是普通的东西。
当我在 http://localhost:8080/transactions 检索对象时通过默认生成的 API,一切顺利,我得到了一个列表,其中包含所有事务作为 JSON 对象,如下所示:
{
"amount": -4.81,
"date": "2014-06-17T21:18:00.000+0000",
"description": "Pizza",
"_links": {
"self": {
"href": "http://localhost:8080/transactions/5"
},
"category": {
"href": "http://localhost:8080/transactions/5/category"
},
"account": {
"href": "http://localhost:8080/transactions/5/account"
}
}
}
但现在的目标是只检索该 URL 下的最新事务,因为我不想序列化整个数据库表。于是我写了一个Controller:
@Controller
public class TransactionController {
private final TransactionRepository transactionRepository;
@Autowired
public TransactionController(TransactionRepository transactionRepository) {
this.transactionRepository = transactionRepository;
}
// return the 5 latest transactions
@RequestMapping(value = "/transactions", method = RequestMethod.GET)
public @ResponseBody List<Transaction> getLastTransactions() {
return transactionRepository.findAll(new PageRequest(0, 5, new Sort(new Sort.Order(Sort.Direction.DESC, "date")))).getContent();
}
}
当我现在尝试访问 http://localhost:8080/transactions 时有一个
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
因为用户和帐户之间的循环引用。当我通过向用户中的帐户列表添加@JsonBackReference 注释来解决此问题时,我可以检索交易列表,但只能使用这种“经典”格式:
{
"id": 5,
"amount": -4.5,
"date": "2014-06-17T21:18:00.000+0000",
"description": "Pizza",
"account": {
"id": 2,
"name": "Account Tilman",
"owner": {
"id": 1,
"name": "Tilman"
},
"categories": [
{
"id": 1,
"name": "Groceries"
},
{
"id": 2,
"name": "Restaurant"
}
],
"users": [
{
"id": 1,
"name": "Tilman"
}
]
},
"category": {
"id": 2,
"name": "Restaurant"
}
}
不再有 HAL 链接,一切都由 jackson 直接序列化。我尝试添加
@EnableHypermediaSupport(type = HypermediaType.HAL)
到实体类,但这并没有让我到任何地方。我只希望我的 Controller 返回与生成的 API 相同的对象,使用 HAL _links 而不是序列化每个引用。有什么想法吗?
编辑: 好的,经过深思熟虑,我意识到@EnableHypermediaSupport 注释必须添加到配置,当然。这解决了循环引用的问题,我可以从用户中删除 @JsonBackReference。但是只有对象本身的属性被序列化,没有_links部分:
{
"amount": -4.81,
"date": "2014-06-17T21:18:00.000+0000",
"description": "Pizza"
}
我知道我可以为我的所有实体编写扩展 ResourceSupport 的包装类,但这似乎毫无意义。由于 spring-hateoas 能够神奇地使用自动创建的 REST 接口(interface)的 _link 部分生成表示,因此应该有一种方法可以从自定义 Controller 返回相同的表示,对吧?
最佳答案
这里有很多方面:
我怀疑
/transactions
的收藏资源如您所述,确实返回了单个交易。为项目资源返回这些表示。如果
TransactionRepository
已经是PageableAndSortingRepository
可以通过扩展在 API 根中为名为transactions
的链接公开的 URI 模板来调整集合资源.默认情况下是page
,size
和sort
范围。这意味着客户已经可以请求您想要公开的内容。如果你想默认分页和排序选项,实现 Controller 是正确的方法。但是,要实现像 Spring Data REST 公开这样的表示,您至少需要返回
ResourceSupport
的实例。因为这是注册 HAL 映射的类型。如果你仔细想想,这里并没有什么神奇之处。普通实体没有任何链接,
ResourcesSupport
和类型如Resource<T>
允许您包装实体并使用您认为合适的链接来丰富它。 Spring Data REST 基本上为您使用大量关于隐式可用的域和存储库结构的知识。您可以重复使用很多,如下所示。这里有一些你需要注意的助手:
-
PersistentEntityResourceAssembler
- 通常注入(inject)到 Controller 方法中。它以 Spring Data REST 方式呈现单个实体,这意味着指向托管类型的关联将呈现为链接等。 -
PagedResourcesAssembler
- 通常注入(inject) Controller instance。负责准备页面中包含的项目,可选择使用专用的ResourceAssembler
.
Spring Data REST 对页面的基本作用如下:
PersistentEntityResourceAssembler entityAssembler = …; Resources<?> … = pagedResourcesAssembler.toResources(page, entityAssembler);
这基本上是使用
PagedResourcesAssembler
与PersistentEntityResourceAssembler
渲染项目。返回
Resources
实例应该会给你你期望的表示设计。-
关于spring - 在 Spring Boot 中为自定义 Controller 方法启用 HAL 序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31758862/