如何/在哪里检查 java.net.http.HttpClient HTTP 响应状态代码
How / where to check for java.net.http.HttpClient HTTP response status codes
Java 11 的 java.net.http.HttpClient
似乎不检查 HTTP 状态代码,除了重定向(如果启用)。 wiki 和 Java API 文档中的所有示例始终假定 HTTP 请求成功,即它们似乎从不检查响应的状态代码。
这很可能永远不是期望的行为,因为来自 HTTP 500(服务器错误)响应的错误页面可能具有不同的格式或没有格式,因此应用程序无法处理。
应该在哪里检查 HTTP 状态代码?
documentation of HttpResponse.BodyHandler
包含以下示例片段:
BodyHandler<Path> bodyHandler = (rspInfo) -> rspInfo.statusCode() == 200
? BodySubscribers.ofFile(Paths.get("/tmp/f"))
: BodySubscribers.replacing(Paths.get("/NULL"));
但是,您必须检查两次状态代码,一次是在上面显示的 BodyHandler
中,一次是在处理响应时(因为尝试从 "/NULL"
读取正文会失败)。
对我来说,仅在 BodyHandler
中执行 HTTP 状态代码检查似乎是最合理的,例如:
BodyHandler<Path> bodyHandler = (rspInfo) -> {
if (rspInfo.statusCode() == 200) {
return BodySubscribers.ofFile(Paths.get("/tmp/f"));
} else {
throw new RuntimeException("Request failed");
}
};
但是,BodyHandler
文档没有提到是否允许抛出异常,或者 HttpClient
在这种情况下会如何表现。
令我惊讶的是,JDK 似乎没有提供开箱即用的处理不成功 HTTP 响应的功能,或者我忽略了什么?
HttpClient
试图很好地捕获用户代码抛出的异常 - 但这不是处理非 200 状态的推荐方法。您将依赖未指定的行为(尽管它可能会按照您的预期进行)。
如果您想 return 状态为 != 200 的例外情况,那么我的建议是编写一个正文订阅者:
- Return 一个失败的 CompletionStage(如果有异常,您本可以异常完成)
- 取消订阅(或转发给 BodySubscribers.discarding() 订阅者)
另一方面 - 如果你想要一个不同的结果类型用于状态!= 200 你可以写一个 BodyHandler
那 returns 一个元组(响应,错误)其中响应是是一种类型,错误是另一种类型。像这样:
record Response<R,T>(R response, T error) {}
static class ErrorBodyHandler<R,T> implements BodyHandler<Response<R,T>> {
final BodyHandler<R> responseHandler;
final BodyHandler<T> errorHandler;
public ErrorBodyHandler(BodyHandler<R> responseHandler, BodyHandler<T> errorHandler) {
this.responseHandler = responseHandler;
this.errorHandler = errorHandler;
}
@Override
public BodySubscriber<Response<R, T>> apply(ResponseInfo responseInfo) {
if (responseInfo.statusCode() == 200) {
return BodySubscribers.mapping(responseHandler.apply(responseInfo),
(r) -> new Response<>(r, null));
} else {
return BodySubscribers.mapping(errorHandler.apply(responseInfo),
(t) -> new Response<>(null, t));
}
}
}
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var handler = new ErrorBodyHandler<>(BodyHandlers.ofFileDownload(Path.of(".")),
BodyHandlers.ofString());
var request = HttpRequest
.newBuilder(URI.create("http://host:port/"))
.build();
var httpResponse =
client.send(request, handler);
if (httpResponse.statusCode() == 200) {
Path path = httpResponse.body().response();
} else {
String error = httpResponse.body().error();
}
}
Java 11 的 java.net.http.HttpClient
似乎不检查 HTTP 状态代码,除了重定向(如果启用)。 wiki 和 Java API 文档中的所有示例始终假定 HTTP 请求成功,即它们似乎从不检查响应的状态代码。
这很可能永远不是期望的行为,因为来自 HTTP 500(服务器错误)响应的错误页面可能具有不同的格式或没有格式,因此应用程序无法处理。
应该在哪里检查 HTTP 状态代码?
documentation of HttpResponse.BodyHandler
包含以下示例片段:
BodyHandler<Path> bodyHandler = (rspInfo) -> rspInfo.statusCode() == 200
? BodySubscribers.ofFile(Paths.get("/tmp/f"))
: BodySubscribers.replacing(Paths.get("/NULL"));
但是,您必须检查两次状态代码,一次是在上面显示的 BodyHandler
中,一次是在处理响应时(因为尝试从 "/NULL"
读取正文会失败)。
对我来说,仅在 BodyHandler
中执行 HTTP 状态代码检查似乎是最合理的,例如:
BodyHandler<Path> bodyHandler = (rspInfo) -> {
if (rspInfo.statusCode() == 200) {
return BodySubscribers.ofFile(Paths.get("/tmp/f"));
} else {
throw new RuntimeException("Request failed");
}
};
但是,BodyHandler
文档没有提到是否允许抛出异常,或者 HttpClient
在这种情况下会如何表现。
令我惊讶的是,JDK 似乎没有提供开箱即用的处理不成功 HTTP 响应的功能,或者我忽略了什么?
HttpClient
试图很好地捕获用户代码抛出的异常 - 但这不是处理非 200 状态的推荐方法。您将依赖未指定的行为(尽管它可能会按照您的预期进行)。
如果您想 return 状态为 != 200 的例外情况,那么我的建议是编写一个正文订阅者:
- Return 一个失败的 CompletionStage(如果有异常,您本可以异常完成)
- 取消订阅(或转发给 BodySubscribers.discarding() 订阅者)
另一方面 - 如果你想要一个不同的结果类型用于状态!= 200 你可以写一个 BodyHandler
那 returns 一个元组(响应,错误)其中响应是是一种类型,错误是另一种类型。像这样:
record Response<R,T>(R response, T error) {}
static class ErrorBodyHandler<R,T> implements BodyHandler<Response<R,T>> {
final BodyHandler<R> responseHandler;
final BodyHandler<T> errorHandler;
public ErrorBodyHandler(BodyHandler<R> responseHandler, BodyHandler<T> errorHandler) {
this.responseHandler = responseHandler;
this.errorHandler = errorHandler;
}
@Override
public BodySubscriber<Response<R, T>> apply(ResponseInfo responseInfo) {
if (responseInfo.statusCode() == 200) {
return BodySubscribers.mapping(responseHandler.apply(responseInfo),
(r) -> new Response<>(r, null));
} else {
return BodySubscribers.mapping(errorHandler.apply(responseInfo),
(t) -> new Response<>(null, t));
}
}
}
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var handler = new ErrorBodyHandler<>(BodyHandlers.ofFileDownload(Path.of(".")),
BodyHandlers.ofString());
var request = HttpRequest
.newBuilder(URI.create("http://host:port/"))
.build();
var httpResponse =
client.send(request, handler);
if (httpResponse.statusCode() == 200) {
Path path = httpResponse.body().response();
} else {
String error = httpResponse.body().error();
}
}