Прочитати тіло відповіді на помилку в 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. (В основному згадуючи про це для кращої зручності.)
— Йонік,

Відповіді:


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 () видасть виняток IO. Вам слід зловити виняток і прочитати з потоку помилок за допомогою getErrorStream (). Здається, це кращий підхід, ніж перевірка коду httpresponse.
— Сударшан Бхат

3
Проблема полягає в тому, що якщо ви прочитаєте код HttpUrlConnection.getErrorStream (), ви побачите, що він ЗАВЖДИ повертає нуль. (Java 6) :-(
— Gangnus

6
Чи не провалиться тут інші коди успіху, такі як "201 CREATED"?
— Багатий

4
У звіті про помилку пропонується перевірити httpConn.getResponseCode() >= 400(і їх обробка має помилку, перевертаючи вхідні потоки для використання)
— Dag

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, яка надає доступ до потоку ON виключення. Тож я думаю, це хороший спосіб для 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 набагато перевершує функціональність.
— Michael Piefel

Станом на 2014 рік найкращим може бути OkHttp (який фактично повертає екземпляри HttpURLConnection при відкритті URL-адреси). Особливо на Android це може допомогти вам уникнути деяких неприємних проблем як простого HttpURLConnection, так і Apache HttpClient.
— Jonik

2

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


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

4
Чи не провалиться тут інші коди успіху, такі як "201 CREATED"?
— Багатий

Так @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.