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
相关产品推荐
相关产品推荐

