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?

Dan Fabulich
źródło
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();
}
TofuBeer
źródło
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!

gpiazzese
źródło
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();
}
Vozzie
źródło
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.

matowe b
źródło
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()

Chris Dolan
źródło
1
InputStream is = null;
if (httpConn.getResponseCode() !=200) {
    is = httpConn.getErrorStream();
} else {
     /* error from server */
    is = httpConn.getInputStream();
}
sth
źródło
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);
Durgesh Kumar
źródło
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());
Esakkiappan .E
źródło