FileNotFoundException beim Abrufen des InputStream-Objekts von HttpURLConnection

109

Ich versuche, eine Post-Anfrage mit HttpURLConnection an eine URL zu senden (für die Verwendung von cUrl in Java). Der Inhalt der Anforderung ist XML. Am Endpunkt verarbeitet die Anwendung die XML-Datei, speichert einen Datensatz in der Datenbank und sendet dann eine Antwort in Form einer XML-Zeichenfolge zurück. Die App wird lokal auf Apache-Tomcat gehostet.

Wenn ich diesen Code vom Terminal aus ausführe, wird der Datenbank erwartungsgemäß eine Zeile hinzugefügt. Beim Abrufen des InputStream von der Verbindung wird jedoch eine Ausnahme wie folgt ausgelöst

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)

Hier ist der Code

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

Es ist verwirrend, weil die Ausnahme auf die Zeile zurückgeführt wird InputStream response = con.getInputStream(); und anscheinend keine Datei für eine FileNotFoundException beteiligt ist.

Wenn ich versuche, eine Verbindung zu einer XML-Datei direkt herzustellen, wird diese Ausnahme nicht ausgelöst.

Die Service-App verwendet Spring Framework und Jaxb2Marshaller, um die Antwort-XML zu erstellen.

Die Klasse ReadWriteTextFile wird von hier übernommen

Vielen Dank.

Bearbeiten: Nun, es speichert die Daten in der Datenbank und sendet gleichzeitig einen 404-Antwortstatuscode zurück.

Ich habe auch versucht, eine Locke mit PHP zu machen und die auszudrucken CURLINFO_HTTP_CODE die sich als 200 herausstellt.

Irgendwelche Ideen, wie ich das debuggen soll? Sowohl der Dienst als auch der Client befinden sich auf dem lokalen Server.

Behoben: Ich konnte das Problem lösen, nachdem ich mich auf eine Antwort bezogen hatte auf SO selbst verwiesen hatte.

Es scheint, dass HttpURLConnection immer eine 404-Antwort zurückgibt, wenn eine Verbindung zu einer URL mit einem nicht standardmäßigen Port hergestellt wird.

Das Hinzufügen dieser Zeilen löste das Problem

con.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
con.setRequestProperty("Accept","*/*");
Naiquevin
quelle
1
"Wenn ich diesen Code vom Terminal aus ausführe" - welcher Code? Es ist unklar, was funktioniert und was nicht.
Jon Skeet
HttpCurl ist der Name der Klasse, die diese Hauptmethode hat. Diese Klasse wird kompiliert und vom Terminal ausgeführt
Naiquevin
3
Ich hatte das gleiche Problem, aber keine der Lösungen hier hat funktioniert. Ich fand schließlich heraus, dass es ein Problem mit Java 1.7.0_05 war und aktualisierte auf die neueste Version 1.7.0_21 und das Problem verschwand. Ich habe auch festgestellt, dass das Problem in Java 1.6 nicht aufgetreten ist. Nur eine Information für jeden, der noch feststeckt.
Steven
Jungs! Siehe "Gelöst" Kommentar zu der Frage anstatt Antworten!
13.
Mögliches Duplikat von Read error response body in Java
Tony

Antworten:

130

Ich weiß nichts über Ihre Spring / JAXB-Kombination, aber der durchschnittliche REST-Webservice gibt keinen Antworttext für POST / PUT zurück, sondern nur einen Antwortstatus . Sie möchten es anstelle des Körpers bestimmen.

Ersetzen

InputStream response = con.getInputStream();

durch

int status = con.getResponseCode();

Alle verfügbaren Statuscodes und ihre Bedeutung sind in der zuvor verlinkten HTTP-Spezifikation verfügbar. Der Webservice selbst sollte auch eine Dokumentation enthalten, in der alle vom Webservice unterstützten Statuscodes und gegebenenfalls ihre besondere Bedeutung aufgeführt sind.

Wenn der Status mit 4nnoder beginnt 5nn, möchten Sie getErrorStream()stattdessen den Antworttext lesen, der möglicherweise die Fehlerdetails enthält.

InputStream error = con.getErrorStream();
BalusC
quelle
Ok, ich habe es versucht und es gibt den Status 404 zurück. Aber seltsamerweise spart es auch in der DB! Bedeutet dies auch, dass ein REST-Service nur einen Statuscode zurückgibt? Was ist, wenn ich weitere Informationen wie eine XML-Validierungsfehlermeldung oder eine URL zurückgeben möchte, wenn der Beitrag erfolgreich ist?
Naiquevin
Ja, bei Änderungsanforderungen wie POST / PUT / etc wird normalerweise kein Text zurückgegeben. Normalerweise möchten Sie den Antwortstatus ermitteln, bevor Sie den Eingabe- oder Fehlerstrom lesen. Ich habe der Antwort einige Details hinzugefügt. Aber wenn es wirklich den Status 404 zurückgibt, gibt es wahrscheinlich einen Fehler im Webservice. Ich würde seinem Betreuer Bericht erstatten.
BalusC
In diesem Fall bin ich der Betreuer des Dienstes! .. Nun, ich habe versucht, eine Locke mit PHP zu machen und es gibt 200 zurück (habe meine Frage bearbeitet) Auch versucht, getErrorStream()wie vorgeschlagen. Es wird eine NullPointerException ausgelöst new InputStreamReader(con.getErrorStream()).
Naiquevin
Entschuldigung, ich bin mit Spring-Webservices nicht vertraut. Ich habe nur praktische Erfahrungen mit JAX-WS / RS mit der Standard-Java EE 5/6-API. Ich würde vorschlagen, einen Haltepunkt für die Methode zu setzen, die für die Verarbeitung der XML-Datei verantwortlich ist, und dann weiter von dort zu gehen.
BalusC
Dieses Verhalten scheint sehr wenig nützlich zu sein, zumal es das Verweisen auf Dinge durch die allgemeinere URLConnectionproblematisch macht. Ich frage mich , warum die Java Jungs einfach nicht umsetzen getInputStream()in HttpUrlConnectionentlang der Linien return responseCode == 200 ? super.getInputStream() : this.getErrorStream(). Aber wie auch immer; informative Antwort, +1.
aroth
51

FileNotFound ist nur eine unglückliche Ausnahme, die angibt, dass der Webserver eine 404 zurückgegeben hat.

Jon Skeet
quelle
1
Das erklärt nicht, warum die Zeile wie vom OP angegeben zur DB hinzugefügt wird.
BalusC
@ BalusC: Es sei denn, der Server fügt eine Zeile hinzu und gibt dann eine 404 zurück.
Jon Skeet
Ja, es scheint sich so zu verhalten. Was heißt das ?
Naiquevin
@naiquevin: Es ist schwer zu sagen, aber da es sich um einen lokal ausgeführten Dienst handelt, sollten Sie ihn selbst debuggen können.
Jon Skeet
7
HttpURLConnection löst auch FileNotFoundException für 403 Antworten (und wahrscheinlich auch andere) aus. (Auch wenn der Antworttext nicht leer ist, scheint es.) Wie auch immer, untersuchen Sie immer, getResponseCode()bevor Sie anrufengetInputStream() .
Jonik
29

Für jeden, der dieses Problem in Zukunft hat, liegt der Grund darin, dass der Statuscode 404 (oder in meinem Fall 500) war. Es erscheint dieInpuStream Funktion einen Fehler auslöst, wenn der Statuscode nicht 200 ist.

In meinem Fall kontrolliere ich meinen eigenen Server und habe einen 500-Statuscode zurückgegeben, um anzuzeigen, dass ein Fehler aufgetreten ist. Obwohl ich auch einen Text mit einer String-Nachricht gesendet habe, die den Fehler detailliert beschreibt, ist derinputstream ausgelöst, unabhängig davon, ob der Body vollständig lesbar ist.

Wenn Sie Ihren Server steuern, können Sie dies vermutlich tun, indem Sie sich einen 200-Statuscode senden und dann die Antwort auf einen Zeichenfolgenfehler behandeln.

Terence Chow
quelle
21
Um klar zu sein, gehen Sie einfach falsch damit um. Sie sollten connection.getResponseCode verwenden, um zu überprüfen, ob es in Ordnung war. Verwenden Sie dann connection.getErrorStream, um den Fehlerkörper anstelle von getInputStream abzurufen. (oder Sie können getResponseMessage verwenden) Sie sollten keinen 200-Statuscode senden, wenn es sich um einen Fehler handelt. Verwenden Sie die http-Fehlercodes wie vorgesehen.
rekh127
5

Für alle anderen, die darüber stolpern, ist mir dasselbe passiert, als ich versucht habe, einen SOAP-Anforderungsheader an einen SOAP-Dienst zu senden. Das Problem war eine falsche Reihenfolge im Code. Ich habe zuerst den Eingabestream angefordert, bevor ich den XML-Text gesendet habe. In dem unten geschnippten Code kam die Zeile InputStream in = conn.getInputStream();unmittelbar danach, ByteArrayOutputStream out = new ByteArrayOutputStream();was die falsche Reihenfolge der Dinge ist.

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 diesem Fall war es eine unglückliche Möglichkeit, den HTTP-Antwortcode 400 zu codieren.

zero0cool
quelle
FileNotFound war, weil die Verbindung keinen Uput-Stream hat. Es hatte nichts mit der Codierung des Antwortcodes 400 zu tun. Sie können den Antwortcode mit getResponseCode () abrufen. Wenn es dann nicht HTTP_OK ist, sollten Sie den Body mit conn.getErrorStream () oder getResponseMessage ()
abrufen
new ByteArrayOutputStream()hat damit überhaupt nichts zu tun. Das Problem ist die Reihenfolge zwischen getInputStream()und getResponseCode().
Marquis von Lorne
4

FileNotFound bedeutet in diesem Fall, dass Sie einen 404 von Ihrem Server erhalten haben. Könnte es sein, dass der Server keine "POST" -Anfragen mag?

tofarr
quelle
Es wird jedoch ein Datensatz in der Datenbank gespeichert. Könnte Jaxb2Marshaller hier das Problem sein?
Naiquevin
2
Dies ist nicht nur bei 404s der Fall. Es passiert auch bei jeder Reaktion mit einem leeren Körper.
Dave Cameron
3
Diese Antwort ist leider falsch. Das FileNotFound in dieser Situation bedeutete nur, dass HttpURLConnection # getInputStream () aufgerufen wurde, wenn der Antwortcode über 399 lag, während in dieser Situation die HttpURLConnection # getErrorStream () aufgerufen werden sollte. OTOH, wenn der Server keine POSTs akzeptiert hätte, hätte er die 405-Methode nicht erlaubt zurückgegeben. Keine Beziehung zu FileNotFound, die dort geworfen wird.
Michal M
0

Die Lösung:
Ändern Sie einfach localhost für die IP Ihres PCs,
wenn Sie dies wissen möchten: Windows + r> cmd> ipconfig
Beispiel: http: // 192.168.0.107 /directory/service/program.php?action=sendSomething
ersetzen Sie einfach 192.168 .0.107 für Ihre eigene IP (versuchen Sie nicht 127.0.0.1, da es mit localhost identisch ist )

Charlie
quelle
-4

Bitte austauschen

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

Zu

con = (HttpURLConnection) new URL("http://YOUR_IP:8080/myapp/service/generate").openConnection();
Phuoc Huynh
quelle
2
Ändern Sie es warum? Das OP hat ausdrücklich angegeben, dass der Dienst lokal ausgeführt wird.
Marquis von Lorne
Falls Sie eine Bildressource von localhost benötigen, funktioniert dies nicht.
Phuoc Huynh