FileNotFoundException al obtener el objeto InputStream de HttpURLConnection

109

Estoy tratando de enviar una solicitud de publicación a una URL usando HttpURLConnection (para usar cUrl en java). El contenido de la solicitud es xml y, en el punto final, la aplicación procesa el xml y almacena un registro en la base de datos y luego envía una respuesta en forma de cadena xml. La aplicación está alojada en apache-tomcat localmente.

Cuando ejecuto este código desde la terminal, se agrega una fila a la base de datos como se esperaba. Pero se lanza una excepción de la siguiente manera al obtener InputStream de la conexión

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 esta el codigo

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 confuso porque la excepción se remonta a la línea InputStream response = con.getInputStream();y no parece haber ningún archivo involucrado para una FileNotFoundException.

Cuando intento abrir una conexión a un archivo xml directamente, no lanza esta excepción.

La aplicación de servicio utiliza Spring Framework y Jaxb2Marshaller para crear la respuesta xml.

La clase ReadWriteTextFile se toma de aquí

Gracias.

Editar: Bueno, guarda los datos en la base de datos y envía un código de estado de respuesta 404 al mismo tiempo.

También intenté hacer un curl usando php e imprimir el CURLINFO_HTTP_CODEque resulta ser 200.

¿Alguna idea sobre cómo puedo depurar esto? Tanto el servicio como el cliente están en el servidor local.

Resuelto: podría resolver el problema después de consultar una respuesta en el SO.

Parece que HttpURLConnection siempre devuelve una respuesta 404 cuando se conecta a una URL con un puerto no estándar.

Agregar estas líneas lo resolvió

con.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
con.setRequestProperty("Accept","*/*");
naiquevin
fuente
1
"Cuando ejecuto este código desde la terminal", ¿qué código? No está claro qué funciona frente a qué no.
Jon Skeet
HttpCurl es el nombre de la clase que tiene este método principal. Esta clase se compila y se ejecuta desde la terminal
naiquevin
3
Experimenté el mismo problema, pero ninguna de las soluciones aquí funcionó. Finalmente descubrí que era un problema con Java 1.7.0_05 y actualicé a la última versión 1.7.0_21 y el problema desapareció. También me di cuenta de que el problema no ocurría en Java 1.6. Solo un FYI para cualquiera que todavía esté atrapado.
Steven
Chicos! consulte el comentario "Resuelto" sobre la pregunta en lugar de las respuestas.
김준호
Posible duplicado del cuerpo de respuesta de error
Tony

Respuestas:

130

No sé acerca de su combinación Spring / JAXB, pero el servicio web REST promedio no devolverá un cuerpo de respuesta en POST / PUT, solo un estado de respuesta . Le gustaría determinarlo en lugar del cuerpo.

Reemplazar

InputStream response = con.getInputStream();

por

int status = con.getResponseCode();

Todos los códigos de estado disponibles y su significado están disponibles en la especificación HTTP, como se vinculó anteriormente. El servicio web en sí también debería venir junto con alguna documentación que describa todos los códigos de estado admitidos por el servicio web y su significado especial, si corresponde.

Si el estado comienza con 4nno 5nn, le gustaría usar getErrorStream()en su lugar para leer el cuerpo de la respuesta que puede contener los detalles del error.

InputStream error = con.getErrorStream();
BalusC
fuente
Ok, probé esto y devuelve el estado 404. Pero, extrañamente, ¡también está guardando en DB! Además, por lo que menciona, ¿significa que cualquier servicio REST solo devolverá un código de estado? ¿Qué sucede si quiero devolver más información, como un mensaje de error de validación xml o una URL si la publicación es exitosa?
naiquevin
Sí, en solicitudes de modificación como POST / PUT / etc., generalmente no devolverá un cuerpo. Por lo general, le gustaría determinar el estado de la respuesta antes de leer el flujo de entrada o error. Agregué algunos detalles a la respuesta. Pero si realmente devuelve el estado 404, es probable que haya algún error en el servicio web. Informaría a su responsable.
BalusC
En este caso, el encargado del mantenimiento del servicio soy yo. .. Bueno, intenté hacer un curl usando php y devuelve 200 (he editado mi pregunta) También intenté getErrorStream()como se sugiere. Lanza una NullPointerException en new InputStreamReader(con.getErrorStream()).
naiquevin
Lo siento, no estoy familiarizado con los servicios web de Spring. Solo tengo experiencia práctica con JAX-WS / RS de la API estándar de Java EE 5/6. Sugeriría poner un punto de interrupción en el método responsable de procesar el archivo XML y luego dar un paso más desde allí.
BalusC
Este comportamiento parece muy poco útil, sobre todo porque hace que las referencias a las cosas sean URLConnectionproblemáticas más genéricas . Me pregunto por qué los chicos de Java no implementaron simplemente getInputStream()en HttpUrlConnectionla línea de return responseCode == 200 ? super.getInputStream() : this.getErrorStream(). Pero de todos modos; respuesta informativa, +1.
aroth
51

FileNotFound es solo una desafortunada excepción que se usa para indicar que el servidor web devolvió un 404.

Jon Skeet
fuente
1
Eso no explica por qué la fila se agrega a DB como lo indica el OP.
BalusC
@BalusC: A menos que el servidor agregue una fila y luego devuelva un 404.
Jon Skeet
sí, parece comportarse de esta manera. Qué significa eso ?
naiquevin
@naiquevin: Es difícil de decir, pero dado que es un servicio que se ejecuta localmente, debería poder depurarlo usted mismo.
Jon Skeet
7
HttpURLConnection también lanza FileNotFoundException para respuestas 403 (y probablemente otras). (Incluso si el cuerpo de respuesta no está vacío, parece). De todos modos, siempre investigue getResponseCode()antes de llamar getInputStream().
Jonik
29

Para cualquiera que tenga este problema en el futuro, la razón es porque el código de estado era 404 (o en mi caso era 500). Parece que la InpuStreamfunción arrojará un error cuando el código de estado no sea 200.

En mi caso, controlo mi propio servidor y estaba devolviendo un código de estado 500 para indicar que ocurrió un error. A pesar de que también envié un cuerpo con un mensaje de cadena que detalla el error, inputstreamarrojó un error independientemente de que el cuerpo sea completamente legible.

Si controla su servidor, supongo que esto se puede manejar enviándose un código de estado 200 y luego manejando la respuesta de error de cadena.

Terence Chow
fuente
21
Para ser claros, lo estás manejando mal. Debería usar connection.getResponseCode para verificar si estaba bien. Luego use connection.getErrorStream para obtener el cuerpo del error en lugar de getInputStream. (o puede usar getResponseMessage) No debe enviar un código de estado 200 si fue un error, use los códigos de error http según lo previsto.
rekh127
5

Para cualquier otra persona que se tropiece con esto, me sucedió lo mismo al intentar enviar un encabezado de solicitud SOAP a un servicio SOAP. El problema fue un orden incorrecto en el código, primero solicité el flujo de entrada antes de enviar el cuerpo XML. En el código recortado a continuación, la línea InputStream in = conn.getInputStream();vino inmediatamente después de la ByteArrayOutputStream out = new ByteArrayOutputStream();cual es el orden incorrecto de las cosas.

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 en este caso fue una forma desafortunada de codificar el código de respuesta HTTP 400.

zero0cool
fuente
FileNotFound se debió a que la conexión no tiene un flujo ascendente. No tuvo nada que ver con la codificación del código de respuesta 400. Puede obtener el código de respuesta con getResponseCode (). Entonces, si no es HTTP_OK, debe obtener el cuerpo con conn.getErrorStream () o getResponseMessage ()
rekh127
new ByteArrayOutputStream()no tiene nada que ver con eso. El problema es el orden entre getInputStream()y getResponseCode().
Marqués de Lorne
4

FileNotFound en este caso significa que obtuvo un 404 de su servidor. ¿Podría ser que al servidor no le gustan las solicitudes "POST"?

tofarr
fuente
Pero guarda un registro en la base de datos. ¿Podría Jaxb2Marshaller ser el problema aquí?
naiquevin
2
Esto no solo sucede con los 404. También ocurre con cualquier respuesta con un cuerpo vacío.
Dave Cameron
3
Esta respuesta es, lamentablemente, incorrecta. FileNotFound en esta situación solo significaba que se llamó a HttpURLConnection # getInputStream () cuando el código de respuesta estaba por encima de 399, mientras que en esta situación se debería haber llamado a HttpURLConnection # getErrorStream (). OTOH, si el servidor no acepta POST, habría devuelto 405 Método no permitido. No hay relación con FileNotFound que se lanza allí.
Michal M
0

La solución:
simplemente cambie localhost por la IP de su PC
si desea saber esto: Windows + r> cmd> ipconfig
ejemplo: http: // 192.168.0.107 /directory/service/program.php?action=send Algo
simplemente reemplace 192.168 .0.107 para su propia IP (no intente 127.0.0.1 porque es lo mismo que localhost )

charlie
fuente
-4

Por favor cambia

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

A

con = (HttpURLConnection) new URL("http://YOUR_IP:8080/myapp/service/generate").openConnection();
Phuoc Huynh
fuente
2
¿Cambiarlo por qué? El OP ha declarado específicamente que el servicio se ejecuta localmente.
Marqués de Lorne
En caso de que necesite obtener el recurso de imagen de localhost, no funciona.
Phuoc Huynh