使用 Spring WebClient,我如何反序列化不同类型的成功和失败响应?

With Spring WebClient, how do I deserialize different types for successful and failed responses?

tbjorch 的回答让我走上了正确的道路,但我发现的一件事是我应该使用静态 .create 方法构造 HTTP 异常 HttpClientErrorExceptionHttpServerErrorException。如果我不这样做,我的所有异常都将作为 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");
  }