FileNotFoundException durante il recupero dell'oggetto InputStream da HttpURLConnection


109

Sto cercando di inviare una richiesta di post a un URL utilizzando HttpURLConnection (per l'utilizzo di cUrl in java). Il contenuto della richiesta è xml e al punto finale, l'applicazione elabora l'xml e memorizza un record nel database, quindi invia una risposta sotto forma di stringa xml. L'app è ospitata localmente su apache-tomcat.

Quando eseguo questo codice dal terminale, una riga viene aggiunta al database come previsto. Ma viene generata un'eccezione come segue durante il recupero di InputStream dalla connessione

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)

Ecco il codice

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

È fonte di confusione perché l'eccezione viene tracciata sulla riga InputStream response = con.getInputStream();e non sembra esserci alcun file coinvolto per un'eccezione FileNotFoundException.

Quando provo ad aprire direttamente una connessione a un file xml, non genera questa eccezione.

L'app di servizio utilizza il framework spring e Jaxb2Marshaller per creare l'xml di risposta.

La classe ReadWriteTextFile è presa da qui

Grazie.

Modifica: salva i dati nel DB e restituisce un codice di stato della risposta 404 allo stesso tempo.

Ho anche provato a fare un curl usando php e ho stampato il CURLINFO_HTTP_CODEche risulta essere 200.

Qualche idea su come posso eseguire il debug di questo? Sia il servizio che il client si trovano sul server locale.

Risolto: potrei risolvere il problema dopo aver fatto riferimento a una risposta su SO stesso.

Sembra che HttpURLConnection restituisca sempre la risposta 404 quando ci si connette a un URL con una porta non standard.

L'aggiunta di queste righe lo ha risolto

con.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
con.setRequestProperty("Accept","*/*");

1
"Quando eseguo questo codice dal terminale" - quale codice? Non è chiaro cosa funziona e cosa non funziona.
Jon Skeet

HttpCurl è il nome della classe che dispone di questo metodo principale. Questa classe è compilata ed eseguita dal terminale
naiquevin

3
Ho riscontrato lo stesso problema, ma nessuna delle soluzioni qui ha funzionato. Alla fine ho capito che si trattava di un problema con Java 1.7.0_05 e aggiornato all'ultima versione 1.7.0_21 e il problema è andato via. Mi sono anche reso conto che il problema non si verificava in Java 1.6. Solo un FYI per chiunque sia ancora bloccato.
Steven

Ragazzi! vedere il commento "Risolto" sulla domanda piuttosto che sulle risposte!
김준호

Possibile duplicato del corpo della risposta all'errore
Tony

Risposte:


130

Non so della tua combinazione Spring / JAXB, ma il servizio web REST medio non restituirà un corpo di risposta su POST / PUT, solo uno stato di risposta . Vorresti determinarlo invece del corpo.

Sostituire

InputStream response = con.getInputStream();

di

int status = con.getResponseCode();

Tutti i codici di stato disponibili e il loro significato sono disponibili nelle specifiche HTTP, come collegato in precedenza. Il webservice stesso dovrebbe anche essere accompagnato da una documentazione che riassume tutti i codici di stato supportati dal webservice e il loro significato speciale, se presente.

Se lo stato inizia con 4nno 5nn, vorresti usarlo getErrorStream()invece per leggere il corpo della risposta che potrebbe contenere i dettagli dell'errore.

InputStream error = con.getErrorStream();

Ok ho provato questo e restituisce lo stato 404. Ma stranamente salva anche in DB! Inoltre da quello che dici, significa che qualsiasi servizio REST restituirà solo un codice di stato? Cosa succede se voglio restituire ulteriori informazioni come un messaggio di errore di convalida xml o un URL se il post ha esito positivo?
naiquevin

Sì, su richieste di modifica come POST / PUT / ecc di solito non restituisce un corpo. Di solito si desidera determinare lo stato della risposta prima di leggere il flusso di input o di errore. Ho aggiunto qualche dettaglio alla risposta. Ma se restituisce davvero lo stato 404, probabilmente c'è qualche bug nel servizio web. Riferirei al suo manutentore.
BalusC

In questo caso il manutentore del servizio sembra essere io! .. Beh, ho provato a fare un curl usando php e restituisce 200 (ho modificato la mia domanda) Ho provato anche getErrorStream()come suggerito. Genera un'eccezione NullPointerException new InputStreamReader(con.getErrorStream()).
naiquevin

Mi spiace, non sono a conoscenza dei servizi web di primavera. Ho solo esperienza pratica con JAX-WS / RS dall'API Java EE 5/6 standard. Suggerirei di inserire un punto di interruzione sul metodo responsabile dell'elaborazione del file XML e quindi di procedere oltre.
BalusC

Questo comportamento sembra molto inutile, soprattutto perché rende problematico il riferimento alle cose da parte dei più generici URLConnection. Mi chiedo perché i ragazzi di Java non abbiano semplicemente implementato getInputStream()in HttpUrlConnectionlungo le linee di return responseCode == 200 ? super.getInputStream() : this.getErrorStream(). Ma comunque; risposta informativa, +1.
aroth

51

FileNotFound è solo una sfortunata eccezione utilizzata per indicare che il server web ha restituito un 404.


1
Ciò non spiega perché la riga viene aggiunta al DB come dichiarato dall'OP.
BalusC

@BalusC: a meno che il server non aggiunga una riga e poi restituisca un 404.
Jon Skeet

sì, sembra che si stia comportando in questo modo. Cosa significa ?
naiquevin

@naiquevin: è difficile da dire, ma dato che si tratta di un servizio in esecuzione localmente, dovresti essere in grado di eseguirne il debug da solo.
Jon Skeet

7
HttpURLConnection genera anche FileNotFoundException per 403 risposte (e probabilmente altre). (Anche se il corpo della risposta non è vuoto, sembra.) In ogni caso, indagare sempre getResponseCode()prima di chiamare getInputStream().
Jonik

29

Per chiunque abbia questo problema in futuro, il motivo è perché il codice di stato era un 404 (o nel mio caso era un 500). Sembra che la InpuStreamfunzione genererà un errore quando il codice di stato non è 200.

Nel mio caso controllo il mio server e stavo restituendo un codice di stato 500 per indicare che si è verificato un errore. Nonostante avessi anche inviato un corpo con un messaggio di stringa che dettagliava l'errore, inputstreamha generato un errore indipendentemente dal fatto che il corpo fosse completamente leggibile.

Se controlli il tuo server, suppongo che questo possa essere gestito inviandoti un codice di stato 200 e quindi gestendo qualunque sia stata la risposta di errore della stringa.


21
Per essere chiari, stai solo gestendolo in modo sbagliato. Dovresti usare connection.getResponseCode per verificare se andava bene. Quindi utilizzare connection.getErrorStream per ottenere il corpo dell'errore invece di getInputStream. (oppure puoi usare getResponseMessage) Non dovresti inviare un codice di stato 200 se si tratta di un errore, usa i codici di errore http come previsto.
rekh127

5

Per chiunque altro si imbattesse in questo, lo stesso è accaduto a me mentre cercavo di inviare un'intestazione di richiesta SOAP a un servizio SOAP. Il problema era un ordine sbagliato nel codice, ho richiesto il flusso di input prima di inviare il corpo XML. Nel codice ritagliato di seguito, la riga è InputStream in = conn.getInputStream();arrivata immediatamente dopo la ByteArrayOutputStream out = new ByteArrayOutputStream();quale è l'ordine errato delle cose.

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 in questo caso è stato un modo sfortunato per codificare il codice di risposta HTTP 400.


FileNotFound era perché la connessione non ha un flusso uput. Non aveva nulla a che fare con la codifica del codice di risposta 400. Puoi ottenere il responsecode con getResponseCode (). Quindi se non è HTTP_OK dovresti ottenere il corpo con conn.getErrorStream () o getResponseMessage ()
rekh127

new ByteArrayOutputStream()non ha nulla a che fare con tutto ciò. Il problema è l'ordine tra getInputStream()e getResponseCode().
Marchese di Lorne

4

FileNotFound in questo caso significa che hai ricevuto un 404 dal tuo server - potrebbe essere che al server non piacciono le richieste "POST"?


Ma salva un record nel db. Jaxb2Marshaller potrebbe essere il problema qui?
naiquevin

2
Questo non accade solo per i 404. Succede anche per qualsiasi risposta con un corpo vuoto.
Dave Cameron

3
Questa risposta è, purtroppo, sbagliata. FileNotFound in questa situazione significava solo che HttpURLConnection # getInputStream () veniva chiamato quando il codice di risposta era superiore a 399, mentre in questa situazione avrebbe dovuto essere chiamato HttpURLConnection # getErrorStream (). OTOH, se il server non avesse accettato i POST, avrebbe restituito 405 Metodo non consentito. Nessuna relazione con FileNotFound generata lì.
Michal M

0

La soluzione:
basta cambiare localhost per l' IP del tuo PC
se vuoi sapere questo: Windows + r> cmd> ipconfig
example: http: // 192.168.0.107 /directory/service/program.php?action=sendQualcosa
basta sostituire 192.168 .0.107 per il tuo IP (non provare 127.0.0.1 perché è uguale a localhost )


-4

Per favore cambia

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

Per

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

2
Cambiarlo perché? L'OP ha espressamente dichiarato che il servizio è in esecuzione localmente.
Marchese di Lorne

Nel caso in cui sia necessario ottenere la risorsa immagine da localhost, non funziona.
Phuoc Huynh
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.