Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combining several REST Call with Spring Webflux

I trying to do something with Spring Webflux but I am really confused with some of the reactive concepts.

I have some REST services protected with Form Authentication, before I could call my Business REST Service, I have to call 'login' URL that is provided via Spring Security, pass my credentials and placed the returned Cookie to the further calls to other REST Services.

Below is the Snippet of the call to the REST Business Service....

@RequestMapping(value = "/endpoint1", method = RequestMethod.POST)
public Mono<String> businessService(@RequestBody ApiManagementWrapper apiManagementWrapper, ServerWebExchange httpRequest) {
    log.info("Handling /enpoint1");

    Mono<String> t = fetch(apiManagementWrapper, httpRequest);

    return t;
}

private Mono<String> fetch(ApiManagementWrapper apiManagementWrapper, ServerWebExchange httpRequest) {
    return this.webClientProxy
            .post()
            .uri("http://localhost:8081/something")
            .headers(httpHeaders ->  httpRequest.getRequest().getHeaders())
            .cookies(httpCookies -> httpRequest.getRequest().getCookies())
            .body(BodyInserters.fromPublisher(Mono.just(apiManagementWrapper.getEnterpriseMessage()), Object.class))
            .exchange()
            .flatMap(response -> response.bodyToMono(String.class));
}

which works perfectly my problem is how to combine this to the call login services..

I thought about following login() method...

private void login(ApiManagementWrapper apiManagementWrapper) {
    LinkedMultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
    formData.add("username", "user1");
    formData.add("password", "user1");

    Mono<ClientResponse> response =  this.webClientRouteConfirmation
            .post()
            .uri("http://localhost:8383/login")
            .body(BodyInserters.fromFormData(formData))
            .exchange();

    response.subscribe(clientResponse -> {
        handleLogin(clientResponse.cookies().getFirst("JSESSION_ID"), apiManagementWrapper);
    });
}

private void handleLogin(ResponseCookie loginCookie, ApiManagementWrapper apiManagementWrapper){
    Mono<Route> route = loadRoute(apiManagementWrapper.getEnterptiseContext(), loginCookie);
    if(route == null) {
        return;
    }
}


private Mono<Route> loadRoute(EnterpriseContext enterpriseContext, ResponseCookie loginCookie) {
    Mono<Route> route = this.webClientRouteConfirmation
            .get()
            .uri("http://localhost:8383/routes/search/findByServiceIdAndClientId?serviceName=" + enterpriseContext.getTarget() + "&clientName" + enterpriseContext.getSource())
            .cookie("JSESSION_ID", loginCookie.getValue())
            .exchange()
            .flatMap(clientResponse -> clientResponse.bodyToMono(Route.class));

    route.subscribe(r -> {
       handleRoute(enterpriseContext, loginCookie);
    });
}

with the 'login()' method I can get the 'JSESSION_ID' Cookie but while it returns a Mono I can't access the Cookie value (well I can with clientResponse.block() but I know it is evil so try not to do it), so I tried to subscribe to clientResponse with a callback, but if I try to do that I will be decoupled from the 'businessService()' method and will not able to return the values I like to...

So what I want to achieve as pseudo-code is following....

@RequestMapping(value = "/endpoint1", method = RequestMethod.POST)
public Mono<String> businessService(@RequestBody ApiManagementWrapper apiManagementWrapper, ServerWebExchange httpRequest) {
    log.info("Handling /netty1");

    Route route = login(apiManagementWrapper);

    Mono<String> t = null;
    if(route != null) {
        t = fetch(apiManagementWrapper, httpRequest);
    }

    return t;
}

but I have no idea how to go there without using Mono.block()..

It does not make any sense to return 'Route' object from 'handleLogin()' because if I understand Reactive concepts correctly, 'fetch()' method will not wait for it anyway....

Any tips?

like image 661
posthumecaver Avatar asked Jan 02 '18 16:01

posthumecaver


1 Answers

Something that you can apply globally:

  • make your methods return reactive types like Mono<T> or Flux<T>
  • avoid using subscribe in your web application, since it decouples the execution from the current request/response and gives no guarantee about the completion of that task

You can then compose those reactive types using the available operators. Something like:

Mono<LoginInformation> login = doLogin();
Mono<String> value = login.flatMap(info -> callRemoteService(info));
like image 62
Brian Clozel Avatar answered Oct 13 '22 11:10

Brian Clozel