Прочитать тело ответа об ошибке в Java

В Java этот код выдает исключение, когда результат HTTP находится в диапазоне 404:

URL url = new URL("http://stackoverflow.com/asdf404notfound");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.getInputStream(); // throws!

В моем случае я знаю, что это 404, но я все равно хотел бы прочитать текст ответа.

(В моем фактическом случае код ответа - 403, но в теле ответа объясняется причина отказа, и я хотел бы показать это пользователю.)

Как я могу получить доступ к телу ответа?


person Dan Fabulich    schedule 05.03.2009    source источник
comment
Вы уверены, что сервер отправляет тело?   -  person Hank Gay    schedule 05.03.2009
comment
@jdigital: исключение, созданное HttpURLConnection.getInputStream (), - это java.io.FileNotFoundException. (В основном упоминание об этом для лучшей работы с Google.)   -  person Jonik    schedule 29.01.2014


Ответы (8)


Вот отчет об ошибке (закройте, не исправит, не ошибка ).

Их совет заключается в следующем:

HttpURLConnection httpConn = (HttpURLConnection)_urlConnection;
InputStream _is;
if (httpConn.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) {
    _is = httpConn.getInputStream();
} else {
     /* error from server */
    _is = httpConn.getErrorStream();
}
person TofuBeer    schedule 05.03.2009
comment
Действительно кажется, как и ожидалось, учитывая дизайн API, звучит разумно для меня - person matt b; 05.03.2009
comment
Разве вы не хотели бы получать поток ошибок, когда код ответа ›= 400, а не наоборот? - person Stephen Swensen; 13.04.2010
comment
В случае ошибок getInputStream () выдаст исключение ввода-вывода. Вы должны перехватить исключение и прочитать из потока ошибок с помощью getErrorStream (). Это кажется лучшим подходом, чем проверка кода httpresponse. - person Sudarshan Bhat; 20.08.2012
comment
Проблема в том, что если вы прочитаете код HttpUrlConnection.getErrorStream (), вы увидите, что он ВСЕГДА возвращает null. (Java 6) :-( - person Gangnus; 25.03.2014
comment
Не потерпят ли здесь ошибку другие коды успеха, такие как 201 CREATED? - person Rich; 20.10.2014
comment
Да Rich, код написан плохо, но это то, что они предоставили. Идея верная, реализация оставляет желать лучшего. - person TofuBeer; 20.10.2014
comment
В отчете об ошибке предлагается проверить httpConn.getResponseCode() >= 400 (и их обходной путь содержит ошибку, переключая входные потоки для использования) - person Dag; 09.08.2015
comment
Для ясности ... приведенный здесь пример конкретно относится к реализации клиента мыла. - person Maybe_Factor; 07.08.2017
comment
Мне это не кажется правильным. Реализация HttpURLConnection#getResponseCode() вызывает HttpURLConnection#getInputStream(), если код ответа еще не присутствует. Таким образом, попытка увидеть, какой должен быть код ответа, чтобы определить, какой поток вам нужен, приведет к той же ошибке. Это кажется неправильным. - person searchengine27; 02.01.2019
comment
@ searchchengine27 getResponseCode() вызывает getInputStream() только для проверки наличия соединения. Он улавливает и сохраняет исключение перед проверкой позиции заголовка 0, которая предназначена для кода состояния. Он все еще может выдавать здесь, но он вернет код состояния без исключения, если исключение выбрано на getInputStream(), а не проблема с получением самого кода состояния. - person doubleA; 18.09.2019

Это та же проблема, что и у меня: HttpUrlConnection возвращает FileNotFoundException, если вы пытаетесь прочитать getInputStream() из соединения.
Вместо этого вы должны использовать getErrorStream(), когда код состояния выше 400.

Более того, будьте осторожны, так как не только 200 - это код статуса успеха, даже 201, 204 и т. Д. Часто используются в качестве статусов успеха.

Вот пример того, как я справился с этим.

... connection code code code ...

// Get the response code 
int statusCode = connection.getResponseCode();

InputStream is = null;

if (statusCode >= 200 && statusCode < 400) {
   // Create an InputStream in order to extract the response object
   is = connection.getInputStream();
}
else {
   is = connection.getErrorStream();
}

... callback/response to your handler....

Таким образом, вы сможете получить необходимый ответ как в случае успеха, так и в случае ошибки.

Надеюсь, это поможет!

person gpiazzese    schedule 29.06.2015

В .Net у вас есть свойство Response для WebException, которое дает доступ к потоку при исключении. Так что я думаю, это хороший способ для Java, ...

private InputStream dispatch(HttpURLConnection http) throws Exception {
    try {
        return http.getInputStream();
    } catch(Exception ex) {
        return http.getErrorStream();
    }
}

Или реализация, которую я использовал. (Могут потребоваться изменения кодировки или другие вещи. Работает в текущей среде.)

private String dispatch(HttpURLConnection http) throws Exception {
    try {
        return readStream(http.getInputStream());
    } catch(Exception ex) {
        readAndThrowError(http);
        return null; // <- never gets here, previous statement throws an error
    }
}

private void readAndThrowError(HttpURLConnection http) throws Exception {
    if (http.getContentLengthLong() > 0 && http.getContentType().contains("application/json")) {
        String json = this.readStream(http.getErrorStream());
        Object oson = this.mapper.readValue(json, Object.class);
        json = this.mapper.writer().withDefaultPrettyPrinter().writeValueAsString(oson);
        throw new IllegalStateException(http.getResponseCode() + " " + http.getResponseMessage() + "\n" + json);
    } else {
        throw new IllegalStateException(http.getResponseCode() + " " + http.getResponseMessage());
    }
}

private String readStream(InputStream stream) throws Exception {
    StringBuilder builder = new StringBuilder();
    try (BufferedReader in = new BufferedReader(new InputStreamReader(stream))) {
        String line;
        while ((line = in.readLine()) != null) {
            builder.append(line); // + "\r\n"(no need, json has no line breaks!)
        }
        in.close();
    }
    System.out.println("JSON: " + builder.toString());
    return builder.toString();
}
person Vozzie    schedule 17.05.2016
comment
Это должен быть принятый ответ ... Интересно, почему люди до сих пор проверяют магические числа вместо обработки исключений ... - person SparK; 04.09.2016
comment
Интересно, почему это не принятый ответ. Мне очень помогли. Спасибо! - person Nikhil Jain; 25.06.2018

Я знаю, что это не дает прямого ответа на вопрос, но вместо использования библиотеки HTTP-соединений, предоставленной Sun, вы можете взглянуть на Commons HttpClient, который (на мой взгляд) имеет гораздо более простой API для работы.

person matt b    schedule 05.03.2009
comment
Позволю себе не согласиться. API от Sun намного проще, если вы делаете действительно простые вещи. Под простыми вещами я подразумеваю просто GET без излишней обработки ошибок, чего достаточно для большого количества случаев. Конечно, HttpClient намного превосходит по функциональности. - person Michael Piefel; 31.05.2011
comment
По состоянию на 2014 год лучшим вариантом может быть OkHttp (который фактически возвращает экземпляры HttpURLConnection при открытии URL-адреса) . Это может помочь вам избежать неприятных проблем как с обычным HttpURLConnection, так и с Apache HttpClient, особенно на Android. - person Jonik; 29.01.2014

Сначала проверьте код ответа, а затем используйте HttpURLConnection.getErrorStream()

person Chris Dolan    schedule 05.03.2009

Мой рабочий код.

  HttpURLConnection httpConn = (HttpURLConnection) urlConn;    
 if (httpConn.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) {
                        in = new InputStreamReader(urlConn.getInputStream());
                        BufferedReader bufferedReader = new BufferedReader(in);
                        if (bufferedReader != null) {
                            int cp;
                            while ((cp = bufferedReader.read()) != -1) {
                                sb.append((char) cp);
                            }
                            bufferedReader.close();
                        }
                            in.close();

                    } else {
                        /* error from server */
                        in = new InputStreamReader(httpConn.getErrorStream());
                    BufferedReader bufferedReader = new BufferedReader(in);
                    if (bufferedReader != null) {
                        int cp;
                        while ((cp = bufferedReader.read()) != -1) {
                            sb.append((char) cp);
                        }
                        bufferedReader.close();
                    }    
                    in.close();
                    }
                    System.out.println("sb="+sb);
person Durgesh Kumar    schedule 22.09.2019
comment
это действительно плохо. Правильная версия должна быть установлена ​​с обычным потоком или потоком ошибок, а затем иметь только ОДНУ последовательность из if (), которая считывается. Этот код ужасно избыточен. - person zakmck; 06.02.2021

Как читать текст ответа 404 в java:

Используйте библиотеку Apache - https://hc.apache.org/httpcomponents-client-4.5.x/httpclient/apidocs/

или Java 11 - https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpClient.html

В приведенном ниже фрагменте используется Apache:

import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.util.EntityUtils;

CloseableHttpClient client = HttpClients.createDefault();
CloseableHttpResponse resp = client.execute(new HttpGet(domainName + "/blablablabla.html"));
String response = EntityUtils.toString(resp.getEntity());
person Esakkiappan .E    schedule 19.03.2020

person    schedule
comment
Не потерпят ли здесь ошибку другие коды успеха, такие как 201 CREATED? - person Rich; 20.10.2014
comment
Да @Rich, поэтому лучше сделать: if (httpConn.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) { - person AO_; 21.11.2017