使用Java HttpClient(JsonBodyHandler)如何处理非200状态响应?
Java HttpClient 处理非200响应及差异化JSON反序列化
核心问题说明
Java HttpClient 默认不会像RestTemplate那样在非200状态码时自动抛出异常,而是会继续尝试用指定的BodyHandler处理响应体。当错误响应的JSON结构和成功响应的POJO不匹配时,就会触发反序列化异常。
解决方案思路
我们需要:
- 先检查响应的状态码,区分成功/错误场景
- 针对错误场景,用对应的错误POJO去反序列化响应体
- 可以通过自定义BodyHandler封装逻辑,或者在获取响应后手动分支处理
具体实现步骤
1. 定义错误响应的POJO
对应服务端返回的错误结构,创建匹配的实体类:
public class ApiError { private ErrorDetail error; // getter、setter、无参构造器 public ErrorDetail getError() { return error; } public void setError(ErrorDetail error) { this.error = error; } } public class ErrorDetail { private String code; private String message; // getter、setter、无参构造器 public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
2. 自定义BodyHandler处理差异化反序列化
实现一个通用的ConditionalJsonBodyHandler,根据状态码自动选择反序列化的目标类:
import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException; import java.io.InputStream; import java.net.http.HttpResponse; import java.util.function.Supplier; public class ConditionalJsonBodyHandler<T> implements HttpResponse.BodyHandler<Supplier<T>> { private final Class<T> successClass; private final Class<?> errorClass; private final ObjectMapper objectMapper = new ObjectMapper(); public ConditionalJsonBodyHandler(Class<T> successClass, Class<?> errorClass) { this.successClass = successClass; this.errorClass = errorClass; } @Override public HttpResponse.BodySubscriber<Supplier<T>> apply(HttpResponse.ResponseInfo responseInfo) { int statusCode = responseInfo.statusCode(); Class<?> targetClass = (statusCode >= 200 && statusCode < 300) ? successClass : errorClass; return HttpResponse.BodySubscribers.mapping( HttpResponse.BodySubscribers.ofInputStream(), inputStream -> () -> { try (InputStream is = inputStream) { return targetClass.cast(objectMapper.readValue(is, targetClass)); } catch (IOException e) { throw new RuntimeException("Failed to deserialize response", e); } } ); } }
3. 使用自定义Handler处理请求
修改原有请求代码,通过自定义Handler统一处理成功和错误响应:
// 创建客户端 var client = HttpClient.newHttpClient(); // 创建请求 var request = HttpRequest.newBuilder( URI.create("https://api.nasa.gov/planetary/apod?api_key=INCORRECT_KEY")) .header("accept", "application/json") .build(); // 发送请求,使用自定义Handler var response = client.send(request, new ConditionalJsonBodyHandler<>(APOD.class, ApiError.class)); int statusCode = response.statusCode(); if (statusCode >= 200 && statusCode < 300) { APOD apod = response.body().get(); // 处理成功逻辑 System.out.println("成功获取APOD: " + apod.getTitle()); } else { ApiError apiError = (ApiError) response.body().get(); // 处理错误逻辑 System.out.println("请求错误: 代码=" + apiError.getError().getCode() + ", 消息=" + apiError.getError().getMessage()); }
替代方案:手动分支处理
如果不想自定义Handler,也可以先获取原始响应,再根据状态码分别处理:
var client = HttpClient.newHttpClient(); var request = HttpRequest.newBuilder(URI.create("https://api.nasa.gov/planetary/apod?api_key=INCORRECT_KEY")) .header("accept", "application/json") .build(); // 先获取带InputStream的原始响应 var rawResponse = client.send(request, HttpResponse.BodyHandlers.ofInputStream()); int statusCode = rawResponse.statusCode(); ObjectMapper objectMapper = new ObjectMapper(); try (InputStream is = rawResponse.body()) { if (statusCode >= 200 && statusCode < 300) { APOD apod = objectMapper.readValue(is, APOD.class); // 成功逻辑处理 } else { ApiError apiError = objectMapper.readValue(is, ApiError.class); // 错误逻辑处理 } } catch (IOException e) { // 处理IO或反序列化异常 }
关键说明
- 自定义Handler的方式更贴合HttpClient的设计模式,将反序列化逻辑封装统一管理
- 两种方案的核心都是先判断状态码,再选择对应的POJO进行反序列化
- 需确保Jackson能正确处理POJO反序列化(如提供无参构造器、字段名与JSON键匹配等)
内容的提问来源于stack exchange,提问作者CodeBot
相关产品推荐
相关产品推荐

