【问题标题】:Combining several REST Call with Spring Webflux将多个 REST 调用与 Spring Webflux 相结合
【发布时间】:2018-06-12 08:42:35
【问题描述】:

我试图用 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));
}

完美运行我的问题是如何将其与呼叫登录服务结合起来..

我想过下面的 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,但是当它返回 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;
}

但我不知道如何在不使用 Mono.block() 的情况下去那里..

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

有什么建议吗?

【问题讨论】:

    标签: project-reactor spring-webflux


    【解决方案1】:

    你可以全局应用的东西:

    • 让你的方法返回像Mono&lt;T&gt;Flux&lt;T&gt;这样的反应类型
    • 避免在您的网络应用程序中使用subscribe,因为它将执行与当前请求/响应分离,并且不保证该任务的完成

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

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

    【讨论】:

    • 首先,谢谢你的回答,它解决了我的问题,我对 Webflux 有了更好的理解,但有些措辞让我感到困惑。
    • 我期待我们用 Mono.flatMap() 实现的功能实际上应该用 Mono.then() 之类的东西来完成,而这就是我们正在做的不是吗?我们正在等待一个 Mono 的结果,然后调用另一个 Mono。那么为什么是 flatMap 的措辞(我看到这里的讨论 'github.com/reactor/reactor-core/issues/516' 但我不明白切换的原因)
    • Mono.then 是关于链接它们而不重用前一个操作的结果(只是确保一个接一个发生)。 Flux.flatMapMono.flatMap 是关于链接和重用中间结果的。进行此更改是为了更好地对齐 FluxMono API。
    • @BrianClozel doLogin() 和 callRemoteService(info) 的错误处理怎么样?特别是在 webClient 上使用 exchange() 方法?我们是否必须始终明确检查 4XX 和 5XX?变得如此丑陋。有更好的方法吗?
    • 您会收到异常作为错误消息,作为反应流合同的一部分。 4xx 和 5xx 被 webclient 变成异常。如果您想要更具体的答案,请提出一个新问题。
    猜你喜欢
    • 1970-01-01
    • 2011-12-20
    • 2018-02-02
    • 2020-06-11
    • 1970-01-01
    • 2021-08-21
    • 1970-01-01
    • 2012-12-31
    • 2021-08-17
    相关资源
    最近更新 更多