Spring Web客户端没有记录错误响应并在收到错误时执行使用者操作



我有一个spring-boot服务,其中API通过RouterFunction公开。一旦接收到API请求,就会触发某些验证。其中一个验证,通过Web客户端调用另一个API来验证接收到的值是否存在。如果值不存在,则必须记录错误消息并将错误消息添加到数组列表中。

然而,下面的实现既不记录错误也不记录成功消息,也不能在数组列表中添加错误消息。

我也尝试过使用block((,但这会导致

'IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-epoll-3'

如果我通过RouterFunction直接公开一个API,只是为了调用这个查找API,那么一切都在工作。这表明,通过验证层进行调用会引起问题,可能是同步和响应式调用方式出现了一些问题。

我无法理解我的执行问题。请指导我做错了什么。

触发外部验证的验证类

@Slf4j
@Component
@RequiredArgsConstructor
public class EmployeeValidation{

private final IdentityApiClient identityApiClient;

public List<ErrorDetail> validate(Optional<EmployeeRequestDto> employeeRequest) {
var errors = new ArrayList<ErrorDetail>();
employeeRequest
.ifPresent(employee -> {
//some other validations
if (errors.isEmpty()) {
validateIfIdentityExist(employee.getSecuredEmployeeDetail(), errors::add);
}
});
return errors;
}

private void validateIfIdentityExist(SecuredEmployeeDetailDto securedEmployeeDetailDto, Consumer<ErrorDetail> errorDetailConsumer) {
Optional.ofNullable(securedEmployeeDetailDto)
.map(SecuredEmployeeDetailDto::getIdentity)
.ifPresent(identityLocal -> {
log.info("Going to retrieve identity [{}] detail", identityLocal);
identityApiClient.retrieveIdentityDetail(identityLocal)
.doOnError(e -> errorDetailConsumer.accept(new ErrorDetail(REQUEST_INVALID_PARAM, e.getMessage())));
});
}
}

正在调用另一个API以验证值的Web客户端

@Slf4j
@Component
@RequiredArgsConstructor
public class IdentityApiClient {
private final WebClient identityWebClient;
private final IdentityProperties identityProperties;

public Mono<IdentityDetail> retrieveIdentityDetail(String identity) {
log.info("Going to retrieve identity [{}] detail", identity);
return identityWebClient
.get()
.uri(identityProperties.getLookupPath(), Map.of("identity", identity))
.retrieve()
.onStatus(httpStatus -> httpStatus.equals(UNAUTHORIZED),
clientResponse -> clientResponse.bodyToMono(String.class)
.flatMap(identityLookUpErrorResponse -> Mono.error(new IdentityLookUpException(UNAUTHORIZED.value(), IdentityLookUpErrorResponse.builder()
.error(identityLookUpErrorResponse)
.message("Unauthorized Access")
.status(UNAUTHORIZED.value())
.build()))))
.onStatus(HttpStatus::is4xxClientError,
clientResponse -> clientResponse.bodyToMono(IdentityLookUpErrorResponse.class)
.switchIfEmpty(Mono.just(IdentityLookUpErrorResponse.builder()
.error("Received Empty Response Body")
.message("Unknown Identity")
.status(NOT_FOUND.value())
.build()))
.flatMap(identityLookUpErrorResponse -> Mono.error(new IdentityLookUpException(identityLookUpErrorResponse.getStatus(), identityLookUpErrorResponse))))
.onStatus(HttpStatus::is5xxServerError,
clientResponse -> clientResponse.bodyToMono(IdentityLookUpErrorResponse.class)
.switchIfEmpty(Mono.just(IdentityLookUpErrorResponse.builder()
.error("Received Empty Response Body")
.message(INTERNAL_SERVER_ERROR.getReasonPhrase())
.status(INTERNAL_SERVER_ERROR.value())
.build()))
.flatMap(identityLookUpErrorResponse -> Mono.error(new IdentityLookUpException(identityLookUpErrorResponse.getStatus(), identityLookUpErrorResponse))))
.bodyToMono(IdentityDetail.class)
.doOnNext(response -> log.info("Identity [{}] response received", response)) // not getting logged when called via validator class
.doOnError(e -> log.error("Identity [{}] error response received", identity, e));// not getting logged when called via validator class
}   
}

方法identityApiClient.retrieveIdentityDetail返回一个未被任何人订阅的Mono。这个隔离的代码没有任何作用。

identityApiClient.retrieveIdentityDetail(identityLocal)
.doOnError(e -> errorDetailConsumer.accept(new ErrorDetail(REQUEST_INVALID_PARAM, e.getMessage())));

反应式编程的口头禅是

"在您订阅";

对于大多数用例,底层框架(在本例中为spring(将为您订阅Mono,只要您从RouterFunction返回它。

我不完全熟悉您的需求,但一个可能的解决方案是重构您的验证器,使其具有反应性,并将其添加到您的反应流中。

最新更新