英文:
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("/auth")
.header(ACCEPT,JSON)
.retrieve()
.bodyToMono(AuthToken.class)
.flatMapMany(authToken -> webclient.post().uri("/api")
.header(ACCEPT,XML)
.header("AUTH",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) -> 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);
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) -> 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();
}
}
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<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()));
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论