Spring HATEOAS场景下如何调用带@RequestParam的Controller方法且不传参
我有一个GET接口用于从仓库获取分页歌曲,接口包含page和size两个非必填的@RequestParam参数。当使用Spring HATEOAS生成全量歌曲的链接时,调用该Controller方法并传入null作为参数,得到的是带模板参数的链接(http://localhost:8080/api/songcollection/songs/{?page,size}),但我希望生成不带任何参数的纯链接(http://localhost:8080/api/songcollection/songs)。
现有Controller代码如下:
@GetMapping("/songs/") public CollectionModel<EntityModel<SongAlbumDTO>> getAllSongs( @RequestParam(name = "page", required = false) Integer page, @RequestParam(name = "size",required = false) Integer size ) { if (page != null && size != null) { List<SongAlbumDTO> dtoSongList = songService.findPaginatedSong(page,size).stream() .map(SongAlbumAdapter::toSongAlbumDTO) .collect(Collectors.toList()); List<EntityModel<SongAlbumDTO>> songs = dtoSongList.stream() .map(songAlbum -> EntityModel.of(songAlbum, linkTo(methodOn(SongAlbumController.class).getSongById(songAlbum.getId())).withSelfRel(), linkTo(methodOn(SongAlbumController.class).getAllSongs(null,null)) // 这里需要生成无参数链接 .withRel("songs"))).collect(Collectors.toList()); return CollectionModel.of(songs, linkTo(methodOn(SongAlbumController.class).getAllSongs(null, null)).withSelfRel()); // 这里需要生成无参数链接 } else { List<SongAlbumDTO> list = songService.getAllSongs().stream() .map(SongAlbumAdapter::toSongAlbumDTO) .collect(Collectors.toList()); List<EntityModel<SongAlbumDTO>> songs = list.stream() .map(songAlbum -> EntityModel.of(songAlbum, linkTo(methodOn(SongAlbumController.class).getSongById(songAlbum.getId())).withSelfRel(), linkTo(methodOn(SongAlbumController.class).getAllSongs(null, null)).withRel("songs"))).collect(Collectors.toList()); // 这里需要生成无参数链接 return CollectionModel.of(songs, linkTo(methodOn(SongAlbumController.class).getAllSongs(null,null)).withSelfRel()); // 这里需要生成无参数链接 } }
当前返回的链接格式:
"_links": { "self": { "href": "http://localhost:8080/api/songcollection/songs/1/" }, "songs": { "href": "http://localhost:8080/api/songcollection/songs/{?page,size}", "templated": true } }
期望的songs链接:
http://localhost:8080/api/songcollection/songs
方案1:直接用linkTo路径构建,跳过methodOn
Spring HATEOAS的linkTo支持直接通过Controller类+路径构建链接,不需要调用带参数的方法,这样就能生成无参数的纯路径:
把所有生成链接的代码,比如:
linkTo(methodOn(SongAlbumController.class).getAllSongs(null,null)).withRel("songs")
替换为:
linkTo(SongAlbumController.class).slash("songs").withRel("songs")
如果你的Controller类上有@RequestMapping("/api/songcollection")这类前缀,也可以用:
linkTo(methodOn(SongAlbumController.class).getClass()).slash("songs").withRel("songs")
这样生成的链接就会是不带参数的目标地址。
方案2:新增无参数接口方法,共用路径
为全量查询单独写一个无参数的方法,和分页方法映射到同一个路径,调用这个无参数方法生成链接时,就不会带模板参数:
// 新增无参数全量查询方法 @GetMapping("/songs/") public CollectionModel<EntityModel<SongAlbumDTO>> getAllSongs() { List<SongAlbumDTO> list = songService.getAllSongs().stream() .map(SongAlbumAdapter::toSongAlbumDTO) .collect(Collectors.toList()); List<EntityModel<SongAlbumDTO>> songs = list.stream() .map(songAlbum -> EntityModel.of(songAlbum, linkTo(methodOn(SongAlbumController.class).getSongById(songAlbum.getId())).withSelfRel(), linkTo(methodOn(SongAlbumController.class).getAllSongs()).withRel("songs"))) .collect(Collectors.toList()); return CollectionModel.of(songs, linkTo(methodOn(SongAlbumController.class).getAllSongs()).withSelfRel()); } // 保留原有的分页方法 @GetMapping("/songs/") public CollectionModel<EntityModel<SongAlbumDTO>> getAllSongs( @RequestParam(name = "page", required = false) Integer page, @RequestParam(name = "size",required = false) Integer size ) { if (page != null && size != null) { List<SongAlbumDTO> dtoSongList = songService.findPaginatedSong(page,size).stream() .map(SongAlbumAdapter::toSongAlbumDTO) .collect(Collectors.toList()); List<EntityModel<SongAlbumDTO>> songs = dtoSongList.stream() .map(songAlbum -> EntityModel.of(songAlbum, linkTo(methodOn(SongAlbumController.class).getSongById(songAlbum.getId())).withSelfRel(), linkTo(methodOn(SongAlbumController.class).getAllSongs()).withRel("songs"))) .collect(Collectors.toList()); return CollectionModel.of(songs, linkTo(methodOn(SongAlbumController.class).getAllSongs(page, size)).withSelfRel()); } else { // 无参数时直接调用全量方法 return getAllSongs(); } }
此时调用methodOn(SongAlbumController.class).getAllSongs()生成的就是无参数的链接。
方案3:手动构建Uri生成Link
用UriComponentsBuilder手动拼接路径,再转换成Spring HATEOAS的Link对象,适合路径固定的场景:
// 在需要生成链接的位置,手动构建路径 UriComponents uriComponents = ServletUriComponentsBuilder.fromCurrentContextPath() .path("/api/songcollection/songs") .build(); Link songsLink = Link.of(uriComponents.toUriString()).withRel("songs");
把这个songsLink替换原来用linkTo生成的链接即可。
内容的提问来源于stack exchange,提问作者varo111

