如何使用Spring WebClient进行后续调用,同时更改头部设置?

huangapple go评论69阅读模式
英文:

How to use Spring WebClient to make a subsequent call with different header setting?

问题

我需要调用一个第三方API,在调用API之前需要进行身份验证调用以获取身份验证令牌。身份验证API返回的是JSON格式,但随后的API调用需要使用XML格式。

我已经分别实现了以下部分:

webclient.post().uri("/auth").header(ACCEPT, JSON).retrieve()
    .bodyToMono(AuthToken.class);
webclient.post().uri("/api").header(ACCEPT, XML).header("AUTH", authToken).retrive().bodyToFlux();

请问我应该如何实现能够访问第二个API的方法?
我尝试在方法内部使用token = firstCall.block()进行变量赋值,但是出现了“block()不受支持”的错误。

英文:

I need to call an third party API which requires an authentication call beforehand to get an authentication token. The Authentication API is in json but the subsequent call is in XML.

I have separately :

webclient.post().uri("/auth").header(ACCEPT,JSON).retrieve()
      .bodyToMono(AuthToken.class);
webclient.post().uri("/api").header(ACCEPT,XML).header("AUTH",authToken).retrive().bodyToFlux();

How should I implement the method to be able to access the second API?
I tried to assign a variable inside the method with token = firstCall.block() but I've got block() is not supported error.

答案1

得分: 3

你只需像这样转换原始的流量:

webclient.post().uri("/auth")
    .header(ACCEPT,JSON)
    .retrieve()
    .bodyToMono(AuthToken.class)
    .flatMapMany(authToken -> webclient.post().uri("/api")
    .header(ACCEPT,XML)
    .header("AUTH",authToken).retrive().bodyToFlux();

更好的解决方案是使用 ExchangeFilterFunction 来为您获取令牌,具体请参阅文档:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/web-reactive.html#webflux-client-filter

类似这样的方式(未经测试,可能存在错误):

WebClient authWebClient = WebClient.builder().build();
WebClient webClient = WebClient.builder()
        .filter(((request, next) -> authWebClient.post()
                .uri("/auth")
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(AuthToken.class)
                .flatMap(authToken -> next.exchange(ClientRequest.from(request)
                        .headers(headers -> headers.add("AUTH", authToken))
                        .build()))
        ))
        .build();

webClient.post().uri("/api")
        .accept(MediaType.APPLICATION_XML)
        .retrieve()
        .bodyToFlux(MyData.class);

这只是基本示例,您可以添加缓存以避免在令牌过期时重新请求或获取...

请注意,内置的 ExchangeFilterFunction 存在用于基本的 OAuth2...

将所有内容封装到 Spring 配置中:

@Configuration
public class WebClientConfiguration {
    @Bean
    public WebClient authWebClient(final WebClient.Builder webClientBuilder) {
        return webClientBuilder.build();
    }

    @Bean
    public ExchangeFilterFunction authFilter(final WebClient authWebClient) {
        return (request, next) -> authWebClient.post()
                .uri("/auth")
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(AuthToken.class)
                .flatMap(authToken -> next.exchange(ClientRequest.from(request)
                        .headers(headers -> headers.add("AUTH", authToken.toString()))
                        .build()));
    }

    @Bean
    public WebClient webClient(final WebClient.Builder webClientBuilder, final ExchangeFilterFunction authFilter) {
        return webClientBuilder
                .filter(authFilter)
                .build();
    }
}

或者,如果您想避免使用 Lambda:

@Configuration
public class WebClientConfiguration {
    @Bean
    public WebClient authWebClient(final WebClient.Builder webClientBuilder) {
        return webClientBuilder.build();
    }

    @Bean
    public WebClient webClient(final WebClient.Builder webClientBuilder, final AuthFilter authFilter) {
        return webClientBuilder
                .filter(authFilter)
                .build();
    }
    
    @Bean
    public AuthFilter authFilter(WebClient authWebClient) {
        return new AuthFilter(authWebClient);
    }
}

public class AuthFilter implements ExchangeFilterFunction {

    private final WebClient authWebClient;

    public AuthFilter(WebClient authWebClient) {
        this.authWebClient = authWebClient;
    }

    @Override
    public Mono<ClientResponse> filter(final ClientRequest request, final ExchangeFunction next) {
        return authWebClient.post()
                .uri("/auth")
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(AuthToken.class)
                .flatMap(authToken -> next.exchange(ClientRequest.from(request)
                        .headers(headers -> headers.add("AUTH", authToken.toString()))
                        .build()));
    }
}
英文:

You just have to transform the original flux like:

webclient.post().uri(&quot;/auth&quot;)
    .header(ACCEPT,JSON)
    .retrieve()
    .bodyToMono(AuthToken.class)
    .flatMapMany(authToken -&gt; webclient.post().uri(&quot;/api&quot;)
    .header(ACCEPT,XML)
    .header(&quot;AUTH&quot;,authToken).retrive().bodyToFlux();

A better solution would be to use a ExchangeFilterFunction that will fetch the token for you https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/web-reactive.html#webflux-client-filter

Something like that (not tested might have bug):

WebClient authWebClient = WebClient.builder().build();
WebClient webClient = WebClient.builder()
        .filter(((request, next) -&gt; authWebClient.post()
                .uri(&quot;/auth&quot;)
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(AuthToken.class)
                .flatMap(authToken -&gt; next.exchange(ClientRequest.from(request)
                        .headers(headers -&gt; headers.add(&quot;AUTH&quot;, authToken))
                        .build()))
        ))
        .build();

webClient.post().uri(&quot;/api&quot;)
        .accept(MediaType.APPLICATION_XML)
        .retrieve()
        .bodyToFlux(MyData.class);

This is basic but you could add cache to avoid requesting or fetch again if token is expired...
Be aware that builtin ExchangeFilterFunction exists for basic oauth2...

Wrap everything with a spring configuration:

@Configuration
public class WebClientConfiguration {
    @Bean
    public WebClient authWebClient(final WebClient.Builder webClientBuilder) {
        return webClientBuilder.build();
    }

    @Bean
    public ExchangeFilterFunction authFilter(final WebClient authWebClient) {
        return (request, next) -&gt; authWebClient.post()
                .uri(&quot;/auth&quot;)
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(AuthToken.class)
                .flatMap(authToken -&gt; next.exchange(ClientRequest.from(request)
                        .headers(headers -&gt; headers.add(&quot;AUTH&quot;, authToken.toString()))
                        .build()));
    }

    @Bean
    public WebClient webClient(final WebClient.Builder webClientBuilder, final ExchangeFilterFunction authFilter) {
        return webClientBuilder
                .filter(authFilter)
                .build();
    }
}

Or if you want to avoid lambda:

@Configuration
public class WebClientConfiguration {
@Bean
    public WebClient authWebClient(final WebClient.Builder webClientBuilder) {
        return webClientBuilder.build();
    }

    @Bean
    public WebClient webClient(final WebClient.Builder webClientBuilder, final AuthFilter authFilter) {
        return webClientBuilder
                .filter(authFilter)
                .build();
    }
    
    @Bean
    public AuthFilter authFilter(WebClient authWebClient) {
        return new AuthFilter(authWebClient);
    }
}

public class AuthFilter implements ExchangeFilterFunction {

    private final WebClient authWebClient;

    public AuthFilter(WebClient authWebClient) {
        this.authWebClient = authWebClient;
    }

    @Override
    public Mono&lt;ClientResponse&gt; filter(final ClientRequest request, final ExchangeFunction next) {
        return authWebClient.post()
                .uri(&quot;/auth&quot;)
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(AuthToken.class)
                .flatMap(authToken -&gt; next.exchange(ClientRequest.from(request)
                        .headers(headers -&gt; headers.add(&quot;AUTH&quot;, authToken.toString()))
                        .build()));
    }

}

huangapple
  • 本文由 发表于 2020年10月21日 01:27:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/64450399.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定