FileNotFoundException saat mendapatkan objek InputStream dari HttpURLConnection

109

Saya mencoba mengirim permintaan posting ke url menggunakan HttpURLConnection (untuk menggunakan cUrl di java). Isi permintaannya adalah xml dan pada titik akhir, aplikasi memproses xml dan menyimpan record ke database dan kemudian mengirimkan kembali respon dalam bentuk string xml. Aplikasi dihosting di apache-tomcat secara lokal.

Ketika saya menjalankan kode ini dari terminal, baris akan ditambahkan ke db seperti yang diharapkan. Tapi pengecualian dilemparkan sebagai berikut saat mendapatkan InputStream dari koneksi

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)

Ini kodenya

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

Ini membingungkan karena pengecualian dilacak ke baris InputStream response = con.getInputStream();dan sepertinya tidak ada file yang terlibat untuk FileNotFoundException.

Saat saya mencoba membuka koneksi ke file xml secara langsung, pengecualian ini tidak muncul.

Aplikasi layanan menggunakan framework spring dan Jaxb2Marshaller untuk membuat xml respons.

Kelas ReadWriteTextFile diambil dari sini

Terima kasih.

Sunting: Ini menyimpan data dalam DB dan mengirimkan kembali kode status respons 404 pada saat yang sama.

Saya juga mencoba melakukan curl menggunakan php dan mencetak CURLINFO_HTTP_CODEyang ternyata 200.

Ada ide tentang bagaimana cara men-debug ini? Baik layanan dan klien berada di server lokal.

Terselesaikan: Saya bisa menyelesaikan masalah setelah mengacu pada jawaban di SO itu sendiri.

Tampaknya HttpURLConnection selalu mengembalikan respons 404 saat menyambungkan ke url dengan port non standar.

Menambahkan garis-garis ini menyelesaikannya

con.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
con.setRequestProperty("Accept","*/*");
naiquevin
sumber
1
"Ketika saya menjalankan kode ini dari terminal" - kode yang mana? Tidak jelas apa yang berhasil vs apa yang tidak.
Jon Skeet
HttpCurl adalah nama kelas yang memiliki metode utama ini. Kelas ini dikompilasi dan dijalankan dari terminal
naiquevin
3
Saya mengalami masalah yang sama, tetapi tidak ada solusi di sini yang berhasil. Saya akhirnya menemukan bahwa itu adalah masalah dengan Java 1.7.0_05 dan memperbarui ke versi terbaru 1.7.0_21 dan masalahnya hilang. Saya juga menyadari bahwa masalahnya tidak terjadi di Java 1.6. Hanya FYI bagi siapa saja yang masih terjebak.
Steven
Guys! lihat komentar "terselesaikan" pada pertanyaan daripada jawaban!
김준호
Kemungkinan duplikat dari tubuh respons kesalahan Baca di Jawa
Tony

Jawaban:

130

Saya tidak tahu tentang kombinasi Spring / JAXB Anda, tetapi rata-rata webservice REST tidak akan mengembalikan tubuh respons pada POST / PUT, hanya status respons . Anda ingin menentukannya, bukan tubuhnya.

Menggantikan

InputStream response = con.getInputStream();

oleh

int status = con.getResponseCode();

Semua kode status yang tersedia dan artinya tersedia dalam spesifikasi HTTP, seperti yang ditautkan sebelumnya. Layanan web itu sendiri juga harus disertai dengan beberapa dokumentasi yang meninjau semua kode status yang didukung oleh layanan web dan arti khususnya, jika ada.

Jika status dimulai dengan 4nnatau 5nn, Anda ingin menggunakan getErrorStream()untuk membaca isi respons yang mungkin berisi detail kesalahan.

InputStream error = con.getErrorStream();
BalusC
sumber
Ok saya coba ini dan mengembalikan status 404. Tapi anehnya, ini juga menyimpan di DB! Juga dari apa yang Anda sebutkan, apakah itu berarti setiap layanan REST hanya akan mengembalikan kode status? Bagaimana jika saya ingin mengembalikan lebih banyak informasi seperti pesan kesalahan validasi xml atau url jika posting berhasil?
naiquevin
Ya, pada permintaan modifikasi seperti POST / PUT / etc biasanya tidak akan mengembalikan body. Anda biasanya ingin menentukan status respons sebelum membaca aliran input atau kesalahan. Saya menambahkan beberapa detail pada jawabannya. Tetapi jika benar-benar mengembalikan status 404 maka mungkin ada beberapa bug di layanan web. Saya akan melaporkan ke pengelolanya.
BalusC
Dalam hal ini pengelola layanan kebetulan adalah saya! .. Yah saya mencoba melakukan curl menggunakan php dan mengembalikan 200 (telah mengedit pertanyaan saya) Juga mencoba getErrorStream()seperti yang disarankan. Ini akan memunculkan NullPointerException new InputStreamReader(con.getErrorStream()).
naiquevin
Maaf, saya tidak famliar dengan layanan web Spring. Saya hanya memiliki pengalaman langsung dengan JAX-WS / RS dari Java EE 5/6 API standar. Saya menyarankan untuk meletakkan breakpoint pada metode yang bertanggung jawab untuk memproses file XML dan kemudian melangkah lebih jauh dari sana.
BalusC
Perilaku ini tampaknya sangat tidak berguna, terutama karena hal itu membuat merujuk pada hal-hal yang lebih umum URLConnectionmenjadi masalah. Saya bertanya-tanya mengapa orang-orang Java tidak hanya menerapkan getInputStream()di HttpUrlConnectionsepanjang baris return responseCode == 200 ? super.getInputStream() : this.getErrorStream(). Tapi bagaimanapun; jawaban informatif, +1.
Apakah
51

FileNotFound hanyalah pengecualian yang tidak menguntungkan yang digunakan untuk menunjukkan bahwa server web mengembalikan 404.

Jon Skeet
sumber
1
Itu tidak menjelaskan mengapa baris tersebut ditambahkan ke DB seperti yang dinyatakan oleh OP.
BalusC
@BalusC: Kecuali server menambahkan baris dan kemudian mengembalikan 404.
Jon Skeet
ya sepertinya berperilaku seperti ini. Apa artinya ?
naiquevin
@naiquevin: Sulit untuk mengatakannya, tetapi mengingat ini adalah layanan yang berjalan secara lokal, Anda harus dapat men-debugnya sendiri.
Jon Skeet
7
HttpURLConnection juga menampilkan FileNotFoundException untuk respons 403 (dan mungkin lainnya). (Meskipun badan respons tidak kosong, tampaknya.) Bagaimanapun, selalu selidiki getResponseCode()sebelum menelepon getInputStream().
Jonik
29

Bagi siapa pun yang memiliki masalah ini di masa mendatang, alasannya adalah karena kode statusnya adalah 404 (atau dalam kasus saya adalah 500). Tampaknya InpuStreamfungsi tersebut akan menampilkan kesalahan saat kode status bukan 200.

Dalam kasus saya, saya mengontrol server saya sendiri dan mengembalikan kode status 500 untuk menunjukkan kesalahan terjadi. Meskipun saya juga mengirim tubuh dengan pesan string yang merinci kesalahan, itu inputstreammelemparkan kesalahan terlepas dari tubuh itu benar-benar dapat dibaca.

Jika Anda mengontrol server Anda, saya kira ini dapat ditangani dengan mengirimkan kode status 200 kepada diri Anda sendiri dan kemudian menangani apa pun respons kesalahan string itu.

Terence Chow
sumber
21
Untuk lebih jelasnya, Anda hanya salah menanganinya. Anda harus menggunakan connection.getResponseCode untuk memeriksa apakah itu baik-baik saja. Kemudian gunakan connection.getErrorStream untuk mendapatkan badan kesalahan, bukan getInputStream. (atau Anda dapat menggunakan getResponseMessage) Anda tidak boleh mengirim kode status 200 jika itu adalah kesalahan, gunakan kode kesalahan http sebagaimana dimaksud.
rekh127
5

Untuk orang lain yang tersandung ini, hal yang sama terjadi pada saya ketika mencoba mengirim header permintaan SOAP ke layanan SOAP. Masalahnya adalah urutan kode yang salah, saya meminta aliran input terlebih dahulu sebelum mengirim badan XML. Dalam kode yang dipotong di bawah ini, baris InputStream in = conn.getInputStream();muncul segera setelah ByteArrayOutputStream out = new ByteArrayOutputStream();itu adalah urutan yang salah.

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 dalam hal ini adalah cara yang tidak menguntungkan untuk menyandikan kode respons HTTP 400.

zero0cool
sumber
FileNotFound karena koneksi tidak memiliki aliran uput. Ini tidak ada hubungannya dengan pengkodean kode respons 400. Anda bisa mendapatkan responsecode dengan getResponseCode (). Kemudian jika bukan HTTP_OK Anda harus mendapatkan body dengan conn.getErrorStream () atau getResponseMessage ()
rekh127
new ByteArrayOutputStream()tidak ada hubungannya sama sekali. Masalahnya adalah urutan antara getInputStream()dan getResponseCode().
Marquis dari Lorne
4

FileNotFound dalam hal ini berarti Anda mendapat 404 dari server Anda - mungkinkah server tidak menyukai permintaan "POST"?

tofarr
sumber
Tapi itu menyimpan rekor ke db. Mungkinkah Jaxb2Marshaller menjadi masalah di sini?
naiquevin
2
Ini tidak hanya terjadi untuk 404. Ini juga terjadi untuk respons apa pun dengan tubuh kosong.
Dave Cameron
3
Sayangnya, jawaban ini salah. FileNotFound dalam situasi ini hanya berarti bahwa HttpURLConnection # getInputStream () dipanggil ketika kode respons di atas 399, sedangkan dalam situasi ini HttpURLConnection # getErrorStream () seharusnya sudah dipanggil. OTOH, jika server tidak menerima POST, server akan mengembalikan 405 Metode Tidak Diizinkan. Tidak ada kaitannya dengan FileNotFound yang dibuang ke sana.
Michal M
0

Solusinya:
ganti saja localhost untuk IP PC anda
jika ingin mengetahui ini: Windows + r> cmd> ipconfig
contoh: http: // 192.168.0.107 /directory/service/program.php?action=sendSesuatu yang
baru ganti 192.168 .0.107 untuk IP Anda sendiri (jangan coba 127.0.0.1 karena sama dengan localhost )

charlie
sumber
-4

Tolong ganti

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

Untuk

con = (HttpURLConnection) new URL("http://YOUR_IP:8080/myapp/service/generate").openConnection();
Phuoc Huynh
sumber
2
Ubah itu mengapa? OP secara spesifik menyatakan bahwa layanan berjalan secara lokal.
Marquis dari Lorne
Jika Anda perlu mendapatkan sumber gambar dari localhost, itu tidak berfungsi.
Phuoc Huynh