Lire le corps de la réponse d'erreur en Java

93

En Java, ce code lève une exception lorsque le résultat HTTP est la plage 404:

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

Dans mon cas, je sais que le contenu est 404, mais j'aimerais quand même lire le corps de la réponse.

(Dans mon cas réel, le code de réponse est 403, mais le corps de la réponse explique la raison du rejet, et j'aimerais l'afficher à l'utilisateur.)

Comment puis-je accéder au corps de la réponse?

Dan Fabulich
la source
Êtes-vous sûr que le serveur envoie un corps?
Hank Gay
2
@jdigital: l'exception lancée par HttpURLConnection.getInputStream () est java.io.FileNotFoundException. (En mentionnant principalement cela pour une meilleure googlability.)
Jonik

Réponses:

172

Voici le rapport de bogue (fermé, ne corrigera pas, pas un bogue).

Leur conseil est de coder comme ceci:

HttpURLConnection httpConn = (HttpURLConnection)_urlConnection;
InputStream _is;
if (httpConn.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) {
    _is = httpConn.getInputStream();
} else {
     /* error from server */
    _is = httpConn.getErrorStream();
}
TofuBière
la source
5
Ne voudriez-vous pas obtenir le flux d'erreur lorsque le code de réponse est> = 400, plutôt que l'inverse?
Stephen Swensen
3
En cas d'erreur, getInputStream () lèvera une exception IO. Vous devez intercepter l'exception et lire à partir du flux d'erreur en utilisant getErrorStream (). Cela semble être une meilleure approche que la vérification du code httpresponse.
Sudarshan Bhat
3
Le problème est que si vous lisez le code HttpUrlConnection.getErrorStream (), vous verrez qu'il renvoie TOUJOURS null. (Java 6) :-(
Gangnus
6
Les autres codes de succès comme "201 CREATED" n'échoueront-ils pas ici?
Riche
4
Le rapport de bogue suggère de vérifier httpConn.getResponseCode() >= 400(et leur contournement a une erreur, retournant les flux d'entrée à utiliser)
Dag
14

C'est le même problème que j'avais: HttpUrlConnectionretourne FileNotFoundExceptionsi vous essayez de lire le getInputStream()depuis la connexion.
Vous devriez plutôt l'utiliser getErrorStream()lorsque le code d'état est supérieur à 400.

De plus, soyez prudent car ce n'est pas seulement 200 pour être le code de statut de réussite, même 201, 204, etc. sont souvent utilisés comme statuts de réussite.

Voici un exemple de la façon dont je suis allé le gérer

... 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 cette façon, vous serez en mesure d'obtenir la réponse nécessaire dans les cas de réussite et d'erreur.

J'espère que cela t'aides!

gpiazzese
la source
13

Dans .Net, vous avez la propriété Response de WebException qui donne accès au flux sur une exception. Donc je suppose que c'est un bon moyen pour Java, ...

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

Ou une implémentation que j'ai utilisée. (Peut nécessiter des modifications pour l'encodage ou d'autres choses. Fonctionne dans l'environnement actuel.)

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
la source
Cela devrait être la réponse acceptée ... Je me demande pourquoi les gens vérifient toujours les nombres magiques au lieu de gérer les exceptions ...
SparK
Je me demande pourquoi ce n'est pas une réponse acceptée. M'a beaucoup aidé. Merci!
Nikhil Jain
2

Je sais que cela ne répond pas directement à la question, mais au lieu d'utiliser la bibliothèque de connexion HTTP fournie par Sun, vous voudrez peut-être jeter un coup d'œil à Commons HttpClient , qui (à mon avis) a une API beaucoup plus facile à utiliser .

mat b
la source
4
Je ne suis pas d'accord. L'API de Sun est beaucoup plus simple, tant que vous faites les choses vraiment simples. Par truc simple, j'entends juste un GET sans trop de gestion des erreurs, ce qui est suffisant pour un grand nombre de cas. Bien sûr, HttpClient est bien supérieur en fonctionnalités.
Michael Piefel
À partir de 2014, le meilleur pourrait être OkHttp (qui renvoie en fait des instances HttpURLConnection lors de l'ouverture d'une URL). Surtout sur Android, cela peut vous aider à éviter certains problèmes désagréables à la fois avec HttpURLConnection et Apache HttpClient.
Jonik
2

Vérifiez d'abord le code de réponse, puis utilisez HttpURLConnection.getErrorStream()

Chris Dolan
la source
1
InputStream is = null;
if (httpConn.getResponseCode() !=200) {
    is = httpConn.getErrorStream();
} else {
     /* error from server */
    is = httpConn.getInputStream();
}
qc
la source
4
Les autres codes de succès comme "201 CREATED" n'échoueront-ils pas ici?
Riche
Oui @ Rich, c'est pourquoi il vaut mieux faire:if (httpConn.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) {
AO_
1

Mon code courant.

  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
la source
0

Comment lire le corps de la réponse 404 en java:

Utilisez la bibliothèque Apache - https://hc.apache.org/httpcomponents-client-4.5.x/httpclient/apidocs/

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

L'extrait de code ci-dessous utilise 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
la source