2016-04-21 75 views
11

我有以下REST存储库,其实现由Spring在运行时生成。Spring数据REST:覆盖控制器上的存储库方法

@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实现的其他方法不再被暴露出来。因此,例如,我可以不再做一个GET请求/富/ 1

问: 是否有覆盖REST方法,同时仍然保持其他自动生成的春方法的一种方式?

额外的信息:

  1. 这个问题似乎很相似: Spring Data Rest: Override Method in RestController with same request-mapping-path ...但我不想路径更改为类似/富/ 1 /保存

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

  3. This part of the Spring Data documentation说以下内容:

    有时你可能需要编写自定义的处理器特定 资源。为了利用Spring数据REST的设置,消息 转换器,异常处理,多的优势,使用 @RepositoryRestController注释,而不是一个标准的Spring MVC @Controller或@RestController

如此看来,它应该开箱即用,但不幸的是没有。

+1

http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/#repositories.single-repository-behaviour这是否也许帮助你? – Tarmo

+0

我意识到这个问题不是Grails的问题,但这个概念与这里描述的问题/答案类似:http://stackoverflow.com/questions/19360559/adding-functionality-to-grails-restfulcontroller – rmlan

+0

@Tarmo :虽然我认为这可能有效,但这会迫使我不断向存储库添加逻辑,并且我更愿意将其保留在服务中。 – Nicolas

回答

8

有没有办法覆盖REST方法,同时仍然保留其他自动生成的Spring方法?

仔细查看文档中的示例:虽然没有明确禁止类级别的请求映射,但它使用方法级别的requestmapping。 我不确定这是想要的行为还是错误,但据我所知,这是使其工作的唯一方法,如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

不幸的是,这也不起作用。如果我这样做,那么Spring实现的GET方法将不起作用。 – Nicolas

+1

似乎由我工作 (spring-boot-starter-data-rest 1.4.1.RELEASE) 此外'@ RepositoryRestController'与'@ RestController'也有所诀窍。 –

+1

还必须将'@ ResponseBody'添加到重写的控制器方法。 –

3

我发现,如果你使用的是Java 8很好地解决 - 只需使用接口的默认方法

@RepositoryRestResource 
public interface FooRepository extends CrudRepository<Foo, Long> { 
    default <S extends T> S save(S var1) { 
     //do some work here 
    } 
} 
+0

这将覆盖'save'方法为此存储库的整个应用程序。如果这不是所需的行为,则不应使用,否则它是一个有效的选项。 – lealceldeiro

5

让我们想象一下,我们有一个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暴露其C在/accounts路德端点:

@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可以是con想通具有以下父和依赖:

<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> 
相关问题