[英]How to log request/response using java.net.http.HttpClient?
[英]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 狀態碼?
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 狀態的推薦方法。 您將依賴未指定的行為(盡管它可能會達到您的預期)。
如果您想在狀態為 = 200: 的情況下返回異常:那么我的建議是編寫一個正文訂閱者:
另一方面 - 如果您想要在 status != 200 的情況下使用不同的結果類型,您可以編寫一個返回元組 (response, error) 的BodyHandler
,其中 response 是一種類型,而 error 是另一種類型。 像這樣的東西:
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();
}
}
我很驚訝 JDK 文檔在這樣一個常見且必要的用例上如此含糊:正確檢測 HTTP 請求的失敗。
閱讀 API 文檔后,仔細閱讀此處的討論,並深入研究源代碼; 我已經確定適當的邏輯可能像這樣簡單:
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);
這看起來對嗎? 我是否正確使用了 API? 基本上我只想丟棄正文,如果有人要求返回null
Path
(表明正文未保存在文件中),並導致完成階段失敗。
(JDK 確實需要一種更好的方法來解決這個問題,至少包括一些預制類。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.