FileNotFoundException при получении объекта InputStream из HttpURLConnection

109

Я пытаюсь отправить почтовый запрос на URL-адрес с помощью HttpURLConnection (для использования cUrl в java). Содержимым запроса является xml, и в конечной точке приложение обрабатывает xml и сохраняет запись в базе данных, а затем отправляет ответ в виде строки xml. Приложение размещено на apache-tomcat локально.

Когда я выполняю этот код с терминала, в базу данных добавляется строка, как и ожидалось. Но при получении InputStream из соединения возникает исключение.

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)

Вот код

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

Это сбивает с толку, потому что исключение отслеживается до строки, InputStream response = con.getInputStream();и похоже, что для FileNotFoundException нет какого-либо файла.

Когда я пытаюсь открыть соединение с XML-файлом напрямую, это исключение не возникает.

Приложение-служба использует платформу Spring и Jaxb2Marshaller для создания XML-ответа.

Класс ReadWriteTextFile взят отсюда

Спасибо.

Изменить: он сохраняет данные в БД и одновременно отправляет обратно код состояния ответа 404.

Я также попытался сделать завиток с помощью php и распечатать, CURLINFO_HTTP_CODEкоторый оказался 200.

Любые идеи о том, как мне отладить это? И служба, и клиент находятся на локальном сервере.

Постановили: я мог бы решить проблему, обратившись к ответу о самой SO.

Кажется, что HttpURLConnection всегда возвращает ответ 404 при подключении к URL-адресу с нестандартным портом.

Добавление этих строк решило это

con.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
con.setRequestProperty("Accept","*/*");
Naiquevin
источник
1
«Когда я выполняю этот код с терминала» - какой код? Непонятно, что работает, а что не работает.
Джон Скит
HttpCurl - это имя класса, в котором есть этот основной метод. Этот класс компилируется и запускается с терминала
найкевин
3
У меня возникла та же проблема, но ни одно из решений здесь не помогло. В конце концов я понял, что это проблема с Java 1.7.0_05, и обновил его до последней версии 1.7.0_21, и проблема исчезла. Я также понял, что проблема не возникла в Java 1.6. Просто к вашему сведению для тех, кто все еще застрял.
Стивен
Ребята! см. "Решенный" комментарий к вопросу, а не ответы!
김준호
Возможный дубликат тела ответа на ошибку чтения в Java
Тони

Ответы:

130

Я не знаю о вашей комбинации Spring / JAXB, но средний веб-сервис REST не будет возвращать тело ответа при POST / PUT, а только статус ответа . Вы бы хотели определить его вместо тела.

Заменить

InputStream response = con.getInputStream();

по

int status = con.getResponseCode();

Все доступные коды состояния и их значения доступны в спецификации HTTP, как указано ранее. Сам веб-сервис также должен сопровождаться некоторой документацией, в которой рассматриваются все коды состояния, поддерживаемые веб-сервисом, и их особое значение, если таковое имеется.

Если статус начинается с 4nnили 5nn, вы хотели бы использовать getErrorStream()вместо него чтение тела ответа, которое может содержать сведения об ошибке.

InputStream error = con.getErrorStream();
BalusC
источник
Хорошо, я пробовал это, и он возвращает статус 404. Но, как ни странно, он также сохраняется в БД! Также из того, что вы упомянули, означает ли это, что любая служба REST будет возвращать только код состояния? Что, если я хочу вернуть дополнительную информацию, такую ​​как сообщение об ошибке проверки XML или URL-адрес, если публикация прошла успешно?
naiquevin
Да, при запросах на изменение, таких как POST / PUT / и т. Д., Он обычно не возвращает тело. Обычно вы хотите определить статус ответа перед чтением потока ввода или ошибки. Я добавил к ответу некоторые детали. Но если он действительно возвращает статус 404, вероятно, в веб-сервисе есть ошибка. Я бы доложил его сопровождающему.
BalusC
В этом случае мейнтейнером сервиса являюсь я! .. Ну, я попытался сделать завиток с помощью php, и он вернул 200 (отредактировал мой вопрос). Также попробовал, getErrorStream()как предлагалось. Это вызывает исключение NullPointerException new InputStreamReader(con.getErrorStream()).
naiquevin
Извините, я не знаком с веб-сервисами Spring. У меня есть только практический опыт работы с JAX-WS / RS из стандартного API Java EE 5/6. Я бы предложил поставить точку останова на методе, отвечающем за обработку XML-файла, а затем сделать шаг дальше.
BalusC
Такое поведение кажется очень бесполезным, особенно потому, что оно делает обращение к вещам более общим URLConnectionпроблематичным. Интересно, почему разработчики Java просто не реализовали его getInputStream()в HttpUrlConnectionдухе return responseCode == 200 ? super.getInputStream() : this.getErrorStream(). Но в любом случае; информативный ответ, +1.
Aroth
51

FileNotFound это просто досадное исключение, используемое, чтобы указать, что веб-сервер вернул 404.

Джон Скит
источник
1
Это не объясняет, почему строка добавляется в БД, как указано в OP.
BalusC
@BalusC: Если сервер не добавляет строку, а затем возвращает 404.
Джон Скит,
да, похоже, он так себя ведет. Что это означает ?
naiquevin
@naiquevin: Трудно сказать, но, учитывая, что это служба, работающая локально, вы сможете отлаживать ее самостоятельно.
Джон Скит
7
HttpURLConnection также генерирует исключение FileNotFoundException для ответов 403 (и, возможно, других). ( Кажется, даже если тело ответа не пустое.) В любом случае, всегда исследуйте, getResponseCode()прежде чем звонить getInputStream().
Jonik
29

Для тех, кто столкнется с этой проблемой в будущем, причина в том, что код состояния был 404 (или в моем случае был 500). Похоже, что InpuStreamфункция выдаст ошибку, если код состояния не равен 200.

В моем случае я управляю своим собственным сервером и возвращал код состояния 500, чтобы указать, что произошла ошибка. Несмотря на то, что я также отправлял тело со строковым сообщением с подробным описанием ошибки, сообщение об ошибке inputstreamвыдавалось независимо от того, было ли тело полностью читаемым.

Если вы управляете своим сервером, я полагаю, что с этим можно справиться, отправив себе код состояния 200, а затем обработав любой ответ с ошибкой строки.

Теренс Чоу
источник
21
Чтобы было ясно, вы просто неправильно с этим справляетесь. Вы должны использовать connection.getResponseCode, чтобы проверить, все ли в порядке. Затем используйте connection.getErrorStream, чтобы получить тело ошибки вместо getInputStream. (или вы можете использовать getResponseMessage) Вы не должны отправлять код состояния 200, если это была ошибка, используйте коды ошибок http по назначению.
rekh127
5

Для всех, кто сталкивался с этим, то же самое случилось со мной, когда я пытался отправить заголовок запроса SOAP в службу SOAP. Проблема заключалась в неправильном порядке в коде, я сначала запросил входной поток перед отправкой тела XML. В приведенном ниже фрагменте кода строка идет InputStream in = conn.getInputStream();сразу после, ByteArrayOutputStream out = new ByteArrayOutputStream();что является неправильным порядком вещей.

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 в данном случае это был неудачный способ кодирования кода ответа HTTP 400.

zero0cool
источник
FileNotFound возник из-за того, что соединение не имеет восходящего потока. Это не имело ничего общего с кодировкой кода ответа 400. Вы можете получить код ответа с помощью getResponseCode (). Тогда, если это не HTTP_OK, вы должны получить тело с помощью conn.getErrorStream () или getResponseMessage ()
rekh127
new ByteArrayOutputStream()не имеет к этому никакого отношения. Проблема заключается в порядке между getInputStream()и getResponseCode().
Marquis of Lorne
4

FileNotFound в данном случае означает, что вы получили 404-е от вашего сервера - может быть, серверу не нравятся запросы POST?

тофарр
источник
Но он сохраняет запись в БД. Может ли здесь проблема Jaxb2Marshaller?
naiquevin
2
Это происходит не только с ошибками 404. Это также происходит с любым ответом с пустым телом.
Дэйв Кэмерон,
3
К сожалению, это неверный ответ. FileNotFound в этой ситуации означал только то, что HttpURLConnection # getInputStream () был вызван, когда код ответа был выше 399, тогда как в этой ситуации должен был быть вызван HttpURLConnection # getErrorStream (). OTOH, если бы сервер не принимал POST-запросы, он бы вернул 405 Method Not Allowed. Никакого отношения к брошенному туда FileNotFound нет.
Michal M
0

Решение:
просто измените localhost на IP-адрес вашего ПК,
если вы хотите это знать: Windows + r> cmd> ipconfig,
пример: http: // 192.168.0.107 /directory/service/program.php?action=sendSomething
просто замените 192.168 .0.107 для вашего собственного IP (не пытайтесь 127.0.0.1, потому что он такой же, как localhost )

Чарли
источник
-4

Пожалуйста измените

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
источник
2
Зачем менять ? OP специально заявил, что служба работает локально.
Marquis of Lorne
Если вам нужно получить ресурс изображения с локального хоста, это не сработает.
Phuoc Huynh