将多个 REST 调用与 Spring Webflux 组合

Combining several REST Call with Spring Webflux

我想用 Spring Webflux 做点什么,但我真的对一些反应式概念感到困惑。

我有一些受表单身份验证保护的 REST 服务,在调用我的业务 REST 服务之前,我必须调用通过 Spring 安全性提供的 'login' URL,通过我的凭据并将 returned 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));
}

效果很好我的问题是如何将其与呼叫登录服务相结合..

我考虑过使用 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);
    });
}

使用 'login()' 方法我可以获得 'JSESSION_ID' Cookie,但是当它 return 是 Mono 时我无法访问 Cookie 值(我可以使用 clientResponse.block() 但我知道这是邪恶的,所以尽量不要这样做),所以我尝试使用回调订阅 clientResponse,但如果我尝试这样做,我将与 'businessService()' 方法分离,并且不会能够 return 我喜欢的价值观...

所以我想要实现的伪代码如下....

@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;
}

但我不知道如何不使用 Mono.block()..

return 'Route' 对象从 'handleLogin()' 没有任何意义,因为如果我正确理解 Reactive 概念,'fetch()' 方法无论如何都不会等待它。 ...

有什么建议吗?

您可以全局应用的内容:

  • 使你的方法 return 像 Mono<T>Flux<T>
  • 这样的反应类型
  • 避免在您的 Web 应用程序中使用 subscribe,因为它会将执行与当前 request/response 分离,并且不保证该任务的完成

然后您可以使用可用的运算符组合这些反应类型。类似于:

Mono<LoginInformation> login = doLogin();
Mono<String> value = login.flatMap(info -> callRemoteService(info));