Leer el cuerpo de la respuesta de error en Java

93

En Java, este código genera una excepción cuando el resultado HTTP es el rango 404:

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

En mi caso, sé que el contenido es 404, pero de todos modos me gustaría leer el cuerpo de la respuesta.

(En mi caso real, el código de respuesta es 403, pero el cuerpo de la respuesta explica el motivo del rechazo y me gustaría mostrárselo al usuario).

¿Cómo puedo acceder al cuerpo de respuesta?

Dan Fabulich
fuente
¿Estás seguro de que el servidor está enviando un cuerpo?
Hank Gay
2
@jdigital: la excepción lanzada por HttpURLConnection.getInputStream () es java.io.FileNotFoundException. (Menciona principalmente esto para una mejor visibilidad en Google.)
Jonik

Respuestas:

172

Aquí está el informe de error (cerrado, no se solucionará, no es un error).

Su consejo es codificar así:

HttpURLConnection httpConn = (HttpURLConnection)_urlConnection;
InputStream _is;
if (httpConn.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) {
    _is = httpConn.getInputStream();
} else {
     /* error from server */
    _is = httpConn.getErrorStream();
}
Tofu, cerveza
fuente
5
¿No le gustaría obtener el flujo de error cuando el código de respuesta es> = 400, en lugar de al revés?
Stephen Swensen
3
En caso de errores, getInputStream () lanzará una excepción IO. Debería capturar la excepción y leer el flujo de errores usando getErrorStream (). Este parece ser un enfoque mejor que verificar el código httppresponse.
Sudarshan Bhat
3
El problema es que si lee el código HttpUrlConnection.getErrorStream (), verá que SIEMPRE devuelve nulo. (Java 6) :-(
Gangnus
6
¿No fallarán aquí otros códigos de éxito como "201 CREADO"?
Rich
4
El informe de error sugiere verificar httpConn.getResponseCode() >= 400(y su solución tiene un error, invirtiendo los flujos de entrada para usar)
Dag
14

Es el mismo problema que estaba teniendo: HttpUrlConnectionregresa FileNotFoundExceptionsi intenta leer getInputStream()desde la conexión.
En su lugar, debe usar getErrorStream()cuando el código de estado sea superior a 400.

Más que esto, tenga cuidado, ya que no solo 200 es el código de estado de éxito, incluso 201, 204, etc. se utilizan a menudo como estados de éxito.

Aquí tienes un ejemplo de cómo fui a gestionarlo.

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

De esta manera, podrá obtener la respuesta necesaria tanto en casos de éxito como de error.

¡Espero que esto ayude!

gpiazzese
fuente
13

En .Net, tiene la propiedad Response de WebException que le da acceso a la transmisión en una excepción. Así que supongo que esta es una buena forma para Java, ...

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

O una implementación que utilicé. (Es posible que necesite cambios para la codificación u otras cosas. Funciona en el entorno actual).

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
fuente
Esta debería ser la respuesta aceptada ... Me pregunto por qué la gente todavía verifica los números mágicos en lugar de manejar las excepciones ...
SparK
Me pregunto por qué esta no es una respuesta aceptada. Me ayudo mucho. ¡Gracias!
Nikhil Jain
2

Sé que esto no responde a la pregunta directamente, pero en lugar de usar la biblioteca de conexión HTTP proporcionada por Sun, es posible que desee echar un vistazo a Commons HttpClient , que (en mi opinión) tiene una API mucho más fácil de trabajar.

mate b
fuente
4
Siento disentir. La API de Sun es mucho más fácil, siempre que haga las cosas realmente simples. Por cosas simples me refiero a un GET sin demasiado manejo de errores, que es suficiente para una gran cantidad de casos. Por supuesto, HttpClient es muy superior en funcionalidad.
Michael Piefel
A partir de 2014, lo mejor podría ser OkHttp (que en realidad devuelve instancias de HttpURLConnection al abrir una URL). Especialmente en Android, puede ayudarlo a evitar algunos problemas desagradables tanto de HttpURLConnection simple como de Apache HttpClient.
Jonik
2

Primero verifique el código de respuesta y luego use HttpURLConnection.getErrorStream()

Chris Dolan
fuente
1
InputStream is = null;
if (httpConn.getResponseCode() !=200) {
    is = httpConn.getErrorStream();
} else {
     /* error from server */
    is = httpConn.getInputStream();
}
algo
fuente
4
¿No fallarán aquí otros códigos de éxito como "201 CREADO"?
Rich
Sí @Rich, por eso es mejor hacer:if (httpConn.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) {
AO_
1

Mi código de ejecución.

  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
fuente
0

Cómo leer el cuerpo de la respuesta 404 en java:

Use la biblioteca 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

El fragmento que se muestra a continuación usa 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
fuente