Leia o corpo da resposta de erro em Java

93

Em Java, este código lança uma exceção quando o resultado HTTP é o intervalo 404:

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

No meu caso, sei que o conteúdo é 404, mas mesmo assim gostaria de ler o corpo da resposta.

(No meu caso real, o código de resposta é 403, mas o corpo da resposta explica o motivo da rejeição e gostaria de exibi-lo ao usuário.)

Como posso acessar o corpo da resposta?

Dan Fabulich
fonte
Tem certeza de que o servidor está enviando um corpo?
Hank Gay,
2
@jdigital: a exceção lançada por HttpURLConnection.getInputStream () é java.io.FileNotFoundException. (Mencionando isso principalmente para melhor googlability.)
Jonik

Respostas:

172

Aqui está o relatório do bug (feche, não corrigirá, não é um bug).

O conselho deles é codificar assim:

HttpURLConnection httpConn = (HttpURLConnection)_urlConnection;
InputStream _is;
if (httpConn.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) {
    _is = httpConn.getInputStream();
} else {
     /* error from server */
    _is = httpConn.getErrorStream();
}
TofuBeer
fonte
5
Você não gostaria de obter o fluxo de erro quando o código de resposta for> = 400, em vez do contrário?
Stephen Swensen
3
Em caso de erros, getInputStream () lançará uma exceção IO. Você deve capturar a exceção e ler do fluxo de erro usando getErrorStream (). Esta parece ser uma abordagem melhor do que verificar o código httpresponse.
Sudarshan Bhat de
3
O problema é que se você ler o código HttpUrlConnection.getErrorStream (), verá que ele SEMPRE retorna nulo. (Java 6) :-(
Gangnus,
6
Outros códigos de sucesso como "201 CRIADO" não falharão aqui?
Rico de
4
O relatório de bug sugere verificação httpConn.getResponseCode() >= 400(e sua solução tem um erro, invertendo os fluxos de entrada para usar)
Dag
14

É o mesmo problema que eu estava tendo: HttpUrlConnectionretorna FileNotFoundExceptionse você tentar ler o getInputStream()da conexão.
Em vez disso, você deve usar getErrorStream()quando o código de status for superior a 400.

Mais do que isso, tenha cuidado, pois não é apenas 200 como o código de status de sucesso, mesmo 201, 204, etc. são frequentemente usados ​​como status de sucesso.

Aqui está um exemplo de como fui gerenciá-lo

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

Dessa forma, você poderá obter a resposta necessária em casos de sucesso e de erro.

Espero que isto ajude!

gpiazzese
fonte
13

Em .Net você tem a propriedade Response da WebException que dá acesso ao stream ON uma exceção. Então eu acho que esta é uma boa maneira de Java, ...

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

Ou uma implementação que usei. (Pode precisar de alterações de codificação ou outras coisas. Funciona no ambiente atual.)

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
fonte
Esta deve ser a resposta aceita ... Eu me pergunto por que as pessoas ainda verificam números mágicos em vez de lidar com exceções ...
SparK
Eu me pergunto por que esta não é uma resposta aceita. Me ajudou muito. Obrigado!
Nikhil Jain
2

Eu sei que isso não responde à pergunta diretamente, mas em vez de usar a biblioteca de conexão HTTP fornecida pela Sun, você pode dar uma olhada em Commons HttpClient , que (em minha opinião) tem uma API muito mais fácil de trabalhar.

matt b
fonte
4
Eu peço desculpa mas não concordo. A API da Sun é muito mais fácil, contanto que você faça as coisas realmente simples. Por coisas simples, quero dizer apenas um GET sem muito tratamento de erros, o que é suficiente para um grande número de casos. É claro que o HttpClient é muito superior em funcionalidade.
Michael Piefel
A partir de 2014, o melhor pode ser OkHttp (que na verdade retorna instâncias de HttpURLConnection ao abrir um URL). Especialmente no Android, pode ajudá-lo a evitar alguns problemas desagradáveis ​​de HttpURLConnection e Apache HttpClient.
Jonik
2

Primeiro verifique o código de resposta e depois use HttpURLConnection.getErrorStream()

Chris Dolan
fonte
1
InputStream is = null;
if (httpConn.getResponseCode() !=200) {
    is = httpConn.getErrorStream();
} else {
     /* error from server */
    is = httpConn.getInputStream();
}
sth
fonte
4
Outros códigos de sucesso como "201 CRIADO" não falharão aqui?
Rico de
Sim @Rich, é por isso que é melhor fazer:if (httpConn.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) {
AO_
1

Meu código em execução.

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

Como ler o corpo da resposta 404 em java:

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

O snippet fornecido abaixo 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
fonte