我有以下REST存储库,其实现由Spring在运行时生成。
@RepositoryRestResource
public interface FooRepository extends CrudRepository<Foo, Long> {
}
这意味着我将通过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: Override Method in RestController with same request-mapping-path ...但我不想将路径更改为/ foo / 1 / save
我想过使用@RepositoryEventHandler,但我不是很喜欢这个想法,因为我想把它封装在服务之下。此外,您似乎失去了对事务上下文的控制。
This part of the Spring Data documentation说明如下:
有时您可能希望为特定的编写自定义处理程序 资源。利用Spring Data REST的设置,消息 转换器,异常处理等,使用 @RepositoryRestController注释而不是标准的Spring MVC @Controller或@RestController
所以看起来它应该开箱即用,但不幸的是没有。
答案 0 :(得分:11)
是否有一种覆盖REST方法的方法,同时仍保留其他自动生成的Spring方法?
仔细查看文档中的示例:虽然没有明确禁止类级请求映射,但它使用方法级请求映射。 我不确定这是否是想要的行为或错误,但据我所知,这是使其成功的唯一方法,如here所述。
只需将您的控制器更改为:
@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<Void> updateFoo(@PathVariable Long fooId) {
fooService.updateProperly(fooId);
return ResponseEntity.ok().build(); // simplest use of a ResponseEntity
}
}
答案 1 :(得分:7)
我们假设我们有一个Account
实体:
@Entity
public class Account implements Identifiable<Integer>, 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<Account, Integer> {
}
AccountController
覆盖默认GET
个终结点AccountRepository
。:
@RepositoryRestController
public class AccountController {
private PagedResourcesAssembler<Account> pagedAssembler;
@Autowired
public AccountController(PagedResourcesAssembler<Account> pagedAssembler) {
this.pagedAssembler = pagedAssembler;
}
private Page<Account> getAccounts(Pageable pageRequest){
int totalAccounts= 50;
List<Account> 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<Page<Account>> 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:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-webmvc</artifactId>
<version>2.6.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
答案 2 :(得分:2)
如果您使用的是Java 8,我找到了一个简洁的解决方案 - 只需在接口
中使用默认方法@RepositoryRestResource
public interface FooRepository extends CrudRepository<Foo, Long> {
default <S extends T> S save(S var1) {
//do some work here
}
}
答案 3 :(得分:1)
我发现一个更新挽救了我的性命。 就像@ mathias-dpunkt在这个答案中的出色表现 https://stackoverflow.com/a/34518166/2836627
最重要的是RepositoryRestController知道spring数据剩余 基本路径,并将在此基本路径下投放。
因此,如果您的基本路径是“ / api”,并且您正在使用@RepositoryRestController
您必须从@RequestMapping中省略“ / api”