我尝试使用spring-boot-starter-data-rest使用Spring Boot构建RESTful API。有一些实体:帐户,交易,类别和用户 - 只是通常的东西。
当我通过默认生成的API检索http://localhost:8080/transactions处的对象时,一切顺利,我得到一个包含所有事务的列表作为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
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注释来解决这个问题时,我可以检索交易列表,但只能使用这个&#34; classic&#34;格式:
{
"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链接,所有内容都由杰克逊直接序列化。我尝试添加
@EnableHypermediaSupport(type = HypermediaType.HAL)
到实体类,但那并没有让我到任何地方。我只是希望我的控制器返回与生成的API相同的对象,使用HAL _links而不是每个被序列化的引用。有什么想法吗?
修改 好的,在考虑了两次后,我意识到必须将@EnableHypermediaSupport注释添加到配置中。这解决了循环引用的问题,我可以从User中删除@JsonBackReference。但只有对象本身的属性被序列化,没有_links部分:
{
"amount": -4.81,
"date": "2014-06-17T21:18:00.000+0000",
"description": "Pizza"
}
我知道我可以为所有实体编写扩展ResourceSupport的包装类,但这似乎毫无意义。由于spring-hateoas能够神奇地为自动创建的REST接口的_link部分生成表示,因此应该有一种从自定义控制器返回相同表示的方法,对吗?
答案 0 :(得分:23)
这里有很多方面:
我怀疑/transactions
的集合资源是否真的如您所述返回了单个事务。这些表示将返回项目资源。
如果TransactionRepository
已经是PageableAndSortingRepository
,则可以通过展开API根目录中为名为transactions
的链接公开的URI模板来调整收集资源。默认情况下,它是page
,size
和sort
参数。这意味着客户可以请求您想要公开的内容。
如果要默认分页和排序选项,实现控制器是正确的方法。但是,为了实现像Spring Data REST公开的表示,您需要至少返回ResourceSupport
的实例,因为这是HAL映射注册的类型。
ResourcesSupport
和类似Resource<T>
的类型允许您包装实体并使用您认为合适的链接丰富它。 Spring Data REST基本上可以为您使用大量有关域和存储库结构的知识,这些知识可以隐式使用。您可以重复使用,如下所示。
您需要注意一些帮助:
PersistentEntityResourceAssembler
- 通常将其注入控制器方法。它以Spring Data REST方式呈现单个实体,这意味着指向托管类型的关联将呈现为链接等。PagedResourcesAssembler
- 通常注入控制器实例。负责准备页面中包含的项目,可选择使用专用的ResourceAssembler
。Spring Data REST基本上对页面执行的操作如下:
PersistentEntityResourceAssembler entityAssembler = …;
Resources<?> … = pagedResourcesAssembler.toResources(page, entityAssembler);
基本上使用PagedResourcesAssembler
和PersistentEntityResourceAssembler
来渲染项目。
返回Resources
实例应该为您提供您期望的表示设计。
答案 1 :(得分:3)
您无需创建自己的控制器来限制查询结果或对结果进行排序。只需在您的存储库中创建query method:
public interface TransactionRepository extends MongoRepository<Transaction, String> {
List<Transaction> findFirst10ByOrderByDateDesc();
}
Spring Data REST会自动将其导出为/transactions/search/findFirst10ByOrderByDateDesc
的{{3}}。
答案 2 :(得分:1)
要在控制器中使用PersistentEntityResourceAssembler,我们应将其标记为@RepositoryRestController
@RestController
@RequestMapping("/categories")
@RepositoryRestController
public class CategoryController implements ValidableController {
// dependencies
@RequestMapping(method = POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<PersistentEntityResource> create(@Valid @RequestBody CategoryForm category,
BindingResult validation,
PersistentEntityResourceAssembler resourceAssembler)
{
validate(validation);
Category entity = categoryConverter.convert(category);
entity = categoryService.save(entity);
return ResponseEntity.ok(resourceAssembler.toFullResource(entity));
}
它建立了非常漂亮的HAL风格的响应
{
"createdTime": "2018-07-24T00:55:32.854",
"updatedTime": "2018-07-24T00:55:32.855",
"name": "cfvfcdfgdfdfdfs32",
"options": [
"aaa",
"bbb"
],
"_links": {
"self": {
"href": "http://localhost:8080/shop/categories/34"
},
"category": {
"href": "http://localhost:8080/shop/categories/34{?projection}",
"templated": true
},
"products": {
"href": "http://localhost:8080/shop/categories/34/products"
},
"categories": {
"href": "http://localhost:8080/shop/categories/34/categories{?projection}",
"templated": true
},
"parent": {
"href": "http://localhost:8080/shop/categories/34/parent{?projection}",
"templated": true
}
}
}