#java #java-http-client
Вопрос:
Java 11 java.net.http.HttpClient
, похоже, не проверяет код состояния HTTP, за исключением перенаправлений (если они включены). И все примеры, найденные в вики и в документации 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"
будет неудачной).
Мне кажется наиболее разумным выполнить проверку кода состояния HTTP в BodyHandler
единственном, например:
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-ответов из коробки, или я что-то упускаю из виду?
Комментарии:
1. Поскольку не у всех ответов есть тело, я предполагаю, что основной статус для проверки-это статус ответа. Возможность считывать статус в обработчике тела может быть просто для предотвращения отходов.
Ответ №1:
HttpClient
Попытки выполнить хорошую работу по перехвату исключений, создаваемых пользовательским кодом, — но это не рекомендуемый способ работы со статусом не 200. Вы будете полагаться на неопределенное поведение (хотя, вероятно, оно будет делать то, что вы ожидаете).
Если вы хотите вернуть исключение в случае статуса, который равен != 200, то моя рекомендация состояла бы в том, чтобы написать основной подписчик, который:
- Верните неудачный этап завершения (который вы завершили бы исключительно с вашим исключением)
- Отмените подписку (или перенаправьте ее подписчику BodySubscribers.discarding ())
С другой стороны, если вам нужен другой тип результата для случая, когда status != 200, вы можете написать a 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();
}
}