Home > Net >  Unable to return a request within webflux in reactive manner
Unable to return a request within webflux in reactive manner

Time:11-13

Here is the following flow in my code: User calls create agency endpoint, containing the agency name and a boolean field ->

Router functions receives call, passes to handler ->

Handler converts request body to Mono, passes that mono so a service ->

Service saves agency to DB, generating an ID in the process, then creates a Response object containing the ID wrapped in a Mono, returns Response back to the Handler.

This is the part where I am stuck. Maintaining a reactive approach, I must save the Agency to Db and create the ID within a "doOnNext() part of the Mono. However, I can't return the ID that I get from the DB in order to create the response object.

What is the reactive way to accomplish this? Thanks in advance!

public RouterFunction<ServerResponse> createAgency() {
        return RouterFunctions
                .route(POST("/agency").and(accept(MediaType.APPLICATION_JSON)), handler::createAgency);
    }
@Component
@AllArgsConstructor
public class AgencyHandler {

    private final AgencyServiceImpl service;

    @NonNull
    public Mono<ServerResponse> createAgency(ServerRequest request){
        Mono<CreateAgencyRequest> agency = request.bodyToMono(CreateAgencyRequest.class);

        Mono<CreateAgencyResponse> response = service.createAgency(agency);

        return ServerResponse.created(URI.create("/agency"))
                .contentType(MediaType.APPLICATION_JSON)
                .body(response, CreateAgencyResponse.class);
    }
}
@Service
@AllArgsConstructor
public class AgencyServiceImpl implements AgencyService {
    private final AgencyRepository agencyRepository;
    private final UuidGenerator uuidGenerator;

    public Mono<CreateAgencyResponse> createAgency(Mono<CreateAgencyRequest> request) {
        UUID id;
        request.doOnNext(agency -> {
            UUID agencyId = uuidGenerator.getUUID();
            Mono<Agency> newAgency = Mono.just(
                new Agency(
                    agencyId,
                    agency.getFields().getName()
            ));

            //repository.save(newAgency)

            //return Mono.just(new CreateAgencyResponse(new CreateAgencyResponseData(agencyId.toString())));
        });

        // return request
        return Mono.just(new CreateAgencyResponse(new CreateAgencyResponseData(agencyId.toString())));
    }
}

CodePudding user response:

Something like the following should do the trick:

@Service
@AllArgsConstructor
public class AgencyServiceImpl implements AgencyService {
    private final AgencyRepository agencyRepository;
    private final UuidGenerator uuidGenerator;

    public Mono<CreateAgencyResponse> createAgency(Mono<CreateAgencyRequest> request) {
        UUID agencyId = uuidGenerator.getUUID();

        return request.flatMap(createAgencyRequest -> {
            Agency agency = new Agency(agencyId, agency.getFields().getName();
            return repository.save(newAgency);    
        }).map(saved ->
            new CreateAgencyResponse(new CreateAgencyResponseData(agencyId.toString()));
        )
    }
}

You would create the Agency in the flatMap operation and store it in the database. I assume your repository is also reactive so it should return Mono as well, hence the flatMap operation. Afterwards you just need to map whatever the repository returned (you may want to have some logic here based on the successful operation on the database) to create the CreateAgencyResponse.

CodePudding user response:

ThedoOnNext is not a good option to perform I/O bound operations such as database access. You should use flatmap instead. Also, have a look at map operator for synchronous, 1-to-1 transformations.

The final code should looks like this:

  public Mono<CreateAgencyResponse> createAgency(Mono<CreateAgencyRequest> request) {
    return request.map(req -> new Agency(uuidGenerator.getUUID(), req.getFields().getName()))
        .flatMap(agency -> repository.save(agency))
        .map(agency -> new CreateAgencyResponse(new CreateAgencyResponseData(agency.getAgencyId())));
  }

CodePudding user response:

doOnNext is for side effects, what you are looking for is flatMap

return request.flatMap(agency -> {
    final UUID agencyId = uuidGenerator.getUUID();
    return repository.save(new Agency(agencyId, agency.getFields().getName()))
        .thenReturn(agencyId);
}).flatMap(id -> ServerResponse.ok()
    .bodyValue(new CreateAgencyResponseData(agencyId.toString()))
    .build());

Wrote this without a compiler to check the code but you should get the gist of it.

  • Related