在Spring Boot中为自定义控制器方法启用HAL序列化
我正在尝试使用SpringBootStarter数据rest构建一个带有SpringBoot的RESTfulAPI。有一些实体:账户、交易、类别和用户——只是普通的东西 当我通过默认生成的API检索处的对象时,一切进展顺利,我得到一个列表,其中所有事务都是JSON对象,如下所示:在Spring Boot中为自定义控制器方法启用HAL序列化,spring,spring-mvc,spring-boot,spring-data-rest,spring-hateoas,Spring,Spring Mvc,Spring Boot,Spring Data Rest,Spring Hateoas,我正在尝试使用SpringBootStarter数据rest构建一个带有SpringBoot的RESTfulAPI。有一些实体:账户、交易、类别和用户——只是普通的东西 当我通过默认生成的API检索处的对象时,一切进展顺利,我得到一个列表,其中所有事务都是JSON对象,如下所示: { "amount": -4.81, "date": "2014-06-17T21:18:00.000+0000", "description": "Pizza", "_links": { "s
{
"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();
}
}
因为用户和帐户之间的循环引用。当我通过向User中的帐户列表添加@JsonBackReference注释来解决此问题时,我可以检索事务列表,但只能使用此“经典”格式:
不再有HAL链接,所有内容都由jackson直接序列化。我试着加上
@EnableHypermediaSupport(type = HypermediaType.HAL)
到实体类,但这并没有让我得到任何东西。我只希望我的控制器返回与生成的API相同的对象,使用HAL\u链接,而不是序列化每个引用。有什么想法吗
编辑:
好的,在仔细考虑之后,我意识到@EnableHypermediaSupport注释当然必须添加到配置中。这解决了循环引用的问题,我可以从用户中删除@JsonBackReference。但是只有对象本身的属性被序列化,没有_links部分:
{
"amount": -4.81,
"date": "2014-06-17T21:18:00.000+0000",
"description": "Pizza"
}
我知道我可以编写包装器类来扩展我所有实体的ResourceSupport,但这似乎毫无意义。由于spring hateoas能够通过自动创建的REST接口的_link部分神奇地生成表示,因此应该有一种方法可以从自定义控制器返回相同的表示,对吗?您不需要创建自己的控制器来限制查询结果或对结果进行排序。只需在存储库中创建一个:
public interface TransactionRepository extends MongoRepository<Transaction, String> {
List<Transaction> findFirst10ByOrderByDateDesc();
}
public interface TransactionRepository扩展了MongoRepository{
列出findFirst10ByOrderByDateDesc();
}
Spring Data REST将自动将其导出为at
/transactions/search/findFirst10ByOrderByDateDesc
这里有很多方面:
/transactions
上的收集资源是否真的返回了您描述的单个事务。将为项目资源返回这些表示TransactionRepository
已经是一个PageableAndSortingRepository
,则可以通过扩展API根目录中为名为transactions
的链接公开的URI模板来调整集合资源。默认情况下,这是一个页面
、大小
和排序
参数。这意味着客户端可以请求您想要公开的内容ResourcesSupport
和Resource
等类型允许您根据需要包装实体并使用链接对其进行丰富。SpringDataREST基本上是通过使用大量关于域和存储库结构的知识来实现这一点的,这些知识是隐式可用的。如下图所示,您可以重复使用很多
您需要注意以下几点:
-通常注入控制器方法。它以Spring数据REST方式呈现单个实体,这意味着指向托管类型的关联将呈现为链接等PersistentEntityResourcesAssembler
-通常注入控制器实例。负责准备页面中包含的项目,可以选择使用专用的PagedResourcesAssembler
ResourceAssembler
PersistentEntityResourceAssembler entityAssembler = …;
Resources<?> … = pagedResourcesAssembler.toResources(page, entityAssembler);
PersistentEntityResourcesAssembler entityAssembler=…;
Resources…=pagedResourcesAssembler.toResources(第页,entityAssembler);
这基本上是使用PagedResourcesAssembler
和persistenentEntityResourcesAssembler
来呈现项目
返回Resources
实例将为您提供所需的表示设计要在控制器中使用PersistentEntityResourcesAssembler,我们应该将其标记为@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));
}
}谢谢,这正是我想要的!(方法名称必须是findFirst10ByOrderByDateDesc(),这使方法资源在可用)这非常有用,谢谢!
ResourcesSupport
是否有文档记录?是的:@OliverGierke非常感谢!关于您的评论:(1)是的,资源当然会返回一个列表。我只是缩短了输出以说明格式。(2.)是的,我有一个分页和排序存储库,但正如您所猜测的,我想(3.)为客户提供并消除一次查询整个数据库表的可能性。@OliverGierke再次感谢!可能是我,但我从未考虑过寻找SpringHateOAS文档。我刚才发现的SpringDataREST文档中有一个脚注。可能会添加一小段“如果您想创建自己的与HATEAOS集成的控制器,请返回ResourcesSupport
,有关详细信息,请参阅Spring HATEAOS文档”?@Tilman您还可以通过使用@RestResource(exported=false)对“findAll()”方法进行注释来禁用该方法的导出
并且仍然使用查询方法。
PersistentEntityResourceAssembler entityAssembler = …;
Resources<?> … = pagedResourcesAssembler.toResources(page, entityAssembler);
@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));
}
{
"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
}
}