6
votes

I have been working on a sample reactive web api using Spring Boot 2.0.1 and its Webflux library. I have been looking at examples from online to try to build it, but I am stumped on two things. Below are the two questions I have.

1) How do I return a Flux of Response Entities, when I try I get an error saying that only a Single Response Entity can be returned. Below is my current code.

@Service
public class MovieServiceImpl implements MovieService {

    @Autowired
    private MovieRepository movieRepository;

    @Override
    public Flux<Movie> list(){
        return movieRepository.findAll();
    }
}

@RestController
public class MovieRestController {

    @Autowired
    private MovieService movieService;

    @GetMapping(value = "/movies")
    public Flux<Movie> list() {

        return movieService.list();
    }
}

2) When I Update an object I use a flatMap to update the object saved in Mongo, and then a Map to turn it to a Response Entity. My question is why am I using flatMap here instead of map? I derived this code from online examples, but no example explained the use of flatMap. I would like to understand why it is being used here. Below is the code.

@Service
public class MovieServiceImpl implements MovieService {

    @Autowired
    private MovieRepository movieRepository;

    @Override
    public Mono<Movie> update(String id, MovieRequest movieRequest) {

       return movieRepository.findById(id).flatMap(existingMovie -> {

           if(movieRequest.getDescription() != null){
               existingMovie.setDescription(movieRequest.getDescription());
           }
           if(movieRequest.getRating() != null){
               existingMovie.setRating(movieRequest.getRating());
           }
           if(movieRequest.getTitle() != null) {
               existingMovie.setTitle(movieRequest.getTitle());
           }

           return movieRepository.save(existingMovie);

       });
    }
}

@RestController
public class MovieRestController {

    @Autowired
    private MovieService movieService;

    @PutMapping("/movies/{movieId}")
    public Mono<ResponseEntity<Movie>> update(
            @PathVariable("movieId") final String movieId,
            @RequestBody final MovieRequest movieRequest) {

        return movieService.update(movieId, movieRequest)
                .map(m -> new ResponseEntity<>(m, HttpStatus.OK))
                .defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));

    }
}
2

2 Answers

12
votes
  1. The response of an HTTP Request is unique. All you can do is to send a Flux or Mono as the body of the response and use a Content-Type header to let the client know that it can be consumed it as a stream application/stream+json or the usual application/json.

  2. Both findById(id) and movieRepository.save(existingMovie) return Mono<Movie>. If you map it, each event Movie passed to the map will return a Mono<Movie> so the concatenation of findById().map(movieRepository.save()) ends up with a Mono<Mono<Movie>>. When you flatmap you're basically "merging" all the publishers from the map into a single Mono.

8
votes

map :

@PutMapping(path="/update/{id}", consumes=MediaType.APPLICATION_JSON_VALUE)
public Mono<Account> update(@PathVariable Long id, @RequestBody Account account) {
    Mono<Account> accFound = accountRepository.findById(id);
    return accFound.map(acc -> {
        acc.setAccountBalance(account.getAccountBalance());
        return accountRepository.save(acc).block();
        });

}

map adds a Mono to whatever is returned inside, note here accountRepository.save(acc) returns a Mono, and if I dont add block(), the method update ends up returning Mono<Mono<Account>> -- compile error in that case.

flatMap :

@PutMapping(path="/update/{id}", consumes=MediaType.APPLICATION_JSON_VALUE)
public Mono<Account> update(@PathVariable Long id, @RequestBody Account account) {
    Mono<Account> accFound = accountRepository.findById(id);
    return accFound.flatMap(acc -> {
        acc.setAccountBalance(account.getAccountBalance());
        return accountRepository.save(acc);
        });

}

flatMap simply returns whatever is returned inside.

Hope it helps to understand it a very basic way