Поток ошибок в HttpUrlConnection - PullRequest
17 голосов
/ 20 мая 2011

Я хочу сделать запрос POST к сервлету HTTP, который я написал сам. Хороший случай (HTTP-код ответа 200) всегда работает нормально, используя метод URL.openConnection (). Но когда я получил желаемый код ответа об ошибке (например, 400), я подумал, что должен использовать HttpUrlConnection.getErrorStream (). Но объект ErrorStream является нулевым, хотя я отправляю данные обратно из сервлета в случае ошибки (я хочу оценить эти данные для генерации сообщений об ошибках). Вот как выглядит мой код:

HttpURLConnection con = null;
        try {
            //Generating request String
            String request = "request="+URLEncoder.encode(xmlGenerator.getStringFromDocument(xmlGenerator.generateConnectRequest(1234)),"UTF-8");
            //Receiving HttpUrlConnection (DoOutput = true; RequestMethod is set to "POST")
            con = openConnection();
            if (con != null){
                PrintWriter pw = new PrintWriter(con.getOutputStream());
                pw.println(request);
                pw.flush();
                pw.close();
                InputStream errorstream = con.getErrorStream();

                BufferedReader br = null;
                if (errorstream == null){
                    InputStream inputstream = con.getInputStream();
                    br = new BufferedReader(new InputStreamReader(inputstream));
                }else{
                    br = new BufferedReader(new InputStreamReader(errorstream));
                }
                String response = "";
                String nachricht;
                while ((nachricht = br.readLine()) != null){
                    response += nachricht;
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

Итак, мой вопрос: почему getErrorStream () возвращает null, хотя код состояния равен 400 (я вижу это в IOException, которое выдается при вызове con.getInputStream ())

Спасибо

Ответы [ 5 ]

19 голосов
/ 17 июля 2012

Из документации Java по getErrorStream ():

Возвращает поток ошибок, если соединение не удалось, но сервер тем не менее отправил полезные данные. Типичным примером является случай, когда HTTP-сервер отвечает 404, что вызывает исключение FileNotFoundException при подключении, но сервер отправляет HTML-страницу справки с предложениями относительно того, что делать. Этот метод не будет вызывать соединение. Если подключение не было подключено, или если на сервере не было ошибки при подключении, или если сервер имел ошибку, но данные об ошибках не были отправлены, этот метод вернет значение NULL. Это значение по умолчанию.

Так что, если вы не попали на сервер (например, неверный URL) или сервер ничего не отправил в ответе, getErrorStream () вернет значение null.

10 голосов
/ 23 сентября 2011
InputStream inputStream = null;     
try {
    inputStream = connection.getInputStream();
} catch(IOException exception) {
   inputStream = connection.getErrorStream();
}

Это похоже на то, что когда вы устанавливаете код состояния заголовка ответа как что-либо, превышающее 200, объект соединения сбрасывается.он будет генерировать SocketTimeoutException при получении входного потока, но когда он попадает в перехват, он все равно дает вам входной поток, что вы ожидаете.

8 голосов
/ 28 июля 2014

Немного покопавшись в коде JDK, я наконец-то нашел причину.HttpURLConnection # getErrorStream () возвращает null при получении 401 или 407, не потому, что реализация noop в абстрактном классе, а потому, что HttpURLConnection немедленно закрывает / очищает соединение, если он видит 401/407 в режиме потоковой передачи (то есть, POST).Посмотрите источник конкретной реализации HttpURLConnection: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/sun/net/www/protocol/http/HttpURLConnection.java#1079

Тем не менее, когда вы ловите IOException при вызове getInputStream (), соединение с сервером уже закрыто, а сокет подчеркивания очищен, так что вы бывсегда получать значение null при вызове getErrorStream ().

Другие варианты, которые многие предложили, это проверка кода состояния перед вызовом getInputStream или getErrorStream.Это не подходит для 401 и 407, поскольку внутренний errorStream устанавливается только при вызове getInputStream, т. Е. Это в основном копия inputStream при коде состояния! = 200. Но опять же, когда вы вызываете getInputStream, соединение будет закрыто.

3 голосов
/ 08 июня 2017

Поставьте оператор conn.setAllowUserInteraction(true); перед выполнением запроса, и соединение не будет закрыто, даже получив статус 401.

conn.setDoInput(true);
conn.setDoOutput(true);
conn.setAllowUserInteraction(true); //                <<<--- ### HERE
//do something
conn.connect();
boolean isError = (conn.getResponseCode() >= 400);
InputSteam is = isError ? con.getErrorStream() : con.getInputStream();
1 голос
/ 04 сентября 2018

Как указано в документации Android :

String responseString;
try {
    responseString = readInputStream(con.getInputStream());
} catch (final IOException e) {
    // This means that an error occurred, read the error from the ErrorStream
    try {
        responseString = readInputStream(con.getErrorStream());
    } catch (IOException e1) {
        throw new IllegalStateException("Unable to read error body.", e);
    }
}

private String readInputStream(final InputStream inputStream) throws IOException {
    final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    final StringBuilder responseString = new StringBuilder();
    String line;
    while ((line = bufferedReader.readLine()) != null) {
        responseString.append(line);
    }
    bufferedReader.close();
    return responseString.toString();
}
...