Reputation: 31
I want to override the PATCH method of a resource in spring-data-rest with my own controller. I currently have this:
@RepositoryRestResource
interface ItemRepository extends JpaRepository<Item, Long> {
}
@RepositoryRestController
@Slf4j
class ItemController {
@Autowired
private ItemRepository itemRepository;
@PatchMapping("/items/{id}")
public void updateItem(@PathVariable("id") Long id, @RequestBody ItemDTO itemDTO) {
log.info("Updating item {}", id);
Item found = itemRepository.findOne(id);
found.name = itemDTO.name;
itemRepository.save(found);
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class ItemDTO {
String name;
}
I can create an item by calling:
curl -X POST \
http://localhost:8080/items \
-H 'Content-Type: application/json' \
-d '{
"name" : "anItem"
}'
But if I then try to update the item with PATCH
as such:
curl -X PATCH \
http://localhost:8080/items/1 \
-H 'Content-Type: application/json' \
-d '{
"name" : "update"
}'
The request is handled by my own controller, but the application also throws this stacktrace and responds with status code 400.
2018-02-27 16:58:55.510 ERROR 16112 --- [o-auto-1-exec-1] o.s.d.r.w.RepositoryRestExceptionHandler : I/O error while reading input message; nested exception is java.io.IOException: Stream closed
org.springframework.http.converter.HttpMessageNotReadableException: I/O error while reading input message; nested exception is java.io.IOException: Stream closed
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:229) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:150) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:128) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.27.jar:8.5.27]
I've created a sample project here (with a test that demonstrates the error): https://github.com/JanRenkin/springdatarest
Upvotes: 3
Views: 898
Reputation: 1
Fazel is correct. You must use BasePathAwareController to OVERRIDE existing default rest implementations. You can use @RepositoryRestController if you want to add additional endpoints but since /items/{id} already exist by default (due to to having @RepositoryRestResource on your repo class), you can't not.
What is happening here is both implementations are being invoked which causes this stream error since you can only read from the stream once. The stream is being consumed by ItemController therefore when the default implementation tries to read from it, it cant.
Upvotes: 0
Reputation: 161
With @BasePathAwareController annotation you can overwrite the default implementation of spring data rest paths. to avoid occurring error your method should return that entity.
@BasePathAwareController
@Slf4j
class ItemController {
@Autowired
private ItemRepository itemRepository;
@PatchMapping("/items/{id}")
public ResponseEntity<Item> updateItem(@PathVariable("id") Long id, @RequestBody
ItemDTO itemDTO) {
log.info("Updating item {}", id);
Item found = itemRepository.findOne(id);
found.name = itemDTO.name;
Item savedItem =itemRepository.save(found);
return new ResponseEntity<>(savedItem , HttpStatus.OK);
}
}
Upvotes: 0