提取 Mono 非阻塞响应并将其存储在变量中并全局使用
Extract Mono nonblocking response and store it in a variable and use it globally
在我的项目中,我有一个要求,我需要调用第三方 api 真实 url 来获取访问令牌。我需要在每个后续请求中设置该访问令牌 header。访问令牌有一定的生命周期,当生命周期到期时,我需要重新生成访问令牌。
application.yml
我已经对 client_id、client_secret、auth_url 和 grant_type 进行了硬编码。
AuthController.java
我在这里创建了一个端点来生成访问令牌。
**`AuthService.java`**
@Services
@Slf4j
public class AuthService{
@Autowired
private WebClient webClient;
static String accessToken="";
public Mono<SeekResponse> getAccessToken(AuthRequest authRequest) throws InvalidTokenException{
Mono<AuthResponse> authResponse=webClient.post()
.bodyValue(authRequest)
.accept(MediaType.APPLICATION_JSON)
.retrive()
.bodyToMono(AuthResponse.class);
authResponse.doOnNext(response->{
String value=response.getAccess_token();
accessToken=accessToken+value;
})
}
}
虽然我已经更新了“accessToken”值,但它 return 将我设为空。我明白,因为我已经进行了异步调用,所以这个值为 null。我不能在这里使用阻塞机制。
有没有其他方法可以生成访问令牌并将其作为 header 传递给后续的身份验证请求。或者我如何在全局范围内使用 accessToken 值,以便我可以将这些令牌值设置为我随后的 api 请求调用。
我已按照以下文章尝试使用 oAuth2:
https://medium.com/@asce4s/oauth2-with-spring-webclient-761d16f89cdd
但是当我执行时出现以下错误:
“找不到预期的 CSRF 令牌”。
提供的示例和实现不是真正的反应式,很难理解。方法 returns Mono
但同时 throws InvalidTokenException
或 onNext
的用法是 so-called side-effect 应该用于日志记录的操作、指标或其他类似用例。
您为 WebClient 实现 oauth 流程的方式是创建 filter
、Client Filters.
Spring Security 为常见的 oauth 流程提供了一些样板。查看 Spring Security OAuth2 了解更多详情。
这里是客户端凭证提供程序的简单实现示例
private ServerOAuth2AuthorizedClientExchangeFilterFunction oauth(String clientRegistrationId, ClientConfig config) {
var clientRegistration = ClientRegistration
.withRegistrationId(clientRegistrationId)
.tokenUri(config.getAuthUrl() + "/token")
.clientId(config.getClientId())
.clientSecret(config.getClientSecret())
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.build();
var authRepository = new InMemoryReactiveClientRegistrationRepository(clientRegistration);
var authClientService = new InMemoryReactiveOAuth2AuthorizedClientService(authRepository);
var authClientManager = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
authRepository, authClientService);
var oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authClientManager);
oauth.setDefaultClientRegistrationId(clientRegistrationId);
return oauth;
}
那么你可以在WebClient
中使用它
WebClient.builder()
.filter(oauth)
.build()
更新
这是没有过滤器的替代方法的示例
AuthService
@Service
public class AuthService {
private final WebClient webClient;
public AuthService() {
this.webClient = WebClient.create("<url>/token");
}
public Mono<String> getAccessToken() {
return webClient.post()
.bodyValue()
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(AuthResponse.class)
.map(res -> res.getAccessToken());
}
}
ApiService
@Service
public class ApiService {
private final WebClient webClient;
private final Mono<String> requestToken;
public ApiService(AuthService authService) {
this.webClient = WebClient.create("<url>/api");
// cache for token expiration
this.requestToken = authService.getAccessToken().cache(Duration.ofMinutes(10));
}
public Mono<String> request() {
return requestToken
.flatMap(token ->
webClient.get()
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(String.class)
);
}
}
我也在学习 Webflux。这是我的想法。如有不妥请指正
我们不会依赖 doOnNext()
或 doOnSuccess()
或其他类似的方法来尝试处理 pre-defined 变量 accessToken
(这不是让 Mono 流动)。我们应该关注的是将一个单声道转换为另一个单声道,例如将单声道响应转换为单声道访问令牌。
方法是 .flatmap()
/.map()
/.zipwith()
/...
例如,
Mono<string> tokenMono = responseMono.flatmap(
// in the map or flatmap, we get the chance to operate on variables/objects.
resp -> {
string token = response.getAccess_token();
return Mono.just(token); // with Mono.just(), we are able to convert object to Mono again.
}
) // this example is not practical, as map() is better to do the same thing. flatmap with Mono.just() is meaningless here.
Mono<string> tokenMono2 = responseMono.map(
resp -> {
string token = response.getAccess_token();
return token;
}
)
从 Mono 开始的所有内容都应该始终是 Mono,直到被订阅或阻止。它们为我们提供了对 Mono<variables>
中的那些 variables
进行操作的方法。这些是 map()
flatmap()
、zipwith()
等
参考作者说的一点,doOnNext()
是为了日志等副作用。
在我的项目中,我有一个要求,我需要调用第三方 api 真实 url 来获取访问令牌。我需要在每个后续请求中设置该访问令牌 header。访问令牌有一定的生命周期,当生命周期到期时,我需要重新生成访问令牌。
application.yml
我已经对 client_id、client_secret、auth_url 和 grant_type 进行了硬编码。
AuthController.java
我在这里创建了一个端点来生成访问令牌。
**`AuthService.java`**
@Services
@Slf4j
public class AuthService{
@Autowired
private WebClient webClient;
static String accessToken="";
public Mono<SeekResponse> getAccessToken(AuthRequest authRequest) throws InvalidTokenException{
Mono<AuthResponse> authResponse=webClient.post()
.bodyValue(authRequest)
.accept(MediaType.APPLICATION_JSON)
.retrive()
.bodyToMono(AuthResponse.class);
authResponse.doOnNext(response->{
String value=response.getAccess_token();
accessToken=accessToken+value;
})
}
}
虽然我已经更新了“accessToken”值,但它 return 将我设为空。我明白,因为我已经进行了异步调用,所以这个值为 null。我不能在这里使用阻塞机制。 有没有其他方法可以生成访问令牌并将其作为 header 传递给后续的身份验证请求。或者我如何在全局范围内使用 accessToken 值,以便我可以将这些令牌值设置为我随后的 api 请求调用。
我已按照以下文章尝试使用 oAuth2: https://medium.com/@asce4s/oauth2-with-spring-webclient-761d16f89cdd 但是当我执行时出现以下错误: “找不到预期的 CSRF 令牌”。
提供的示例和实现不是真正的反应式,很难理解。方法 returns Mono
但同时 throws InvalidTokenException
或 onNext
的用法是 so-called side-effect 应该用于日志记录的操作、指标或其他类似用例。
您为 WebClient 实现 oauth 流程的方式是创建 filter
、Client Filters.
Spring Security 为常见的 oauth 流程提供了一些样板。查看 Spring Security OAuth2 了解更多详情。
这里是客户端凭证提供程序的简单实现示例
private ServerOAuth2AuthorizedClientExchangeFilterFunction oauth(String clientRegistrationId, ClientConfig config) {
var clientRegistration = ClientRegistration
.withRegistrationId(clientRegistrationId)
.tokenUri(config.getAuthUrl() + "/token")
.clientId(config.getClientId())
.clientSecret(config.getClientSecret())
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.build();
var authRepository = new InMemoryReactiveClientRegistrationRepository(clientRegistration);
var authClientService = new InMemoryReactiveOAuth2AuthorizedClientService(authRepository);
var authClientManager = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
authRepository, authClientService);
var oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authClientManager);
oauth.setDefaultClientRegistrationId(clientRegistrationId);
return oauth;
}
那么你可以在WebClient
WebClient.builder()
.filter(oauth)
.build()
更新 这是没有过滤器的替代方法的示例
AuthService
@Service
public class AuthService {
private final WebClient webClient;
public AuthService() {
this.webClient = WebClient.create("<url>/token");
}
public Mono<String> getAccessToken() {
return webClient.post()
.bodyValue()
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(AuthResponse.class)
.map(res -> res.getAccessToken());
}
}
ApiService
@Service
public class ApiService {
private final WebClient webClient;
private final Mono<String> requestToken;
public ApiService(AuthService authService) {
this.webClient = WebClient.create("<url>/api");
// cache for token expiration
this.requestToken = authService.getAccessToken().cache(Duration.ofMinutes(10));
}
public Mono<String> request() {
return requestToken
.flatMap(token ->
webClient.get()
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(String.class)
);
}
}
我也在学习 Webflux。这是我的想法。如有不妥请指正
我们不会依赖 doOnNext()
或 doOnSuccess()
或其他类似的方法来尝试处理 pre-defined 变量 accessToken
(这不是让 Mono 流动)。我们应该关注的是将一个单声道转换为另一个单声道,例如将单声道响应转换为单声道访问令牌。
方法是 .flatmap()
/.map()
/.zipwith()
/...
例如,
Mono<string> tokenMono = responseMono.flatmap(
// in the map or flatmap, we get the chance to operate on variables/objects.
resp -> {
string token = response.getAccess_token();
return Mono.just(token); // with Mono.just(), we are able to convert object to Mono again.
}
) // this example is not practical, as map() is better to do the same thing. flatmap with Mono.just() is meaningless here.
Mono<string> tokenMono2 = responseMono.map(
resp -> {
string token = response.getAccess_token();
return token;
}
)
从 Mono 开始的所有内容都应该始终是 Mono,直到被订阅或阻止。它们为我们提供了对 Mono<variables>
中的那些 variables
进行操作的方法。这些是 map()
flatmap()
、zipwith()
等
参考作者说的一点,doOnNext()
是为了日志等副作用。