我可以让自定义控制器镜像 Spring-Data-Rest / Spring-Hateoas 生成的类的格式吗?
Posted
技术标签:
【中文标题】我可以让自定义控制器镜像 Spring-Data-Rest / Spring-Hateoas 生成的类的格式吗?【英文标题】:Can I make a custom controller mirror the formatting of Spring-Data-Rest / Spring-Hateoas generated classes? 【发布时间】:2014-12-19 17:10:03 【问题描述】:我正在尝试做一些我认为应该非常简单的事情。我有一个Question
对象,使用 spring-boot、spring-data-rest 和 spring-hateoas 设置。所有的基础工作都很好。我想添加一个自定义控制器,它返回一个List<Question>
,其格式与我的Repository
的/questions
url 的GET 格式完全相同,以便两者之间的响应兼容。
这是我的控制器:
@Controller
public class QuestionListController
@Autowired private QuestionRepository questionRepository;
@Autowired private PagedResourcesAssembler<Question> pagedResourcesAssembler;
@Autowired private QuestionResourceAssembler questionResourceAssembler;
@RequestMapping(
value = "/api/questions/filter", method = RequestMethod.GET,
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody PagedResources<QuestionResource> filter(
@RequestParam(value = "filter", required = false) String filter,
Pageable p)
// Using queryDSL here to get a paged list of Questions
Page<Question> page =
questionRepository.findAll(
QuestionPredicate.findWithFilter(filter), p);
// Option 1 - default resource assembler
return pagedResourcesAssembler.toResource(page);
// Option 2 - custom resource assembler
return pagedResourcesAssembler.toResource(page, questionResourceAssembler);
选项 1:依靠提供的 SimplePagedResourceAssembler
这个选项的问题是没有必要的_links
被渲染。如果有解决方案,这将是最简单的解决方案。
选项 2:实现我的开放资源汇编器
此选项的问题在于,根据Spring-Hateoas documentation 实现QuestionResourceAssembler
会导致QuestionResource
最终成为Question
的近重复项,然后汇编程序需要手动复制数据在这两个对象之间,我需要手动构建所有相关的_links
。这似乎浪费了很多精力。
怎么办?
我知道 Spring 在导出 QuestionRepository
时已经生成了执行所有这些操作的代码。有什么方法可以利用该代码并使用它,以确保控制器的输出与生成的响应无缝且可互换?
【问题讨论】:
【参考方案1】:我找到了一种完全模仿 Spring Data Rest 行为的方法。诀窍在于使用PagedResourcesAssembler
和PersistentEntityResourceAssembler
的参数注入实例的组合。只需按如下方式定义您的控制器...
@RepositoryRestController
@RequestMapping("...")
public class ThingController
@Autowired
private PagedResourcesAssembler pagedResourcesAssembler;
@SuppressWarnings("unchecked") // optional - ignores warning on return statement below...
@RequestMapping(value = "...", method = RequestMethod.GET)
@ResponseBody
public PagedResources<PersistentEntityResource> customMethod(
...,
Pageable pageable,
// this gets automatically injected by Spring...
PersistentEntityResourceAssembler resourceAssembler)
Page<MyEntity> page = ...;
...
return pagedResourcesAssembler.toResource(page, resourceAssembler);
这要归功于 PersistentEntityResourceAssemblerArgumentResolver
的存在,Spring 使用它为您注入 PersistentEntityResourceAssembler
。结果正是您对存储库查询方法之一的期望!
【讨论】:
很好,我试试看。 另见***.com/questions/31758862/…。 小补充:可以防止未经检查的警告,当你的方法直接返回PagedResource<MyEntity>
这是(paged)resourceAssembler.toResource返回的
这有效,除非没有找到结果。在这种情况下,REST 响应中没有 _embedded
键,这与自动生成的 REST/HATEOAS 控制器不同。在这种情况下,我不得不手动调用pagedResourcesAssembler.toEmptyResource
。
@alalonde 可能 Spring Data REST 行为已更改,但如果生成的 HATEOAS 存储库中的集合为空,我也不会得到 _embedded
。【参考方案2】:
更新了这个老问题的答案:您现在可以通过PersistentEntityResourceAssembler
做到这一点
在您的 @RepositoryRestController 中:
@RequestMapping(value = "somePath", method = POST)
public @ResponseBody PersistentEntityResource postEntity(@RequestBody Resource<EntityModel> newEntityResource, PersistentEntityResourceAssembler resourceAssembler)
EntityModel newEntity = newEntityResource.getContent();
// ... do something additional with new Entity if you want here ...
EntityModel savedEntity = entityRepo.save(newEntity);
return resourceAssembler.toResource(savedEntity); // this will create the complete HATEOAS response
【讨论】:
是否可以在控制器方法参数中接受指向现有资源的链接并自动将其转换为实体? 是的,如果您的实体链接到另一个“子”实体,那么您可以简单地发布一个 URI 来创建该链接。例如 HTTP POST /path/to/parent/entity Payload 用于 ecamplesomeAttr: "example Value", linkToChildEntity: "/path/to/child/entity/<id>"
希望这会有所帮助。有关更多详细信息,请参阅this *** question
我的意思是在自定义控制器中。我知道,Spring data rest 做到了。问题是,我怎样才能编写一个可以做同样事情的控制器?
如何处理空值? ... "PersistentEntity 不能为空!"
我也不确定如何处理集合,例如GET/findAll 操作。请您提供一些有效的例子吗?【参考方案3】:
我相信我已经以一种相当直接的方式解决了这个问题,尽管它本可以得到更好的记录。
阅读SimplePagedResourceAssembler
的实现后,我意识到混合解决方案可能有效。提供的Resource<?>
类可以正确呈现实体,但不包含链接,因此您只需添加它们即可。
我的QuestionResourceAssembler
实现如下所示:
@Component
public class QuestionResourceAssembler implements ResourceAssembler<Question, Resource<Question>>
@Autowired EntityLinks entityLinks;
@Override
public Resource<Question> toResource(Question question)
Resource<Question> resource = new Resource<Question>(question);
final LinkBuilder lb =
entityLinks.linkForSingleResource(Question.class, question.getId());
resource.add(lb.withSelfRel());
resource.add(lb.slash("answers").withRel("answers"));
// other links
return resource;
完成后,在我的控制器中,我使用了上面的 选项 2:
return pagedResourcesAssembler.toResource(page, questionResourceAssembler);
这很好用,而且代码也不多。唯一的麻烦是您需要为您需要的每个参考手动添加链接。
【讨论】:
当然,Spring 似乎可以让这变得容易得多。您必须添加自己的链接这一事实是否仍会导致 API 可能脱节(就像您在其他地方指出的那样)?你必须在任何地方都使用 Pages 吗?当我尝试返回 List以上是关于我可以让自定义控制器镜像 Spring-Data-Rest / Spring-Hateoas 生成的类的格式吗?的主要内容,如果未能解决你的问题,请参考以下文章
如何让自定义导航控制器为每个故事板提供相同的自定义导航栏按钮
无法让自定义 RKBlockValueTransformer 与 RestKit 一起正常工作
在 NextJS 中是不是可以让自定义 _app.js 读取 slug、getInitialProps 并将这些道具传递给每个组件,包括所有页面?