Как / где проверить наличие java.net.http.Коды состояния HTTP-ответа HttpClient

#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, то моя рекомендация состояла бы в том, чтобы написать основной подписчик, который:

  1. Верните неудачный этап завершения (который вы завершили бы исключительно с вашим исключением)
  2. Отмените подписку (или перенаправьте ее подписчику 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();
    }
}