Leggi il corpo della risposta all'errore in Java


93

In Java, questo codice genera un'eccezione quando il risultato HTTP è l'intervallo 404:

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

Nel mio caso, so che il contenuto è 404, ma mi piacerebbe comunque leggere il corpo della risposta.

(Nel mio caso reale il codice di risposta è 403, ma il corpo della risposta spiega il motivo del rifiuto e vorrei mostrarlo all'utente.)

Come posso accedere al corpo della risposta?


Sei sicuro che il server stia inviando un corpo?
Hank Gay

2
@jdigital: l'eccezione generata da HttpURLConnection.getInputStream () è java.io.FileNotFoundException. (Principalmente menzionandolo per una migliore googlability.)
Jonik

Risposte:


172

Ecco il bug report (chiudere, non risolverà, non un bug).

Il loro consiglio è di codificare in questo modo:

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

5
Non vorresti ottenere il flusso di errore quando il codice di risposta è> = 400, piuttosto che il contrario?
Stephen Swensen

3
In caso di errori, getInputStream () genererà un'eccezione IO. Dovresti catturare l'eccezione e leggere dal flusso di errore usando getErrorStream (). Questo sembra essere un approccio migliore rispetto al controllo del codice http://www.httpresponse.com
Sudarshan Bhat

3
Il problema è che se leggi il codice HttpUrlConnection.getErrorStream (), vedrai che restituisce SEMPRE null. (Java 6) :-(
Gangnus

6
Altri codici di successo come "201 CREATED" non falliranno qui?
Rich

4
Il bug report suggerisce di controllare httpConn.getResponseCode() >= 400(e la loro soluzione ha un errore, capovolgendo gli inputstream per usarli)
Giorno

14

È lo stesso problema che stavo avendo: HttpUrlConnectionrestituisce FileNotFoundExceptionse provi a leggere il getInputStream()dalla connessione.
Dovresti invece usarlo getErrorStream()quando il codice di stato è maggiore di 400.

Inoltre, fai attenzione poiché non è solo 200 ad essere il codice dello stato di successo, anche 201, 204, ecc. Sono spesso usati come stati di successo.

Ecco un esempio di come sono andato a gestirlo

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

In questo modo, sarai in grado di ottenere la risposta necessaria sia nei casi di successo che in quelli di errore.

Spero che questo ti aiuti!


13

In .Net hai la proprietà Response della WebException che dà accesso allo stream su un'eccezione. Quindi immagino che questo sia un buon modo per Java, ...

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

O un'implementazione che ho usato. (Potrebbe essere necessario apportare modifiche per la codifica o altre cose. Funziona nell'ambiente corrente.)

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

Questa dovrebbe essere la risposta accettata ... Mi chiedo perché le persone verificano ancora i numeri magici invece di gestire le eccezioni ...
SparK

Mi chiedo perché non sia una risposta accettata. Mi ha aiutato molto. Grazie!
Nikhil Jain

2

So che questo non risponde direttamente alla domanda, ma invece di utilizzare la libreria di connessione HTTP fornita da Sun, potresti voler dare un'occhiata a Commons HttpClient , che (a mio parere) ha un'API molto più semplice con cui lavorare.


4
Mi permetto di dissentire. L'API di Sun è molto più semplice, a patto che tu faccia le cose veramente semplici. Per cose semplici intendo solo un GET senza troppa gestione degli errori, che è sufficiente per un gran numero di casi. Ovviamente HttpClient è di gran lunga superiore in termini di funzionalità.
Michael Piefel

A partire dal 2014, il migliore potrebbe essere OkHttp (che in realtà restituisce istanze di HttpURLConnection all'apertura di un URL). Soprattutto su Android può aiutarti a evitare alcuni fastidiosi problemi sia del semplice HttpURLConnection che di Apache HttpClient.
Jonik

2

Prima controlla il codice di risposta e poi usa HttpURLConnection.getErrorStream()


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

4
Altri codici di successo come "201 CREATED" non falliranno qui?
Rich

Sì @Rich, ecco perché è meglio fare:if (httpConn.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) {
AO_

1

Il mio codice in esecuzione.

  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

Come leggere il corpo della risposta 404 in java:

Usa la libreria Apache - https://hc.apache.org/httpcomponents-client-4.5.x/httpclient/apidocs/

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

Lo snippet fornito di seguito utilizza 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());
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.