Spring Data REST:覆盖控制器上的存储库方法

我有以下REST存储库,其实现由Spring在运行时生成。

@RepositoryRestResource public interface FooRepository extends CrudRepository { } 

这意味着我将通过REST提供save(),find(),exists()和其他可用方法。

现在,我想覆盖其中一个方法; 例如,save()。 为此,我将创建一个暴露该方法的控制器,如下所示:

 @RepositoryRestController @RequestMapping("/foo") public class FooController { @Autowired FooService fooService; @RequestMapping(value = "/{fooId}", method = RequestMethod.PUT) public void updateFoo(@PathVariable Long fooId) { fooService.updateProperly(fooId); } } 

问题:如果我启用了这个控制器,那么Spring实现的所有其他方法都不再暴露。 因此,例如,我不能再对/ foo / 1执行GET请求

问题:是否有一种覆盖REST方法的方法,同时仍保留其他自动生成的Spring方法?

额外信息:

  1. 这个问题看起来非常相似: Spring Data Rest:RestController中的覆盖方法,具有相同的请求映射路径 …但我不想将路径更改为/ foo / 1 / save之类的东西

  2. 我想过使用@RepositoryEventHandler,但我不是很喜欢这个想法,因为我想把它封装在服务之下。 此外,您似乎失去了对事务上下文的控制。

  3. Spring Data文档的这一部分说明如下:

    有时您可能希望为特定资源编写自定义处理程序。 要利用Spring Data REST的设置,消息转换器,exception处理等,请使用@RepositoryRestController注释而不是标准的Spring MVC @Controller或@RestController

所以它似乎应该开箱即用,但不幸的是没有。

有没有一种方法可以覆盖REST方法,同时仍保留其他自动生成的Spring方法?

仔细查看文档中的示例:虽然没有明确禁止类级请求映射,但它使用方法级请求映射。 我不确定这是否是想要的行为或错误,但据我所知,这是让它工作的唯一方法,如此处所述。

只需将您的控制器更改为:

 @RepositoryRestController public class FooController { @Autowired FooService fooService; @RequestMapping(value = "/foo/{fooId}", method = RequestMethod.PUT) public void updateFoo(@PathVariable Long fooId) { fooService.updateProperly(fooId); } // edited after Sergey's comment @RequestMapping(value = "/foo/{fooId}", method = RequestMethod.PUT) public RequestEntity updateFoo(@PathVariable Long fooId) { fooService.updateProperly(fooId); return ResponseEntity.ok().build(); // simplest use of a ResponseEntity } } 

我们假设我们有一个Account实体:

 @Entity public class Account implements Identifiable, Serializable { private static final long serialVersionUID = -3187480027431265380L; @Id private Integer id; private String name; public Account(Integer id, String name) { this.id = id; this.name = name; } public void setId(Integer id) { this.id = id; } @Override public Integer getId() { return id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } 

使用AccountRepository/accounts上公开其CRUD端点:

 @RepositoryRestResource(collectionResourceRel = "accounts", path = "accounts") public interface AccountRepository extends CrudRepository { } 

以及覆盖默认GET端点的AccountController形成AccountRepository

 @RepositoryRestController public class AccountController { private PagedResourcesAssembler pagedAssembler; @Autowired public AccountController(PagedResourcesAssembler pagedAssembler) { this.pagedAssembler = pagedAssembler; } private Page getAccounts(Pageable pageRequest){ int totalAccounts= 50; List accountList = IntStream.rangeClosed(1, totalAccounts) .boxed() .map( value -> new Account(value, value.toString())) .skip(pageRequest.getOffset()) .limit(pageRequest.getPageSize()) .collect(Collectors.toList()); return new PageImpl(accountList, pageRequest, totalAccounts); } @RequestMapping(method= RequestMethod.GET, path="/accounts", produces = "application/hal+json") public ResponseEntity> getAccountsHal(Pageable pageRequest, PersistentEntityResourceAssembler assembler){ return new ResponseEntity(pagedAssembler.toResource(getAccounts(pageRequest), (ResourceAssembler) assembler), HttpStatus.OK); } 

如果您调用GET /accounts?size=5&page=0您将获得以下使用模拟实现的输出:

 { "_embedded": { "accounts": [ { "name": "1", "_links": { "self": { "href": "http://localhost:8080/accounts/1" }, "account": { "href": "http://localhost:8080/accounts/1" } } }, { "name": "2", "_links": { "self": { "href": "http://localhost:8080/accounts/2" }, "account": { "href": "http://localhost:8080/accounts/2" } } }, { "name": "3", "_links": { "self": { "href": "http://localhost:8080/accounts/3" }, "account": { "href": "http://localhost:8080/accounts/3" } } }, { "name": "4", "_links": { "self": { "href": "http://localhost:8080/accounts/4" }, "account": { "href": "http://localhost:8080/accounts/4" } } }, { "name": "5", "_links": { "self": { "href": "http://localhost:8080/accounts/5" }, "account": { "href": "http://localhost:8080/accounts/5" } } } ] }, "_links": { "first": { "href": "http://localhost:8080/accounts?page=0&size=5" }, "self": { "href": "http://localhost:8080/accounts?page=0&size=5" }, "next": { "href": "http://localhost:8080/accounts?page=1&size=5" }, "last": { "href": "http://localhost:8080/accounts?page=9&size=5" } }, "page": { "size": 5, "totalElements": 50, "totalPages": 10, "number": 0 } } 

只是为了完整性,可以使用以下父级和依赖项配置POM:

  org.springframework.boot spring-boot-starter-parent 1.5.2.RELEASE    org.springframework.boot spring-boot-starter-web   org.springframework.data spring-data-rest-webmvc 2.6.1.RELEASE   org.springframework.boot spring-boot-starter-data-jpa   com.h2database h2   

如果您使用Java 8,我找到了一个简洁的解决方案 – 只需在界面中使用默认方法

 @RepositoryRestResource public interface FooRepository extends CrudRepository { default  S save(S var1) { //do some work here } } 

只是我发现的更新救了我的命。 正如@ mathias-dpunkt在这个答案中出色地说的那样https://stackoverflow.com/a/34518166/2836627

最重要的是,RepositoryRestController知道spring数据rest基本路径,并将在此基本路径下提供。

因此,如果您的基本路径是“/ api”并且您正在使用@RepositoryRestController

你必须从@RequestMapping中省略“/ api”