简体   繁体   English

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

[英]How / where to check for java.net.http.HttpClient HTTP response status codes

Java 11's java.net.http.HttpClient does not seem to check the HTTP status code, except for redirects (if enabled). Java 11 的java.net.http.HttpClient似乎没有检查 HTTP 状态代码,重定向除外(如果启用)。 And all examples found in the wiki and in the Java API documentation always assume that HTTP requests are successful, ie they never seem to check the status code of the response.wiki和 Java API 文档中找到的所有示例始终假定 HTTP 请求是成功的,即它们似乎从不检查响应的状态代码。

This is most likely never the desired behavior because an error page from an HTTP 500 (server error) response has probably a different, or no format and can therefore not be handled by the application.这很可能永远不是所需的行为,因为来自 HTTP 500(服务器错误)响应的错误页面可能具有不同的格式或没有格式,因此无法由应用程序处理。

Where should the check for the HTTP status code occur?应该在哪里检查 HTTP 状态码?

The documentation of HttpResponse.BodyHandler contains the following example snippet: HttpResponse.BodyHandler的文档包含以下示例片段:

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

However, then you would have to check twice for the status code, once in the BodyHandler shown above, and once when handling the response (since trying to read the body from "/NULL" would fail).但是,您必须检查两次状态代码,一次在上面显示的BodyHandler中,一次在处理响应时(因为尝试从"/NULL"读取正文会失败)。

To me it seems most reasonable to perform the HTTP status code check in the BodyHandler only, eg:对我来说,仅在BodyHandler中执行 HTTP 状态代码检查似乎是最合理的,例如:

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

However, the BodyHandler documentation does not mention whether it is allowed to throw exceptions, or how HttpClient would behave in that case.但是, BodyHandler文档没有提到是否允许抛出异常,或者HttpClient在这种情况下会如何表现。

It is also surprises me that the JDK does not seem to offer functionality for handling unsuccessful HTTP responses out of the box, or am I overlooking something?让我感到惊讶的是,JDK 似乎没有提供处理不成功的 HTTP 响应的功能,还是我忽略了什么?

The HttpClient attempts to do a good job at catching exceptions thrown by user code - but this is not the recommended way of dealing with non 200 status. HttpClient试图很好地捕捉用户代码抛出的异常——但这不是处理非 200 状态的推荐方法。 You'd be relying on unspecified behavior (though it would probably do what you expect).您将依赖未指定的行为(尽管它可能会达到您的预期)。

If you want to return an exception in case of a status which is,= 200: then my recommendation would be to write a body subscriber that:如果您想在状态为 = 200: 的情况下返回异常:那么我的建议是编写一个正文订阅者:

  1. Return a failed CompletionStage (which you would have completed exceptionally with your exception)返回一个失败的 CompletionStage (你会在你的异常情况下异常完成)
  2. Cancel the subscription (or forwards it to a BodySubscribers.discarding() subscriber)取消订阅(或将其转发给 BodySubscribers.discarding() 订阅者)

On the other hand - if you want a different result type for the case where status != 200 you could write a BodyHandler that returns a tuple (response, error) where response is of one type and error is of another type.另一方面 - 如果您想要在 status != 200 的情况下使用不同的结果类型,您可以编写一个返回元组 (response, error) 的BodyHandler ,其中 response 是一种类型,而 error 是另一种类型。 Something like this:像这样的东西:

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();
    }
}

I'm astounded that the JDK documentation could be so vague on such a common and necessary use case: correctly detecting failure for an HTTP request.我很惊讶 JDK 文档在这样一个常见且必要的用例上如此含糊:正确检测 HTTP 请求的失败。

After reading the API docs, poring over the discussion here, and digging into the source code;阅读 API 文档后,仔细阅读此处的讨论,并深入研究源代码; I've determined that the appropriate logic might be as simple as this:我已经确定适当的逻辑可能像这样简单:

final BodyHandler<Path> bodyHandler = responseInfo -> {
  final BodySubscriber<Path> bodySubscriber;
  final int statusCode = responseInfo.statusCode();
  if(statusCode == 200) {
    bodySubscriber = BodySubscribers.ofFile(toFile);
  } else {
    bodySubscriber = BodySubscribers.replacing(null);
    bodySubscriber.onError(new IOException("HTTP request failed with response status code "
        + statusCode + "."));
  }
  return bodySubscriber;
};
getHttpClient().send(httpRequest, bodyHandler);

Does that seem right?这看起来对吗? Am I using the API correctly?我是否正确使用了 API? Basically I just want to discard the body, return a null Path if someone asks for it (indicating the body was not saved in a file), and result in a failed completion stage.基本上我只想丢弃正文,如果有人要求返回null Path (表明正文未保存在文件中),并导致完成阶段失败。

(The JDK really needs a better approach to this, including some pre-made classes at the very least.) (JDK 确实需要一种更好的方法来解决这个问题,至少包括一些预制类。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用 java.net.http.HttpClient 记录请求/响应? - How to log request/response using java.net.http.HttpClient? 如何使用 java.net.http.HttpClient 跳过中间状态代码(如 103)并只读最终状态代码 - How to skip intermediary status codes (like 103) with java.net.http.HttpClient and read only the final status code Java - ClassNotFoundException: java.net.http.HttpClient - Java - ClassNotFoundException: java.net.http.HttpClient 摘要认证 java.net.http.HttpClient - Digest Authentication java.net.http.HttpClient 如何给java.net.http.HttpClient GET请求添加参数? - How to add parameters to java.net.http.HttpClient GET request? 如何禁用 openJdk 11 java.net.http.HttpClient 日志记录? - How to disable openJdk 11 java.net.http.HttpClient logging? 如何更改 java.net.http.HttpClient 的用户代理字符串 - How to change the user agent string for java.net.http.HttpClient 来自 java.net.http.HttpClient 调用的不可靠响应使用 Java 11 和 Spring 启动 - Unreliable response from java.net.http.HttpClient call using Java 11 and Spring Boot 重新加载 java.net.http.HttpClient 的 SSLContext - Reloading a java.net.http.HttpClient's SSLContext 撰写桌面应用程序崩溃:ClassNotFoundException:java.net.http.HttpClient - Compose Desktop application crashing: ClassNotFoundException: java.net.http.HttpClient
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM