Reputation: 388
I need to add additional business-logic for POST method. For now, I'm going to reuse the logic from RepositoryEntityController for getting and save the needed object.
@RepositoryRestController
@RequestMapping("/customPost")
public class UserController implements ApplicationEventPublisherAware {
private final UserRepository userRepository;
private final RepositoryRestConfiguration config;
private final HttpHeadersPreparer headersPreparer;
private ApplicationEventPublisher publisher;
@Autowired
public UserController(UserRepository userRepository, RepositoryRestConfiguration config, HttpHeadersPreparer headersPreparer) {
this.userRepository = userRepository;
this.config = config;
this.headersPreparer = headersPreparer;
}
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@ResponseBody
@RequestMapping(
value = {"/{repository}"},
method = {RequestMethod.POST}
)
public ResponseEntity<ResourceSupport> postCollectionResource(PersistentEntityResource payload, PersistentEntityResourceAssembler assembler, @RequestHeader(value = "Accept", required = false) String acceptHeader) throws HttpRequestMethodNotSupportedException {
return this.createAndReturn(payload.getContent(), assembler, this.config.returnBodyOnCreate(acceptHeader));
}
private ResponseEntity<ResourceSupport> createAndReturn(Object domainObject, PersistentEntityResourceAssembler assembler, boolean returnBody) {
publisher.publishEvent(new BeforeCreateEvent(domainObject));
Object savedObject = userRepository.save((User) domainObject);
publisher.publishEvent(new AfterCreateEvent(savedObject));
PersistentEntityResource resource = returnBody ? assembler.toFullResource(savedObject) : null;
HttpHeaders headers = headersPreparer.prepareHeaders(resource);
addLocationHeader(headers, assembler, savedObject);
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, headers, resource);
}
private void addLocationHeader(HttpHeaders headers, PersistentEntityResourceAssembler assembler, Object source) {
String selfLink = assembler.getSelfLinkFor(source).getHref();
headers.setLocation((new UriTemplate(selfLink)).expand(new Object[0]));
}
}
The code which I sent - is working ok. But the issue is that I need to add some request mapping @RequestMapping("/customPost") to controller.
Without this mapping - method will not work. I tried to have the same controller but without "/customPost" mapping. I got this exception at start of applicaton:
caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'repositoryEntityController' method
public org.springframework.http.ResponseEntity<org.springframework.hateoas.ResourceSupport> org.springframework.data.rest.webmvc.RepositoryEntityController.postCollectionResource(org.springframework.data.rest.webmvc.RootResourceInformation,org.springframework.data.rest.webmvc.PersistentEntityResource,org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler,java.lang.String) throws org.springframework.web.HttpRequestMethodNotSupportedException
to {[/{repository}],methods=[POST],produces=[application/hal+json || application/json]}: There is already 'userController' bean method
public org.springframework.http.ResponseEntity<org.springframework.hateoas.ResourceSupport> com.project.controller.UserController.postCollectionResource(org.springframework.data.rest.webmvc.PersistentEntityResource,org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler,java.lang.String) throws org.springframework.web.HttpRequestMethodNotSupportedException mapped.
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.assertUniqueMethodMapping(AbstractHandlerMethodMapping.java:576) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.register(AbstractHandlerMethodMapping.java:540) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerHandlerMethod(AbstractHandlerMethodMapping.java:264) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
I also tried to remove "/customPost" mapping from controller and change mapping in metod to "/users". But for this case, I have this exception:
java.lang.NullPointerException: null
at org.springframework.data.rest.webmvc.config.RootResourceInformationHandlerMethodArgumentResolver.resolveArgument(RootResourceInformationHandlerMethodArgumentResolver.java:86) ~[spring-data-rest-webmvc-2.6.10.RELEASE.jar:na]
at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.resolveArgument(PersistentEntityResourceHandlerMethodArgumentResolver.java:113) ~[spring-data-rest-webmvc-2.6.10.RELEASE.jar:na]
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.doPost(FrameworkServlet.java:872) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) ~[tomcat-embed-core-8.5.27.jar:8.5.27]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[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]
The question is: how can I have needed logic without adding "/customPost" mapping?
Upvotes: 3
Views: 1843
Reputation: 30309
Spring Data REST emits its own events when working with entities:
You can listen to these events to add your additional business logic. For example, to perform something after the SDR creates a new User
(with POST method) or saves (updates) the existent User
(with PUT/PATCH methods), you can use such a handler:
@Component
@RepositoryEventHandler
public class UserEventHandler {
private final UserServie userServie;
public UserEventHandler(UserServie userServie) {
this.userServie = userServie;
}
@HandleAfterCreate
public void handleAfterCreateUser(User user) {
userService.afterCreate(user)
}
@HandleAfterSave
public void handleAfterSaveUser(User user) {
userService.afterSave(user)
}
}
P.S. If I'm not mistaken, SDR emits these events outside the current transactions, only before it is started or after it is committed. You need to take this into account when implementing your business logic...
Upvotes: 5