FileNotFoundException podczas pobierania obiektu InputStream z HttpURLConnection

109

Próbuję wysłać żądanie wpisu do adresu URL za pomocą HttpURLConnection (do używania cUrl w java). Treść żądania to xml, a na końcu aplikacja przetwarza xml i przechowuje rekord w bazie danych, a następnie odsyła odpowiedź w postaci ciągu xml. Aplikacja jest hostowana lokalnie na apache-tomcat.

Kiedy wykonuję ten kod z terminala, wiersz zostanie dodany do bazy danych zgodnie z oczekiwaniami. Ale wyjątek jest zgłaszany w następujący sposób podczas pobierania InputStream z połączenia

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)

Oto kod

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

Jest to mylące, ponieważ wyjątek jest śledzony w wierszu InputStream response = con.getInputStream();i wydaje się, że nie ma żadnego pliku związanego z wyjątkiem FileNotFoundException.

Kiedy próbuję bezpośrednio otworzyć połączenie z plikiem xml, nie zgłasza tego wyjątku.

Aplikacja usługowa wykorzystuje Spring Framework i Jaxb2Marshaller do tworzenia odpowiedzi XML.

Klasa ReadWriteTextFile jest pobierana stąd

Dzięki.

Edycja: Cóż, zapisuje dane w bazie danych i odsyła kod statusu odpowiedzi 404 w tym samym czasie.

Spróbowałem też zrobić curl używając php i wydrukować, CURLINFO_HTTP_CODEktóry okazuje się być 200.

Jakieś pomysły, jak mam to debugować? Usługa i klient znajdują się na serwerze lokalnym.

Rozwiązane: mogłem rozwiązać problem po zapoznaniu się z odpowiedzią dotyczącą samego SO.

Wygląda na to, że HttpURLConnection zawsze zwraca odpowiedź 404 podczas łączenia się z adresem URL z niestandardowym portem.

Dodanie tych linii rozwiązało problem

con.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
con.setRequestProperty("Accept","*/*");
naiquevin
źródło
1
„Kiedy wykonuję ten kod z terminala” - jaki kod? Nie jest jasne, co działa, a co nie.
Jon Skeet,
HttpCurl to nazwa klasy, która ma tę główną metodę. Ta klasa jest kompilowana i uruchamiana z terminala
naiquevin
3
Doświadczyłem tego samego problemu, ale żadne z rozwiązań tutaj nie zadziałało. W końcu doszedłem do wniosku, że to problem z Javą 1.7.0_05 i zaktualizowałem ją do najnowszej wersji 1.7.0_21 i problem zniknął. Zrozumiałem również, że problem nie występuje w Javie 1.6. Tylko do Twojej wiadomości dla każdego, kto wciąż utknął.
Steven
Chłopaki! zobacz komentarz „Rozwiązany” zamiast odpowiedzi!
김준호

Odpowiedzi:

130

Nie wiem o twojej kombinacji Spring / JAXB, ale przeciętny serwis WWW REST nie zwróci treści odpowiedzi w POST / PUT, tylko status odpowiedzi . Chciałbyś to określić zamiast ciała.

Zastąpić

InputStream response = con.getInputStream();

przez

int status = con.getResponseCode();

Wszystkie dostępne kody stanu i ich znaczenie są dostępne w specyfikacji HTTP, zgodnie z wcześniejszym połączeniem. Sama usługa sieciowa powinna również zawierać dokumentację, która zawiera przegląd wszystkich kodów statusu obsługiwanych przez usługę sieciową i ich specjalne znaczenie, jeśli takie istnieje.

Jeśli stan zaczyna się od 4nnlub 5nn, getErrorStream()zamiast tego chcesz odczytać treść odpowiedzi, która może zawierać szczegóły błędu.

InputStream error = con.getErrorStream();
BalusC
źródło
Ok, próbowałem tego i zwraca stan 404. Ale o dziwo, zapisuje również w DB! Poza tym z tego, o czym wspomniałeś, czy oznacza to, że jakakolwiek usługa REST zwróci tylko kod statusu? Co zrobić, jeśli chcę zwrócić więcej informacji, takich jak komunikat o błędzie walidacji XML lub adres URL, jeśli wysyłanie wiadomości zakończyło się pomyślnie?
naiquevin
Tak, w przypadku żądań modyfikacji, takich jak POST / PUT / etc, zwykle nie zwraca treści. Zwykle chciałbyś określić stan odpowiedzi przed odczytaniem wejścia lub strumienia błędów. Dodałem trochę szczegółów do odpowiedzi. Ale jeśli naprawdę zwraca status 404, prawdopodobnie jest jakiś błąd w usłudze sieciowej. Zgłosiłbym się do jego opiekuna.
BalusC
W tym przypadku opiekunem usługi jestem ja! .. Cóż, próbowałem zrobić curl używając php i zwraca 200 (zredagowałem moje pytanie) getErrorStream(). Zgłasza wyjątek NullPointerException na new InputStreamReader(con.getErrorStream()).
naiquevin
Przepraszamy, nie znam usług sieciowych Spring. Mam tylko doświadczenie z JAX-WS / RS ze standardowego API Java EE 5/6. Proponuję umieścić punkt przerwania w metodzie odpowiedzialnej za przetwarzanie pliku XML, a następnie przejść dalej.
BalusC
To zachowanie wydaje się bardzo nieużyteczne, zwłaszcza że sprawia, że ​​odwoływanie się do rzeczy za pomocą bardziej ogólnych rzeczy staje się URLConnectionproblematyczne. Zastanawiam się, dlaczego faceci z Java nie zaimplementowali po prostu getInputStream()w HttpUrlConnectionstylu return responseCode == 200 ? super.getInputStream() : this.getErrorStream(). Ale w każdym razie; informacyjna odpowiedź, +1.
aroth
51

FileNotFound to tylko niefortunny wyjątek używany do wskazania, że ​​serwer WWW zwrócił błąd 404.

Jon Skeet
źródło
1
To nie wyjaśnia, dlaczego wiersz jest dodawany do DB, jak podano w PO.
BalusC
@BalusC: Chyba że serwer dodaje wiersz, a następnie zwraca 404.
Jon Skeet
tak, wydaje się, że zachowuje się w ten sposób. Co to znaczy ?
naiquevin
@naiquevin: Trudno powiedzieć, ale biorąc pod uwagę, że jest to usługa działająca lokalnie, powinieneś być w stanie samodzielnie ją zdebugować.
Jon Skeet,
7
HttpURLConnection zgłasza również wyjątek FileNotFoundException dla odpowiedzi 403 (i prawdopodobnie innych). (Nawet jeśli treść odpowiedzi nie jest pusta, jak się wydaje). W każdym razie, zawsze sprawdź, getResponseCode()zanim zadzwonisz getInputStream().
Jonik
29

Dla każdego, kto będzie miał ten problem w przyszłości, powodem jest to, że kod statusu to 404 (lub w moim przypadku był to 500). Wygląda na to, że InpuStreamfunkcja zgłosi błąd, gdy kod stanu jest inny niż 200.

W moim przypadku kontroluję swój własny serwer i zwracałem kod statusu 500, aby wskazać, że wystąpił błąd. Pomimo tego, że wysłałem również treść z komunikatem tekstowym opisującym błąd, inputstreamwyrzuciłem błąd, niezależnie od tego , czy treść była całkowicie czytelna.

Jeśli kontrolujesz swój serwer, przypuszczam, że można to załatwić, wysyłając sobie kod statusu 200, a następnie obsługując odpowiedź na błąd ciągu.

Terence Chow
źródło
21
Żeby było jasne, po prostu źle sobie z tym radzisz. Powinieneś użyć connection.getResponseCode, aby sprawdzić, czy wszystko jest w porządku. Następnie użyj connection.getErrorStream, aby pobrać treść błędu zamiast getInputStream. (lub możesz użyć getResponseMessage) Nie powinieneś wysyłać kodu statusu 200, jeśli był to błąd, użyj kodów błędów http zgodnie z przeznaczeniem.
rekh127
5

Dla każdego, kto się o to potyka, to samo przytrafiło mi się podczas próby wysłania nagłówka żądania SOAP do usługi SOAP. Problem polegał na złej kolejności w kodzie, najpierw zażądałem strumienia wejściowego przed wysłaniem treści XML. W poniższym wyciętym kodzie linia InputStream in = conn.getInputStream();pojawiła się bezpośrednio po ByteArrayOutputStream out = new ByteArrayOutputStream();której jest niepoprawna kolejność rzeczy.

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 w tym przypadku był to niefortunny sposób zakodowania kodu odpowiedzi HTTP 400.

zero0cool
źródło
FileNotFound powstał, ponieważ połączenie nie ma strumienia uput. Nie miało to nic wspólnego z kodowaniem kodu odpowiedzi 400. Kod odpowiedzi można uzyskać za pomocą metody getResponseCode (). Następnie, jeśli nie jest to HTTP_OK, powinieneś pobrać treść za pomocą conn.getErrorStream () lub getResponseMessage ()
rekh127
new ByteArrayOutputStream()nie ma z tym w ogóle nic wspólnego. Problem dotyczy kolejności między getInputStream()a getResponseCode().
Markiz Lorne
4

FileNotFound w tym przypadku oznacza, że ​​otrzymałeś 404 z serwera - czy to możliwe, że serwer nie lubi żądań „POST”?

tofarr
źródło
Ale zapisuje rekord w bazie danych. Czy Jaxb2Marshaller może być tutaj problemem?
naiquevin
2
Dzieje się tak nie tylko w przypadku 404. Dzieje się tak również w przypadku każdej odpowiedzi z pustym ciałem.
Dave Cameron,
3
Ta odpowiedź jest niestety błędna. FileNotFound w tej sytuacji oznaczało jedynie, że HttpURLConnection # getInputStream () zostało wywołane, gdy kod odpowiedzi był powyżej 399, podczas gdy w tej sytuacji należało wywołać HttpURLConnection # getErrorStream (). OTOH, gdyby serwer nie akceptował POST, zwróciłby 405 Metoda niedozwolona. Brak związku z wyrzucaniem tam FileNotFound.
Michał M
0

Rozwiązanie:
Wystarczy zmienić localhost dla IP komputera
, jeśli chcesz wiedzieć, to: Windows + R> cmd> ipconfig
przykład: http: // 192.168.0.107 /directory/service/program.php?action=sendSomething
prostu zastąpić 192.168 .0.107 dla własnego adresu IP (nie próbuj 127.0.0.1, ponieważ jest to to samo, co localhost )

Charlie
źródło
-4

Proszę zmień

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

Do

con = (HttpURLConnection) new URL("http://YOUR_IP:8080/myapp/service/generate").openConnection();
Phuoc Huynh
źródło
2
Zmień to, dlaczego? OP wyraźnie stwierdził, że usługa działa lokalnie.
Markiz Lorne
W przypadku, gdy potrzebujesz uzyskać zasób obrazu z localhost, to nie działa.
Phuoc Huynh