如何/在哪里检查 java.net.http.HttpClient HTTP 响应状态代码

Mar*_*234 9 java java-http-client

Java 11java.net.http.HttpClient似乎不检查 HTTP 状态代码,重定向除外(如果启用)。wiki和 Java API 文档中找到的所有示例始终假设 HTTP 请求是成功的,即它们似乎从不检查响应的状态代码。

这很可能永远不是所需的行为,因为来自 HTTP 500(服务器错误)响应的错误页面可能具有不同的格式或没有格式,因此应用程序无法处理。

应该在哪里检查 HTTP 状态代码?

的文档HttpResponse.BodyHandler包含以下示例片段:

BodyHandler<Path> bodyHandler = (rspInfo) -> rspInfo.statusCode() == 200
    ? BodySubscribers.ofFile(Paths.get("/tmp/f"))
    : BodySubscribers.replacing(Paths.get("/NULL"));
Run Code Online (Sandbox Code Playgroud)

但是,您必须检查两次状态代码,一次在BodyHandler上面显示的情况下,一次在处理响应时(因为尝试从中读取正文"/NULL"会失败)。

对我来说,仅执行 HTTP 状态代码检查似乎是最合理的BodyHandler,例如:

BodyHandler<Path> bodyHandler = (rspInfo) -> {
    if (rspInfo.statusCode() == 200) {
        return BodySubscribers.ofFile(Paths.get("/tmp/f"));
    } else {
        throw new RuntimeException("Request failed");
    }
};
Run Code Online (Sandbox Code Playgroud)

但是,BodyHandler文档没有提及是否允许抛出异常,或者HttpClient在这种情况下会如何表现。

让我感到惊讶的是,JDK 似乎没有提供开箱即用的处理不成功 HTTP 响应的功能,还是我忽略了某些东西?

dan*_*iel 5

尝试HttpClient很好地捕获用户代码引发的异常 - 但这不是处理非 200 状态的推荐方法。您将依赖于未指定的行为(尽管它可能会按照您的预期进行)。

如果您想在状态为 != 200 的情况下返回异常,那么我的建议是编写一个正文订阅者:

  1. 返回失败的 CompletionStage(如果没有异常,您将异常完成)
  2. 取消订阅(或将其转发给 BodySubscribers.discarding() 订阅者)

另一方面 - 如果您想要在 status != 200 的情况下使用不同的结果类型,您可以编写一个BodyHandler返回元组(响应,错误)的元组,其中响应是一种类型,错误是另一种类型。像这样的东西:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)