You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java 11中java.net.http.HttpClient多BodySubscriber响应解析异常处理问题

处理HTTP响应时根据状态码动态选择解析模型的Java 11解决方案

问题场景

使用JsonBodyHandler解析请求成功的响应体,同时定义ExceptionModel处理请求失败的场景,需要根据响应状态码将响应映射为预期模型或异常模型类。

现有代码实现

JsonBodyHandler类

private static class JsonBodyHandler<R> implements HttpResponse.BodyHandler<Supplier<R>> {

    private final Class<R> returnClz;

    public JsonBodyHandler(Class<R> returnClz) {
      this.returnClz = returnClz;
    }

    private static <R> HttpResponse.BodySubscriber<Supplier<R>> asJson(Class<R> returnClz) {
      HttpResponse.BodySubscriber<InputStream> upstream =
          HttpResponse.BodySubscribers.ofInputStream();

      return HttpResponse.BodySubscribers.mapping(
          upstream, inputStream -> toSupplierOfType(inputStream, returnClz));
    }

    private static <R> Supplier<R> toSupplierOfType(InputStream inputStream, Class<R> returnClz) {
      return () -> {
        try (InputStream stream = inputStream) {
          ObjectMapper objectMapper = new ObjectMapper();
          return objectMapper.readValue(stream, returnClz);
        } catch (IOException e) {
          throw new UncheckedIOException(e);
        }
      };
    }

    @Override
    public HttpResponse.BodySubscriber<Supplier<R>> apply(HttpResponse.ResponseInfo responseInfo) {
      return asJson(returnClz);
    }
}

请求发送方法

<B, R> R exchange(URI uri, String method, B body, Class<R> returnClz) {
    Builder httpRequestBuilder = HttpRequest.newBuilder().uri(uri);
    addHeaders(httpRequestBuilder);

    var httpRequest =
        "GET".equals(method)
            ? httpRequestBuilder.GET().build()
            : httpRequestBuilder.method(method, getBodyPublisher(body)).build();

    var httpClient = HttpClient.newHttpClient();
    JsonBodyHandler<R> bodyHandler = new JsonBodyHandler<>(returnClz);
    Supplier<R> responseSupplier = httpClient.send(httpRequest, bodyHandler).body();
    return responseSupplier.get();
}

尝试方案及编译错误

尝试根据状态码选择不同的BodySubscriber,但出现编译错误:

BodySubscriber<Supplier<R>> successBodySubscriber = JsonBodyHandler.asJson(returnClz);
BodySubscriber<Supplier<ExceptionModel>> failureBodySubscriber =
    JsonBodyHandler.asJson(ExceptionModel.class);
BodyHandler<Supplier> jsonBodyHandler =
    (rspInfo) -> rspInfo.statusCode() == 200 ? successBodySubscriber : failureBodySubscriber;

HttpResponse<Supplier> httpResponse = httpClient.send(httpRequest, jsonBodyHandler);
if (httpResponse.statusCode() != 200) {
  Supplier<ExceptionModel> responseSupplier = httpResponse.body();
  throw ClientServiceError.invalidResponse(responseSupplier.get());
}

Supplier<R> responseSupplier = httpResponse.body();
return responseSupplier.get();

编译错误信息:

Incompatible types. Found: 'java.net.http.HttpResponse.BodySubscriber<java.util.function.Supplier>', required: 'java.net.http.HttpResponse.BodySubscriber<java.util.function.Supplier>'
Incompatible types. Found: 'java.net.http.HttpResponse.BodySubscriber<java.util.function.Supplier<com.project.proxy.impl.ExceptionModel>>', required: 'java.net.http.HttpResponse.BodySubscriber<java.util.function.Supplier>'

注意:使用Java 11,无法升级版本。

可行解决方案

方案1:统一返回类型为Supplier<?>

通过泛型通配符统一不同类型的Subscriber,解决编译类型不兼容问题:

// 统一类型为Supplier<?>,兼容两种解析结果
BodySubscriber<Supplier<?>> successBodySubscriber = JsonBodyHandler.asJson(returnClz);
BodySubscriber<Supplier<?>> failureBodySubscriber = JsonBodyHandler.asJson(ExceptionModel.class);

BodyHandler<Supplier<?>> jsonBodyHandler = rspInfo -> 
    rspInfo.statusCode() == 200 ? successBodySubscriber : failureBodySubscriber;

HttpResponse<Supplier<?>> httpResponse = httpClient.send(httpRequest, jsonBodyHandler);
if (httpResponse.statusCode() != 200) {
  // 基于状态码的强制转换是安全的
  Supplier<ExceptionModel> exceptionSupplier = (Supplier<ExceptionModel>) httpResponse.body();
  throw ClientServiceError.invalidResponse(exceptionSupplier.get());
}

// 强制转换为预期类型
Supplier<R> responseSupplier = (Supplier<R>) httpResponse.body();
return responseSupplier.get();

方案2:封装动态解析逻辑到Handler内部

扩展JsonBodyHandler,让它内部根据状态码自动切换解析类型:

private static class DynamicJsonBodyHandler<R> implements HttpResponse.BodyHandler<Supplier<?>> {

    private final Class<R> successClz;
    private final Class<ExceptionModel> failureClz;

    public DynamicJsonBodyHandler(Class<R> successClz) {
        this.successClz = successClz;
        this.failureClz = ExceptionModel.class;
    }

    private static <T> HttpResponse.BodySubscriber<Supplier<T>> asJson(Class<T> returnClz) {
        HttpResponse.BodySubscriber<InputStream> upstream = HttpResponse.BodySubscribers.ofInputStream();
        return HttpResponse.BodySubscribers.mapping(upstream, inputStream -> toSupplierOfType(inputStream, returnClz));
    }

    private static <T> Supplier<T> toSupplierOfType(InputStream inputStream, Class<T> returnClz) {
        return () -> {
            try (InputStream stream = inputStream) {
                ObjectMapper objectMapper = new ObjectMapper();
                return objectMapper.readValue(stream, returnClz);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        };
    }

    @Override
    public HttpResponse.BodySubscriber<Supplier<?>> apply(HttpResponse.ResponseInfo responseInfo) {
        // 根据状态码选择解析类型
        if (responseInfo.statusCode() == 200) {
            return asJson(successClz);
        } else {
            return asJson(failureClz);
        }
    }
}

在请求方法中使用该动态Handler:

<B, R> R exchange(URI uri, String method, B body, Class<R> returnClz) {
    Builder httpRequestBuilder = HttpRequest.newBuilder().uri(uri);
    addHeaders(httpRequestBuilder);

    var httpRequest =
        "GET".equals(method)
            ? httpRequestBuilder.GET().build()
            : httpRequestBuilder.method(method, getBodyPublisher(body)).build();

    var httpClient = HttpClient.newHttpClient();
    DynamicJsonBodyHandler<R> bodyHandler = new DynamicJsonBodyHandler<>(returnClz);
    HttpResponse<Supplier<?>> httpResponse = httpClient.send(httpRequest, bodyHandler);
    
    if (httpResponse.statusCode() != 200) {
        Supplier<ExceptionModel> exceptionSupplier = (Supplier<ExceptionModel>) httpResponse.body();
        throw ClientServiceError.invalidResponse(exceptionSupplier.get());
    }

    Supplier<R> responseSupplier = (Supplier<R>) httpResponse.body();
    return responseSupplier.get();
}

方案说明

  • 两种方案都利用泛型通配符?统一不同Supplier类型,解决编译时类型不兼容问题
  • 基于状态码的强制转换是安全的,因为我们已明确对应状态下的解析类型,不会出现运行时类型转换异常
  • 方案2将逻辑封装到Handler内部,代码更整洁,符合单一职责原则

内容的提问来源于stack exchange,提问作者Keshavram Kuduwa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 03:17:08