FileNotFoundException lors de l'obtention de l'objet InputStream à partir de HttpURLConnection

109

J'essaie d'envoyer une demande de publication à une URL en utilisant HttpURLConnection (pour utiliser cUrl en java). Le contenu de la demande est xml et au point final, l'application traite le xml et stocke un enregistrement dans la base de données, puis renvoie une réponse sous forme de chaîne xml. L'application est hébergée localement sur apache-tomcat.

Lorsque j'exécute ce code à partir du terminal, une ligne est ajoutée à la base de données comme prévu. Mais une exception est levée comme suit lors de l'obtention du InputStream à partir de la connexion

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)

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

C'est déroutant car l'exception est tracée jusqu'à la ligne InputStream response = con.getInputStream();et il ne semble pas y avoir de fichier impliqué pour une FileNotFoundException.

Lorsque j'essaye d'ouvrir directement une connexion à un fichier xml, cela ne lève pas cette exception.

L'application de service utilise Spring Framework et Jaxb2Marshaller pour créer la réponse xml.

La classe ReadWriteTextFile est tirée d' ici

Merci.

Edit: Eh bien, il enregistre les données dans le DB et renvoie un code d'état de réponse 404 en même temps.

J'ai également essayé de faire une boucle en utilisant php et d'imprimer le CURLINFO_HTTP_CODEqui s'avère être 200.

Des idées sur la façon de déboguer cela? Le service et le client se trouvent sur le serveur local.

Résolu: je pourrais résoudre le problème après avoir fait référence à une réponse sur SO lui-même.

Il semble que HttpURLConnection renvoie toujours une réponse 404 lors de la connexion à une URL avec un port non standard.

L'ajout de ces lignes l'a résolu

con.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
con.setRequestProperty("Accept","*/*");
naiquevin
la source
1
"Quand j'exécute ce code depuis le terminal" - quel code? On ne sait pas ce qui fonctionne et ce qui ne fonctionne pas.
Jon Skeet
HttpCurl est le nom de la classe qui a cette méthode principale. Cette classe est compilée et exécutée à partir du terminal
naiquevin
3
J'ai rencontré le même problème, mais aucune des solutions proposées ici n'a fonctionné. J'ai finalement compris que c'était un problème avec Java 1.7.0_05 et mis à jour avec la dernière version 1.7.0_21 et le problème a disparu. J'ai également réalisé que le problème ne se produisait pas dans Java 1.6. Juste un FYI pour quiconque est encore coincé.
Steven
Les mecs! voir le commentaire "Résolu" sur la question plutôt que les réponses!
김준호
Copie possible du corps de la réponse d'erreur
Tony

Réponses:

130

Je ne connais pas votre combinaison Spring / JAXB, mais le service Web REST moyen ne retournera pas de corps de réponse sur POST / PUT, juste un état de réponse . Vous aimeriez le déterminer au lieu du corps.

Remplacer

InputStream response = con.getInputStream();

par

int status = con.getResponseCode();

Tous les codes d'état disponibles et leur signification sont disponibles dans la spécification HTTP, comme précédemment lié. Le service Web lui-même devrait également être accompagné d'une documentation qui présente tous les codes d'état pris en charge par le service Web et leur signification particulière, le cas échéant.

Si l'état commence par 4nnou 5nn, vous souhaitez l'utiliser à la getErrorStream()place pour lire le corps de la réponse qui peut contenir les détails de l'erreur.

InputStream error = con.getErrorStream();
BalusC
la source
Ok j'ai essayé ça et ça retourne le statut 404. Mais étrangement, ça sauve aussi en DB! Également d'après ce que vous mentionnez, cela signifie-t-il qu'un service REST ne renverra qu'un code d'état? Que faire si je souhaite renvoyer plus d'informations telles qu'un message d'erreur de validation XML ou une URL si la publication est réussie?
naiquevin
Oui, sur les demandes de modification comme POST / PUT / etc, il ne renvoie généralement pas de corps. Vous souhaitez généralement déterminer l'état de la réponse avant de lire le flux d'entrée ou d'erreur. J'ai ajouté quelques détails à la réponse. Mais s'il renvoie vraiment le statut 404, il y a probablement un bogue dans le service Web. Je ferais rapport à son responsable.
BalusC
Dans ce cas, le responsable du service se trouve être moi! .. Eh bien, j'ai essayé de faire un curl en utilisant php et il renvoie 200 (j'ai édité ma question) Également essayé getErrorStream()comme suggéré. Il lève une NullPointerException sur new InputStreamReader(con.getErrorStream()).
naiquevin
Désolé, je ne suis pas familier avec les services Web Spring. Je n'ai qu'une expérience pratique avec JAX-WS / RS de l'API Java EE 5/6 standard. Je suggérerais de mettre un point d'arrêt sur la méthode responsable du traitement du fichier XML, puis d'aller plus loin à partir de là.
BalusC
Ce comportement semble très peu utile, d'autant plus qu'il rend la référence aux choses par le plus générique URLConnectionproblématique. Je me demande pourquoi les gars de Java n'ont pas simplement mis getInputStream()en œuvre dans HttpUrlConnectionle sens de return responseCode == 200 ? super.getInputStream() : this.getErrorStream(). Mais de toute façon; réponse informative, +1.
aroth le
51

FileNotFound est juste une exception malheureuse utilisée pour indiquer que le serveur Web a renvoyé un 404.

Jon Skeet
la source
1
Cela n'explique pas pourquoi la ligne est ajoutée à DB comme indiqué par l'OP.
BalusC
@BalusC: Sauf si le serveur ajoute une ligne puis renvoie un 404.
Jon Skeet
oui, il semble se comporter de cette manière. Qu'est-ce que ça veut dire ?
naiquevin
@naiquevin: C'est difficile à dire, mais étant donné que c'est un service fonctionnant localement, vous devriez pouvoir le déboguer vous-même.
Jon Skeet
7
HttpURLConnection lance également FileNotFoundException pour 403 réponses (et probablement d'autres). (Même si le corps de la réponse n'est pas vide, semble-t-il.) Quoi qu'il en soit, examinez toujours getResponseCode()avant d'appeler getInputStream().
Jonik
29

Pour toute personne ayant ce problème à l'avenir, la raison en est que le code d'état était un 404 (ou dans mon cas était un 500). Il semble que la InpuStreamfonction génère une erreur lorsque le code d'état n'est pas 200.

Dans mon cas, je contrôle mon propre serveur et je renvoie un code d'état 500 pour indiquer qu'une erreur s'est produite. Bien que j'envoie également un corps avec un message de chaîne détaillant l'erreur, le message a généré inputstreamune erreur indépendamment du fait que le corps soit complètement lisible.

Si vous contrôlez votre serveur, je suppose que cela peut être géré en vous envoyant un code d'état 200, puis en gérant quelle que soit la réponse d'erreur de chaîne.

Terence Chow
la source
21
Pour être clair, vous le gérez mal. Vous devez utiliser connection.getResponseCode pour vérifier si tout va bien. Utilisez ensuite connection.getErrorStream pour obtenir le corps de l'erreur au lieu de getInputStream. (ou vous pouvez utiliser getResponseMessage) Vous ne devriez pas envoyer un code d'état 200 s'il s'agissait d'une erreur, utilisez les codes d'erreur http comme prévu.
rekh127
5

Pour quiconque a trébuché là-dessus, la même chose m'est arrivée en essayant d'envoyer un en-tête de requête SOAP à un service SOAP. Le problème était un mauvais ordre dans le code, j'ai d'abord demandé le flux d'entrée avant d'envoyer le corps XML. Dans le code extrait ci-dessous, la ligne InputStream in = conn.getInputStream();est venue immédiatement après ByteArrayOutputStream out = new ByteArrayOutputStream();ce qui est le mauvais ordre des choses.

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 dans ce cas, c'était une manière malheureuse d'encoder le code de réponse HTTP 400.

zero0cool
la source
FileNotFound était dû au fait que la connexion n'a pas de flux amont. Cela n'avait rien à voir avec l'encodage du code de réponse 400. Vous pouvez obtenir le responsecode avec getResponseCode (). Ensuite, si ce n'est pas HTTP_OK, vous devriez obtenir le corps avec conn.getErrorStream () ou getResponseMessage ()
rekh127
new ByteArrayOutputStream()n'a absolument rien à voir avec cela. Le problème est l'ordre entre getInputStream()et getResponseCode().
Marquis of Lorne
4

FileNotFound dans ce cas signifie que vous avez obtenu un 404 de votre serveur - est-ce que le serveur n'aime pas les requêtes "POST"?

tofarr
la source
Mais il enregistre un enregistrement dans la base de données. Jaxb2Marshaller pourrait-il être le problème ici?
naiquevin
2
Cela ne se produit pas seulement pour les 404. Cela se produit également pour toute réponse avec un corps vide.
Dave Cameron
3
Cette réponse est malheureusement fausse. Le FileNotFound dans cette situation signifiait seulement que HttpURLConnection # getInputStream () était appelé lorsque le code de réponse était supérieur à 399, alors que dans cette situation, HttpURLConnection # getErrorStream () aurait dû être appelé. OTOH, si le serveur n'acceptait pas les POST, il aurait renvoyé la méthode 405 non autorisée. Aucune relation avec FileNotFound qui y est jeté.
Michal M
0

La solution:
changez simplement localhost pour l' IP de votre PC
si vous voulez savoir ceci: Windows + r> cmd>
exemple ipconfig : http: // 192.168.0.107 /directory/service/program.php?action=send Quelque chose
remplace juste 192.168 .0.107 pour votre propre IP (n'essayez pas 127.0.0.1 car c'est la même chose que localhost )

Charlie
la source
-4

S'il vous plaît changer

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

À

con = (HttpURLConnection) new URL("http://YOUR_IP:8080/myapp/service/generate").openConnection();
Phuoc Huynh
la source
2
Changer pourquoi? Le PO a spécifiquement déclaré que le service fonctionne localement.
Marquis of Lorne
Au cas où vous auriez besoin d'obtenir une ressource d'image de localhost, cela ne fonctionne pas.
Phuoc Huynh