So the problem is as follows:
- I am exposing API to register user within my service
- Before I am going to register user, I want to validate him in external service (rest API)
- I want to inform user with proper response status whether he was created (201 or some error code)
My code to create user looks like that:
@Transactional
public void createUser(UserDto userDto) {
UserEntity newUser = userFactory.createUser(userDto);
String userId = userDto.getUserId();
externalApiService
.validateUser(userId)
.subscribe(a-> userRepository.save(newUser));
}
and validation of user is as follows:
public Mono<Void> validateUser(String userId) {
return webClient
.get(...)
.retrieve()
.toEntity(Void.class);
//Exact request is bit more complicated, but those details shouldn't be crucial in this example
}
My endpoint that is utilizing this looks like that:
@PostMapping
public ResponseEntity<Void> createUser(@Valid @RequestBody UserDto dto) {
userService.createUser(dto);
return new ResponseEntity<>(HttpStatus.CREATED);
}
This is quite easy when I use blocking RestTemplate
or use block()
on Mono
that I just got from WebClient
. If there is an exception from REST call to external service my global exception handler will do the job and respond user with proper response code.
But what if I want to utilize full power of reactive programming and don't want to block()
. I cannot figure out how to properly do this. Can anyone give me some advice?
And please excuse me if my question is trivial, I am quite new to WebClient
and reactive programming at all.
CodePudding user response:
WebClient
is a reactive client and the only way to use it with non-reactive Spring MVC is to block
.
To make the flow fully non-blocking, all components/functions should be reactive. You would need to switch to Spring WebFlux and also make createUser
reactive.
@PostMapping
public Mono<ResponseEntity<Void>> createUser(@RequestBody UserDto dto) {
return userService.createUser(dto)
.map(user -> ResponseEntity.status(HttpStatus.CREATED).build());
}
This code assumes that createUser
is reactive
public static Mono<UserEntity> createUser(UserDto dto) {
...
}
In case userRepository
is not reactive you could wrap non-reactive code and run it on a separate scheduler How Do I Wrap a Synchronous, Blocking Call?.