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方法?
额外信息:
-
这个问题看起来非常相似: Spring Data Rest:RestController中的覆盖方法,具有相同的请求映射路径 …但我不想将路径更改为/ foo / 1 / save之类的东西
-
我想过使用@RepositoryEventHandler,但我不是很喜欢这个想法,因为我想把它封装在服务之下。 此外,您似乎失去了对事务上下文的控制。
-
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”
- 不兼容的类型和新的类型变量
- 如何对Tomcat上部署的Jersey Web应用程序进行unit testing?
- Maven错误:(重复)java.security.InvalidAlgorithmParameterException:trustAnchors参数必须为非空
- 我在windows中输入了mvn命令,但命令行返回了一个java提示
- Hibernate和@JoinFormula:org.hibernate.mapping.Formula无法强制转换为org.hibernate.mapping.Column
- 序列化包含对象java的对象
- 扫描程序 – java.util.NoSuchElementException
- 无法从START_ARRAY标记中反序列化java.util.HashMap的实例
- LibGDX网格高度图法线和灯光