Прочитати тіло відповіді на помилку в 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.