使用 Spring WebClient,我如何反序列化不同类型的成功和失败响应?
With Spring WebClient, how do I deserialize different types for successful and failed responses?
tbjorch 的回答让我走上了正确的道路,但我发现的一件事是我应该使用静态 .create
方法构造 HTTP 异常 HttpClientErrorException
和 HttpServerErrorException
。如果我不这样做,我的所有异常都将作为 Exception
超类抛出,这是不可取的,因为它丢失了原始 Http 状态代码的上下文。
我是 Spring WebClient 的新手。我正在实施的 API 根据成功或失败使用不同的 JSON 结构进行响应。在失败的情况下,我希望能够反序列化响应主体和 return 错误消息。根据下面的代码,我收到一条错误消息:
java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-2
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83)
我同意,在 2 个地方屏蔽是没有意义的。但我不明白如何反序列化错误响应以获取错误消息。我在网络和 Whosebug 上阅读的所有内容都表明人们只是 return Mono.error
或抛出异常,但在这些情况下他们似乎并没有反序列化响应主体。这是我的代码:
public boolean updatePassword(String id, String data) {
final var responseSpec =
client
.post()
.uri(builder -> builder.path("/{id}/change_password").build(id))
.header(HttpHeaders.AUTHORIZATION, "Bearer " + staticData.getAPIToken())
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(data))
.retrieve()
.onStatus(
HttpStatus::isError,
error -> {
final var errorResponse = error.bodyToMono(ErrorResponse.class).block();
final var errorMsg = errorResponse.getCause().getSummary();
if (error.statusCode().is4xxClientError()) {
throw new HttpClientErrorException(error.statusCode(), errorMsg);
}
if (error.statusCode().is5xxServerError()) {
throw new HttpServerErrorException(error.statusCode(), errorMsg);
}
return Mono.error(
new HttpServerErrorException(
HttpStatus.INTERNAL_SERVER_ERROR, "Something else."));
})
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {});
final var response = responseSpec.block();
return response.containsKey("password") && response.containsKey("provider");
}
PS. 我也尝试过使用 .exchangeToMono
而不是 .retrieve
,这样我就可以检查响应状态代码,然后为 .bodyToMono
函数使用不同的类型,但是一旦我在成功和失败之间写了两种不同的类型,我就会收到一条错误消息,指出无法推断类型参数。
无法完全尝试您的代码,但可以尝试这样的操作:
public boolean updatePassword(String id, String data) {
final var responseSpec =
client
.post()
.uri(builder -> builder.path("/{id}/change_password").build(id))
.header(HttpHeaders.AUTHORIZATION, "Bearer " /*+ staticData.getAPIToken()*/)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(data))
.retrieve()
.onStatus(
HttpStatus::isError,
error -> error.bodyToMono(ErrorResponse.class)
.map(errorResponse -> {
final var errorMsg = errorResponse.getCause().getSummary();
if (error.statusCode().is4xxClientError()) {
throw new HttpClientErrorException(error.statusCode(), errorMsg);
}
if (error.statusCode().is5xxServerError()) {
throw new HttpServerErrorException(error.statusCode(), errorMsg);
}
return new HttpServerErrorException(
HttpStatus.INTERNAL_SERVER_ERROR, "Something else.");
}))
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {});
final var response = responseSpec.block();
return response.containsKey("password") && response.containsKey("provider");
}
tbjorch 的回答让我走上了正确的道路,但我发现的一件事是我应该使用静态 .create
方法构造 HTTP 异常 HttpClientErrorException
和 HttpServerErrorException
。如果我不这样做,我的所有异常都将作为 Exception
超类抛出,这是不可取的,因为它丢失了原始 Http 状态代码的上下文。
我是 Spring WebClient 的新手。我正在实施的 API 根据成功或失败使用不同的 JSON 结构进行响应。在失败的情况下,我希望能够反序列化响应主体和 return 错误消息。根据下面的代码,我收到一条错误消息:
java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-2
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:83)
我同意,在 2 个地方屏蔽是没有意义的。但我不明白如何反序列化错误响应以获取错误消息。我在网络和 Whosebug 上阅读的所有内容都表明人们只是 return Mono.error
或抛出异常,但在这些情况下他们似乎并没有反序列化响应主体。这是我的代码:
public boolean updatePassword(String id, String data) {
final var responseSpec =
client
.post()
.uri(builder -> builder.path("/{id}/change_password").build(id))
.header(HttpHeaders.AUTHORIZATION, "Bearer " + staticData.getAPIToken())
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(data))
.retrieve()
.onStatus(
HttpStatus::isError,
error -> {
final var errorResponse = error.bodyToMono(ErrorResponse.class).block();
final var errorMsg = errorResponse.getCause().getSummary();
if (error.statusCode().is4xxClientError()) {
throw new HttpClientErrorException(error.statusCode(), errorMsg);
}
if (error.statusCode().is5xxServerError()) {
throw new HttpServerErrorException(error.statusCode(), errorMsg);
}
return Mono.error(
new HttpServerErrorException(
HttpStatus.INTERNAL_SERVER_ERROR, "Something else."));
})
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {});
final var response = responseSpec.block();
return response.containsKey("password") && response.containsKey("provider");
}
PS. 我也尝试过使用 .exchangeToMono
而不是 .retrieve
,这样我就可以检查响应状态代码,然后为 .bodyToMono
函数使用不同的类型,但是一旦我在成功和失败之间写了两种不同的类型,我就会收到一条错误消息,指出无法推断类型参数。
无法完全尝试您的代码,但可以尝试这样的操作:
public boolean updatePassword(String id, String data) {
final var responseSpec =
client
.post()
.uri(builder -> builder.path("/{id}/change_password").build(id))
.header(HttpHeaders.AUTHORIZATION, "Bearer " /*+ staticData.getAPIToken()*/)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(data))
.retrieve()
.onStatus(
HttpStatus::isError,
error -> error.bodyToMono(ErrorResponse.class)
.map(errorResponse -> {
final var errorMsg = errorResponse.getCause().getSummary();
if (error.statusCode().is4xxClientError()) {
throw new HttpClientErrorException(error.statusCode(), errorMsg);
}
if (error.statusCode().is5xxServerError()) {
throw new HttpServerErrorException(error.statusCode(), errorMsg);
}
return new HttpServerErrorException(
HttpStatus.INTERNAL_SERVER_ERROR, "Something else.");
}))
.bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {});
final var response = responseSpec.block();
return response.containsKey("password") && response.containsKey("provider");
}