Przeczytaj treść odpowiedzi na błąd w Javie


93

W Javie ten kod zgłasza wyjątek, gdy wynik HTTP to zakres 404:

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

W moim przypadku tak się składa, że ​​wiem, że treść to 404, ale i tak chciałbym przeczytać treść odpowiedzi.

(W moim przypadku kod odpowiedzi to 403, ale treść odpowiedzi wyjaśnia powód odrzucenia i chciałbym wyświetlić to użytkownikowi).

Jak mogę uzyskać dostęp do treści odpowiedzi?


Czy na pewno serwer wysyła treść?
Hank Gay

2
@jdigital: wyjątek zgłoszony przez HttpURLConnection.getInputStream () to java.io.FileNotFoundException. (Głównie wspominając o tym dla lepszej widoczności w Google.)
Jonik

Odpowiedzi:


172

Oto raport o błędzie (zamknij, nie naprawi, nie jest to błąd).

Ich rada to kodowanie w ten sposób:

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

5
Czy nie chciałbyś uzyskać strumienia błędów, gdy kod odpowiedzi to> = 400, a nie na odwrót?
Stephen Swensen

3
W przypadku błędów getInputStream () zgłosi wyjątek IO. Powinieneś złapać wyjątek i odczytać ze strumienia błędów za pomocą metody getErrorStream (). Wydaje się, że jest to lepsze podejście niż sprawdzanie kodu httpresponse.
Sudarshan Bhat,

3
Problem polega na tym, że jeśli przeczytasz kod HttpUrlConnection.getErrorStream (), zobaczysz, że ZAWSZE zwraca on null. (Java 6) :-(
Gangnus,

6
Czy inne kody sukcesu, takie jak „Utworzono 201”, nie zawiodą tutaj?
Rich

4
Raport błędów sugerują sprawdzania httpConn.getResponseCode() >= 400(i ich obejścia zawiera błąd, odchylając inputstreams do użytku)
Dag

14

To ten sam problem, który miałem: HttpUrlConnectionzwraca, FileNotFoundExceptionjeśli spróbujesz odczytać getInputStream()z połączenia.
Zamiast tego należy używać, getErrorStream()gdy kod statusu jest wyższy niż 400.

Co więcej, bądź ostrożny, ponieważ kod statusu sukcesu to nie tylko 200, nawet 201, 204 itd. Są często używane jako statusy sukcesu.

Oto przykład tego, jak sobie z tym poradziłem

... 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....

W ten sposób będziesz w stanie uzyskać potrzebną odpowiedź zarówno w przypadku powodzenia, jak i błędów.

Mam nadzieję że to pomoże!


13

W .Net masz właściwość Response z WebException, która daje dostęp do strumienia ON jako wyjątek. Więc myślę, że to dobry sposób na Javę, ...

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

Albo implementacja, której użyłem. (Może wymagać zmian w kodowaniu lub innych rzeczach. Działa w obecnym środowisku).

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

To powinna być akceptowana odpowiedź ... Zastanawiam się, dlaczego ludzie nadal weryfikują magiczne liczby zamiast obsługiwać wyjątki ...
SparK

Zastanawiam się, dlaczego nie jest to akceptowana odpowiedź. Bardzo mi pomogło. Dzięki!
Nikhil Jain

2

Wiem, że to nie odpowiada bezpośrednio na pytanie, ale zamiast korzystać z biblioteki połączeń HTTP dostarczonej przez Sun, możesz rzucić okiem na Commons HttpClient , który (moim zdaniem) ma znacznie łatwiejszy interfejs API.


4
Pozwolę sobie być innego zdania. API firmy Sun jest dużo łatwiejsze, o ile wykonujesz naprawdę proste rzeczy. Przez proste rzeczy mam na myśli tylko GET bez zbytniej obsługi błędów, co jest wystarczające w wielu przypadkach. Oczywiście HttpClient ma znacznie lepszą funkcjonalność.
Michael Piefel

Od 2014 r. Najlepszym może być OkHttp (który w rzeczywistości zwraca wystąpienia HttpURLConnection podczas otwierania adresu URL). Szczególnie w systemie Android może pomóc uniknąć nieprzyjemnych problemów zarówno zwykłego HttpURLConnection, jak i Apache HttpClient.
Jonik,

2

Najpierw sprawdź kod odpowiedzi, a następnie użyj HttpURLConnection.getErrorStream()


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

4
Czy inne kody sukcesu, takie jak „Utworzono 201”, nie zawiodą tutaj?
Rich

Tak @Rich, dlatego lepiej to zrobić:if (httpConn.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) {
AO_

1

Mój działający kod.

  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

Jak czytać treść odpowiedzi 404 w java:

Użyj biblioteki Apache - https://hc.apache.org/httpcomponents-client-4.5.x/httpclient/apidocs/

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

Podany poniżej fragment korzysta z 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());
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.