Spring data REST: nadpisywanie metody repozytorium na kontrolerze


Mam następujące repozytorium REST, którego implementacja jest generowana w czasie wykonywania przez Spring.
@RepositoryRestResource
public interface FooRepository extends CrudRepository<Foo, Long> {}

Oznacza to, że będę miał save (), find (), exist () i inne metody dostępne i ujawnione przez REST.
Teraz chciałbym zmienić jedną z metod; na przykład save (). Aby to zrobić, utworzyłbym kontroler, który ujawnia tę metodę, na przykład:
@RepositoryRestController
@RequestMapping("/foo")
public class FooController { @Autowired
FooService fooService;
@RequestMapping(value = "/{fooId}", method = RequestMethod.PUT)
public void updateFoo(@PathVariable Long fooId) {
fooService.updateProperly(fooId);
}}


Problem:

jeśli włączę ten kontroler, wszystkie inne metody zaimplementowane przez Spring nie będą już dostępne. Na przykład nie mogę już wysłać żądania GET do/foo/1

Pytanie:

czy istnieje sposób na przesłonięcie metod REST przy jednoczesnym zachowaniu innych automatycznie generowanych metod Spring?

Dodatkowe informacje

:
  • To pytanie wydaje się być bardzo podobne: Spring Data Rest: przesłanianie metody w RestController tą samą ścieżką mapowania żądań https://coderoad.ru/28455208/... ale nie chcę zmieniać ścieżki na coś takiego jak/foo/1/save
  • Myślałem, że użyję @RepositoryEventHandler, ale nie podoba mi się ten pomysł, ponieważ chciałbym zamknąć go w usłudze. Wydaje się również, że tracisz kontrolę nad kontekstem transakcji.
  • W tej części dokumentacji danych http://docs.spring.io/spring-d ... dlersWiosna mówi, co następuje: Czasami może być konieczne napisanie niestandardowego programu obsługi dla określonego pliku ratunek. Aby skorzystać z dostosowań Spring Data REST, konwertery wiadomości, obsługa wyjątków i nie tylko Adnotacja @RepositoryRestController zamiast standardowego Spring MVC @Controller lub @RestController

więc wygląda na to, że powinno to działać po wyjęciu z pudełka, ale niestety tak nie jest.
Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Czy istnieje sposób na przesłonięcie metod REST przy jednoczesnym zachowaniu innych automatycznie generowanych metod Spring?

Przyjrzyj się bliżej przykładowi w dokumentacji: chociaż wyraźnie nie zabrania on dopasowywania zapytań na poziomie klasy, używa dopasowywania zapytań na poziomie metody.
Nie jestem pewien, czy jest to pożądane zachowanie, czy błąd, ale o ile wiem, jest to jedyny sposób, aby działało zgodnie z opisem

tutaj
https://jira.spring.io/browse/DATAREST-535
.
Po prostu zmień kontroler na:
@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
}
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Wyobraźmy sobie, że mamy jednostkę
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;
}
}

Dzięki
AccountRepository
ujawniając swoje punkty końcowe CRUD do
/ accounts
:
@RepositoryRestResource(collectionResourceRel = "accounts", path = "accounts")
public interface AccountRepository extends CrudRepository<Account, Integer> {
}

I
AccountController
, który zastępuje domyślny kształt punktu końcowego
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);
}

Jeśli zadzwonisz do
GET/accounts? Size = 5 & amp; page = 0
, otrzymasz następujące dane wyjściowe, które wykorzystują układ implementacji:
{
"_embedded": {
"accounts": [
{
"name": "1",
"_links": {
"self": {
"href": "[url=http://localhost:8080/accounts/1"]http://localhost:8080/accounts/1"[/url]
},
"account": {
"href": "[url=http://localhost:8080/accounts/1"]http://localhost:8080/accounts/1"[/url]
}
}
},
{
"name": "2",
"_links": {
"self": {
"href": "[url=http://localhost:8080/accounts/2"]http://localhost:8080/accounts/2"[/url]
},
"account": {
"href": "[url=http://localhost:8080/accounts/2"]http://localhost:8080/accounts/2"[/url]
}
}
},
{
"name": "3",
"_links": {
"self": {
"href": "[url=http://localhost:8080/accounts/3"]http://localhost:8080/accounts/3"[/url]
},
"account": {
"href": "[url=http://localhost:8080/accounts/3"]http://localhost:8080/accounts/3"[/url]
}
}
},
{
"name": "4",
"_links": {
"self": {
"href": "[url=http://localhost:8080/accounts/4"]http://localhost:8080/accounts/4"[/url]
},
"account": {
"href": "[url=http://localhost:8080/accounts/4"]http://localhost:8080/accounts/4"[/url]
}
}
},
{
"name": "5",
"_links": {
"self": {
"href": "[url=http://localhost:8080/accounts/5"]http://localhost:8080/accounts/5"[/url]
},
"account": {
"href": "[url=http://localhost:8080/accounts/5"]http://localhost:8080/accounts/5"[/url]
}
}
}
]
},
"_links": {
"first": {
"href": "[url=http://localhost:8080/accounts?page=0&size=5"]http://localhost:8080/accounts ... ot%3B[/url]
},
"self": {
"href": "[url=http://localhost:8080/accounts?page=0&size=5"]http://localhost:8080/accounts ... ot%3B[/url]
},
"next": {
"href": "[url=http://localhost:8080/accounts?page=1&size=5"]http://localhost:8080/accounts ... ot%3B[/url]
},
"last": {
"href": "[url=http://localhost:8080/accounts?page=9&size=5"]http://localhost:8080/accounts ... ot%3B[/url]
}
},
"page": {
"size": 5,
"totalElements": 50,
"totalPages": 10,
"number": 0
}
}

Aby uzyskać kompletność, POM można skonfigurować za pomocą następujących elementów nadrzędnych i zależności:
<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>
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Tylko aktualizacja, którą znalazłem, uratowała mi życie.
Jak wspaniale powiedział @ mathias-dpunkt w tej odpowiedzi

https://stackoverflow.com/a/34518166/2836627
https://stackoverflow.com/a/34518166/2836627Najważniejsze, co RepositoryRestController wie o wiosennym odpoczynku danych
podstawowa ścieżka i będzie obsługiwana pod tą ścieżką podstawową.

Więc jeśli twoja podstawowa ścieżka to „/ api” i używasz @RepositoryRestController
musisz pominąć „/ api” w @RequestMapping
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Znalazłem fajne rozwiązanie, jeśli używasz Java 8 - po prostu użyj domyślnych metod w interfejsie
@RepositoryRestResource
public interface FooRepository extends CrudRepository<Foo, Long> {
default <S extends T> S save(S var1) {
//do some work here
}
}

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się