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

Java java.net.http.HttpClient异步调用exceptionallyAsync错误处理实现方案

问题澄清

首先需要明确java.net.http.HttpClient的默认行为:sendAsync方法只有在请求级失败(如超时、连接错误、请求中断、DNS解析失败等场景)才会进入异常处理链路,所有能拿到服务端HTTP响应的场景,哪怕返回的是4xx、5xx错误状态码,都会走正常的thenApply处理链路,不会触发异常。

代码实现方案

基于你现有写法的改造

你需要分别在正常响应分支处理状态码逻辑,在异常分支处理请求失败逻辑,exceptionallyAsync的返回值需要和上游泛型保持一致,即HttpResponse<String>类型,你可以根据业务需要选择返回自定义响应或者null:

import java.net.ConnectException;
import java.net.http.HttpTimeoutException;
import java.net.http.HttpResponse;
import java.util.Collections;
import java.util.Optional;

CompletableFuture<HttpResponse<String>> response =
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
  .thenApplyAsync(resp -> {
    // 所有收到HTTP响应的场景都会走到这里,先执行自定义代码
    System.out.println("收到服务端响应,状态码:" + resp.statusCode());
    // 你可以在这里添加不同状态码的处理逻辑
    if (resp.statusCode() >= 400) {
      // 4xx、5xx错误的自定义处理代码
      System.out.println("请求业务失败,响应内容:" + resp.body());
    } else {
      // 2xx正常响应的自定义处理代码
      System.out.println("请求业务成功");
    }
    return resp;
  })
  .exceptionallyAsync(exception -> {
    // 超时、连接失败等请求级异常会走到这里,执行自定义代码
    System.out.println("请求执行失败,错误信息:" + exception.getMessage());
    // 异常类型判断,单独处理超时场景
    if (exception instanceof HttpTimeoutException) {
      System.out.println("触发请求超时,执行超时专属处理逻辑");
    } else if (exception instanceof ConnectException) {
      System.out.println("连接服务器失败,执行连接错误专属处理逻辑");
    }
    // 这里返回值要匹配HttpResponse<String>类型,两种可选方案:
    // 方案1:后续不需要用到response的结果,直接返回null即可,注意后续使用时做空判断
    // return null;
    // 方案2:构造自定义响应,方便后续统一处理逻辑
    return new HttpResponse<String>() {
      @Override
      public int statusCode() {
        return 599; // 自定义异常状态码,和服务端返回的标准状态码区分
      }
      @Override
      public HttpRequest request() {
        return request;
      }
      @Override
      public Optional<HttpResponse<?>> previousResponse() {
        return Optional.empty();
      }
      @Override
      public HttpHeaders headers() {
        return HttpHeaders.of(Collections.emptyMap(), (k, v) -> true);
      }
      @Override
      public String body() {
        return "请求执行异常:" + exception.getMessage();
      }
      @Override
      public SSLSession sslSession() {
        return null;
      }
      @Override
      public URI uri() {
        return request.uri();
      }
      @Override
      public HttpClient.Version version() {
        return HttpClient.Version.HTTP_1_1;
      }
    };
  });

更简洁的统一处理方案

如果你只需要在请求结束(不管正常还是异常)时执行统一的自定义代码,更推荐使用whenCompleteAsync方法,不用拆分两个分支写重复逻辑:

CompletableFuture<HttpResponse<String>> response =
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
  .whenCompleteAsync((resp, exception) -> {
    // 这段代码不管请求正常还是异常都会执行
    System.out.println("请求生命周期结束");
    if (exception != null) {
      // 异常处理逻辑
      System.out.println("请求失败:" + exception.getMessage());
    } else {
      // 响应处理逻辑
      System.out.println("响应状态码:" + resp.statusCode());
    }
  });

注意事项

  • 如果你在exceptionallyAsync里返回null,后续调用response.get()等方法获取结果时一定要做空判断,避免空指针异常
  • 超时异常HttpTimeoutException只有你在HttpClient构建或者HttpRequest构建时配置了超时参数才会触发,没有配置超时的情况下请求会一直等待不会抛出该异常

内容的提问来源于stack exchange,提问作者Hollow.Quincy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 13:18:00