将多个REST调用与Spring Webflux结合

pos*_*ver 5 project-reactor spring-webflux

我尝试使用Spring Webflux做一些事情,但是我确实对某些反应性概念感到困惑。

我有一些受表单身份验证保护的REST服务,在我可以调用我的业务REST服务之前,我必须调用通过Spring Security提供的“登录” URL,传递我的凭据并将返回的Cookie放置到对其他REST服务的进一步调用中。

以下是REST业务服务的呼叫摘要。

@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));
}
Run Code Online (Sandbox Code Playgroud)

完美地工作,我的问题是如何将其结合到呼叫登录服务中。

我考虑过以下login()方法...

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);
    });
}
Run Code Online (Sandbox Code Playgroud)

使用'login()'方法,我可以获取'JSESSION_ID'Cookie,但是当它返回Mono时,我无法访问Cookie值(我可以使用clientResponse.block()这样做,但是我知道这很邪恶,所以请不要这样做这样做),所以我尝试通过回调订阅clientResponse,但如果尝试这样做,则将与'businessService()'方法分离,并且将无法返回我喜欢的值...

因此,我要实现的伪代码如下。

@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;
}
Run Code Online (Sandbox Code Playgroud)

但是我不知道不使用Mono.block()怎么去那里。

从'handleLogin()'返回'Route'对象没有任何意义,因为如果我正确理解了Reactive概念,那么'fetch()'方法将永远不会等待它。

有小费吗?

Bri*_*zel 5

您可以全局应用的内容:

  • 使您的方法返回反应性类型,例如Mono<T>Flux<T>
  • 避免subscribe在Web应用程序中使用,因为它会将执行与当前请求/响应分离开来,并且不能保证该任务的完成

然后,您可以使用可用的运算符来组合那些反应类型。就像是:

Mono<LoginInformation> login = doLogin();
Mono<String> value = login.flatMap(info -> callRemoteService(info));
Run Code Online (Sandbox Code Playgroud)