FileNotFoundException ao obter o objeto InputStream de HttpURLConnection

109

Estou tentando enviar uma solicitação de postagem para um url usando HttpURLConnection (para usar cUrl em java). O conteúdo da solicitação é xml e, no ponto final, o aplicativo processa o xml e armazena um registro no banco de dados e, em seguida, envia de volta uma resposta na forma de string xml. O aplicativo está hospedado no apache-tomcat localmente.

Quando executo este código no terminal, uma linha é adicionada ao banco de dados conforme o esperado. Mas uma exceção é lançada da seguinte maneira ao obter o InputStream da conexão

java.io.FileNotFoundException: http://localhost:8080/myapp/service/generate
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1401)
    at org.kodeplay.helloworld.HttpCurl.main(HttpCurl.java:30)

Aqui está o código

public class HttpCurl {
    public static void main(String [] args) {

        HttpURLConnection con;

        try {
            con = (HttpURLConnection) new URL("http://localhost:8080/myapp/service/generate").openConnection();
            con.setRequestMethod("POST");
            con.setDoOutput(true);
            con.setDoInput(true);

            File xmlFile = new File("test.xml");

            String xml = ReadWriteTextFile.getContents(xmlFile);                

            con.getOutputStream().write(xml.getBytes("UTF-8"));
            InputStream response = con.getInputStream();

            BufferedReader reader = new BufferedReader(new InputStreamReader(response));
            for (String line ; (line = reader.readLine()) != null;) {
                System.out.println(line);
            }
            reader.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  }

É confuso porque a exceção é rastreada até a linha InputStream response = con.getInputStream();e não parece haver nenhum arquivo envolvido para uma FileNotFoundException.

Quando tento abrir uma conexão com um arquivo xml diretamente, ele não lança essa exceção.

O aplicativo de serviço usa o framework spring e Jaxb2Marshaller para criar o xml de resposta.

A classe ReadWriteTextFile é tirada daqui

Obrigado.

Edit: Bem, ele salva os dados no banco de dados e envia de volta um código de status de resposta 404 ao mesmo tempo.

Eu também tentei fazer um curl usando php e imprimir o CURLINFO_HTTP_CODEque acabou sendo 200.

Alguma ideia de como faço para depurar isso? O serviço e o cliente estão no servidor local.

Resolvido: eu poderia resolver o problema após consultar uma resposta no próprio SO.

Parece que HttpURLConnection sempre retorna a resposta 404 ao se conectar a um url com uma porta não padrão.

Adicionar essas linhas resolveu

con.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
con.setRequestProperty("Accept","*/*");
Naiquevin
fonte
1
"Quando executo este código a partir do terminal" - qual código? Não está claro o que está funcionando versus o que não está funcionando.
Jon Skeet,
HttpCurl é o nome da classe que possui este método principal. Esta aula é compilada e executada a partir do terminal
naiquevin,
3
Eu tive o mesmo problema, mas nenhuma das soluções aqui funcionou. Acabei descobrindo que era um problema com o Java 1.7.0_05 e atualizei para a versão mais recente 1.7.0_21 e o problema foi embora. Também percebi que o problema não ocorria no Java 1.6. Apenas um FYI para quem ainda está preso.
Steven
Rapazes! veja o comentário "Resolvido" na questão em vez das respostas!
김준호
Possível duplicata do corpo de resposta do erro
Tony

Respostas:

130

Não sei sobre sua combinação Spring / JAXB, mas o serviço da web REST médio não retornará um corpo de resposta no POST / PUT, apenas um status de resposta . Você gostaria de determiná-lo em vez do corpo.

Substituir

InputStream response = con.getInputStream();

de

int status = con.getResponseCode();

Todos os códigos de status disponíveis e seus significados estão disponíveis na especificação HTTP, conforme linkado anteriormente. O próprio serviço da web também deve vir junto com alguma documentação que apresenta uma visão geral de todos os códigos de status suportados pelo serviço da web e seu significado especial, se houver.

Se o status começar com 4nnou 5nn, você gostaria de usar getErrorStream()para ler o corpo da resposta, que pode conter os detalhes do erro.

InputStream error = con.getErrorStream();
BalusC
fonte
Ok, tentei fazer isso e retorna o status 404. Mas estranhamente, também está salvando no DB! Além disso, pelo que você mencionou, isso significa que qualquer serviço REST retornará apenas um código de status? E se eu quiser retornar mais informações, como uma mensagem de erro de validação xml ou um url, se a postagem for bem-sucedida?
naiquevin,
Sim, em solicitações de modificação como POST / PUT / etc, geralmente não retornará um corpo. Normalmente, você gostaria de determinar o status da resposta antes de ler a entrada ou o fluxo de erro. Eu adicionei alguns detalhes à resposta. Mas se ele realmente está retornando o status 404, provavelmente há algum bug no serviço da web. Eu reportaria ao seu mantenedor.
BalusC
Neste caso, o mantenedor do serviço sou eu! .. Bem, eu tentei fazer um curl usando php e ele retorna 200 (editei minha pergunta) Também tentei getErrorStream()como sugerido. Ele lança uma NullPointerException new InputStreamReader(con.getErrorStream()).
naiquevin,
Desculpe, não estou familiarizado com os webservices Spring. Tenho apenas experiência prática com JAX-WS / RS da API Java EE 5/6 padrão. Eu sugiro colocar um ponto de interrupção no método responsável pelo processamento do arquivo XML e, em seguida, dar um passo adiante a partir daí.
BalusC de
Esse comportamento parece muito inútil, principalmente porque torna a referência às coisas uma URLConnectionproblemática mais genérica . Eu me pergunto por que os caras do Java não implementaram simplesmente getInputStream()ao HttpUrlConnectionlongo das linhas de return responseCode == 200 ? super.getInputStream() : this.getErrorStream(). Mas de qualquer maneira; resposta informativa, +1.
aroth
51

FileNotFound é apenas uma exceção infeliz usada para indicar que o servidor da web retornou um 404.

Jon Skeet
fonte
1
Isso não explica por que a linha é adicionada ao banco de dados conforme declarado pelo OP.
BalusC de
@BalusC: A menos que o servidor está adicionando uma linha e , em seguida, retornar um 404.
Jon Skeet
sim, parece estar se comportando dessa maneira. O que isso significa ?
naiquevin,
@naiquevin: É difícil dizer, mas como é um serviço executado localmente, você deve ser capaz de depurá-lo sozinho.
Jon Skeet,
7
HttpURLConnection também lança FileNotFoundException para 403 respostas (e provavelmente outras). (Mesmo que o corpo de resposta não esteja vazio, ao que parece.) De qualquer forma, sempre investigue getResponseCode()antes de ligar getInputStream().
Jonik
29

Para qualquer pessoa com esse problema no futuro, o motivo é porque o código de status era 404 (ou no meu caso era 500). Parece que a InpuStreamfunção gerará um erro quando o código de status não for 200.

No meu caso, eu controlo meu próprio servidor e estava retornando um código de status 500 para indicar que ocorreu um erro. Apesar de eu também enviar um corpo com uma mensagem em string detalhando o erro, o inputstreamlançou um erro independentemente de o corpo ser completamente legível.

Se você controlar seu servidor, suponho que isso possa ser resolvido enviando a você mesmo um código de status 200 e, em seguida, tratando de qualquer resposta de erro de string.

Terence Chow
fonte
21
Para ser claro, você está apenas lidando com isso da maneira errada. Você deve usar connection.getResponseCode para verificar se estava tudo bem. Em seguida, use connection.getErrorStream para obter o corpo do erro em vez de getInputStream. (ou você pode usar getResponseMessage). Você não deve enviar um código de status 200 se for um erro, use os códigos de erro http conforme pretendido.
rekh127
5

Para qualquer outra pessoa que tenha problemas com isso, o mesmo aconteceu comigo enquanto tentava enviar um cabeçalho de solicitação SOAP para um serviço SOAP. O problema era uma ordem errada no código, solicitei o fluxo de entrada antes de enviar o corpo XML. No código cortado abaixo, a linha InputStream in = conn.getInputStream();veio imediatamente após a ByteArrayOutputStream out = new ByteArrayOutputStream();qual está a ordem incorreta das coisas.

ByteArrayOutputStream out = new ByteArrayOutputStream();
// send SOAP request as part of HTTP body 
byte[] data = request.getHttpBody().getBytes("UTF-8");
conn.getOutputStream().write(data); 

if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
  Log.d(TAG, "http response code is " + conn.getResponseCode());
  return null;
}

InputStream in = conn.getInputStream();

FileNotFound neste caso, foi uma forma infeliz de codificar o código de resposta HTTP 400.

zero0cool
fonte
FileNotFound foi porque a conexão não tem um fluxo uput. Não teve nada a ver com a codificação do código de resposta 400. Você pode obter o código de resposta com getResponseCode (). Então, se não for HTTP_OK, você deve obter o corpo com conn.getErrorStream () ou getResponseMessage ()
rekh127
new ByteArrayOutputStream()não tem nada a ver com isso. O problema é a ordem entre getInputStream()e getResponseCode().
Marquês de Lorne
4

FileNotFound, neste caso, significa que você obteve um 404 do seu servidor - será que o servidor não gosta de solicitações "POST"?

tofarr
fonte
Mas ele salva um registro no banco de dados. Jaxb2Marshaller poderia ser o problema aqui?
naiquevin,
2
Isso não acontece apenas com 404s. Isso também acontece para qualquer resposta com um corpo vazio.
Dave Cameron
3
Essa resposta está, infelizmente, errada. O FileNotFound nesta situação significava apenas que HttpURLConnection # getInputStream () foi chamado quando o código de resposta estava acima de 399, enquanto nesta situação o HttpURLConnection # getErrorStream () deveria ter sido chamado. OTOH, se o servidor não aceitasse POSTs, ele retornaria 405 Método não permitido. Nenhuma relação com FileNotFound sendo jogado lá.
Michal M
0

A solução:
basta alterar localhost para o IP do seu PC
, se você quiser saber o seguinte: Windows + r> cmd> ipconfig
exemplo: http: // 192.168.0.107 /directory/service/program.php?action=sendSomething
apenas substituir 192.168 .0.107 para seu próprio IP (não tente 127.0.0.1 porque é o mesmo que localhost )

Charlie
fonte
-4

Por favor, mude

con = (HttpURLConnection) new URL("http://localhost:8080/myapp/service/generate").openConnection();

Para

con = (HttpURLConnection) new URL("http://YOUR_IP:8080/myapp/service/generate").openConnection();
Phuoc Huynh
fonte
2
Mudar por quê? O OP declarou especificamente que o serviço está sendo executado localmente.
Marquês de Lorne
Caso você precise obter o recurso de imagem do localhost, ele não funciona.
Phuoc Huynh de