Reactive Spring Query Parameters

Anthony Ikeda picture Anthony Ikeda · Mar 1, 2018 · Viewed 10.9k times · Source

My Spring Reactive Web API should be able to filter based on query parameters but I'm struggling to get this right.

GET /v1/address?type=business

Should return all addresses of type 'business' in the system (just an example since I'm not posting the actual requirements). If the parameter is absent then just return all addresses.

[
  { "id" : 1, "type" : "business", "street" : "..."},
  { "id" : 2, "type" : "business", "street" : "..."},
  ...
}

I'm using Router functions to map the request:

public RouterFunction<ServerResponse> route(AddressHandler handler) {
    return route(GET("/v1/address"), handler::getAddresses);
}

But I'm struggling to determine how to check for the parameter and, if it isn't present, to return all addresses.

class AddressHandler {
    Mono<ServerResponse> getAddressesFilteredOrNot(ServerRequest request) {
        Optional<String> addressType = request.getQueryParam("type");

        ...
    }
}

Now the Optional.ifPresent() isn't going to work, I can't return from the method:

class AddressHandler {
    Mono<ServerResponse> getAddressesFilteredOrNot(ServerRequest request) {
        Optional<String> addressType = request.getQueryParam("type");

        // invalid
        addressType.ifPresent(addrType -> {
            return ServerResponse.ok()
                .body(this.service.getAddressByType(addrType),
                   Address.class);
    });
}

Also I can't switchIfEmpty() on the parameter since it throws a NullPointerException:

class AddressHandler {
    Mono<ServerResponse> getAddressesFilteredOrNot(ServerRequest request) {
        Optional<String> addressType = request.getQueryParam("type");

        // also invalid
        return Mono.justOrEmpty(addressType.orElse(""))
               .flatMap(addrType -> ServerResponse.ok().body(this.service.getAddressByType(addrType), Address.class)
               .switchIfEmpty(ServerResponse.ok().body(this.service.getAllAddresses(), Address.class));
    });
}

What's the flow to use query parameters if they are present and fall back on the getAllAddresses() method? Is it actually possible?

Answer

g00glen00b picture g00glen00b · Mar 1, 2018

You can use the orElse() or orElseGet() functionality of Optional to invoke an alternative method. For example:

request
    .getQueryParam("type")
    .map(type -> service.getAddressByType(type))
    .orElseGet(() -> service.getAllAddresses());

In this case, orElseGet() makes more sense, because it's lazily invoked. Be aware though that both methods should have the same return type, so probably Flux<Address>.