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


93

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

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

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

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

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


Вы уверены, что сервер отправляет тело?
Hank Gay,

2
@jdigital: исключение, созданное HttpURLConnection.getInputStream (), - это java.io.FileNotFoundException. (В основном упоминание об этом для удобства просмотра в Google.)
Jonik

Ответы:


172

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

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

HttpURLConnection httpConn = (HttpURLConnection)_urlConnection;
InputStream _is;
if (httpConn.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) {
    _is = httpConn.getInputStream();
} else {
     /* error from server */
    _is = httpConn.getErrorStream();
}

5
Разве вы не хотели бы получать поток ошибок, когда код ответа> = 400, а не наоборот?
Стивен Свенсен,

3
В случае ошибок getInputStream () выдаст исключение ввода-вывода. Вы должны перехватить исключение и прочитать из потока ошибок с помощью getErrorStream (). Это кажется лучшим подходом, чем проверка кода httpresponse.
Сударшан Бхат,

3
Проблема в том, что если вы прочитаете код HttpUrlConnection.getErrorStream (), вы увидите, что он ВСЕГДА возвращает null. (Java 6) :-(
Gangnus

6
Не потерпят ли здесь ошибку другие коды успеха, такие как «201 CREATED»?
Rich

4
В отчете об ошибке предлагается проверить httpConn.getResponseCode() >= 400(и их обходной путь содержит ошибку, переключая входные потоки для использования)
Даг

14

Это та же проблема, что и у меня: 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....

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

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


13

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

Это должен быть принятый ответ ... Интересно, почему люди до сих пор проверяют магические числа вместо обработки исключений ...
SparK

Интересно, почему это не принятый ответ. Мне очень помогли. Благодарность!
Nikhil Jain

2

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


4
Позволю себе не согласиться. API от Sun намного проще, если вы делаете действительно простые вещи. Под простыми вещами я подразумеваю просто GET без излишней обработки ошибок, чего достаточно для большого количества случаев. Конечно, HttpClient намного превосходит по функциональности.
Майкл Пифель

По состоянию на 2014 год лучшим вариантом может быть OkHttp (который фактически возвращает экземпляры HttpURLConnection при открытии URL-адреса). Это может помочь вам избежать некоторых неприятных проблем как простого HttpURLConnection, так и Apache HttpClient, особенно на Android.
Jonik


1
InputStream is = null;
if (httpConn.getResponseCode() !=200) {
    is = httpConn.getErrorStream();
} else {
     /* error from server */
    is = httpConn.getInputStream();
}

4
Не потерпят ли здесь ошибку другие коды успеха, такие как «201 CREATED»?
Rich

Да @Rich, вот почему так лучше сделать:if (httpConn.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) {
AO_

1

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

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

0

Как прочитать тело ответа 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());
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.