Сообщение HttpURLConnection: поток вывода не влияет?

Я разрабатываю приложение для Android и уже обнаружил, что разные версии Android по-разному обрабатывают Http(s)URLConnections (http://stackoverflow.com/q/9556316/151682).

Я столкнулся с проблемой, что Android 4 хорошо выполняет запрос POST через HTTPS, автоматически добавляя заголовки, такие как Content-Type, при запуске кода ниже.

Однако на Android 2.3.5 (устройство и эмулятор) любая запись в выходной поток, по-видимому, игнорируется — я отладил его с помощью веб-прокси Charles, и хотя все заголовки отправляются, данные, записанные в выходной поток, не отправляются ...

Кто-нибудь знает, как это решить?

Примечание. Поскольку API, для которого я разрабатываю, имеет только самозаверяющий сертификат, мне нужно отключить проверку сертификации на данный момент.

Тиа, Патрик

Обновление Тем временем я также безрезультатно пытался выполнить следующие действия:

  • Вызов close() после вызова flush() в BufferedOutputStream
  • Вызов close() как для OutputStream, так и для BufferedOutputStream
  • Вместо этого используйте OutputStreamWriter
  • Не звонить close() до звонка getInputStream()

    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setConnectTimeout(CONNECT_TIMEOUT);
    
    connection.setDoOutput(true); // Triggers POST.
    connection.setRequestMethod("POST");
    int contentLength = 0;
    if(body != null) {
        contentLength = body.getBytes().length;
    }
    
    // Workarounds for older Android versions who do not do that automatically (2.3.5 for example)
    connection.setRequestProperty(HTTP.TARGET_HOST, url.getHost());
    connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
    
    // Set SSL Context -- Development only
    if(context != null && connection instanceof HttpsURLConnection){
        HttpsURLConnection conn = (HttpsURLConnection)connection;
        conn.setSSLSocketFactory(context.getSocketFactory());
        conn.setHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });
    }
    
    try{
        // Add headers
        if(headers != null){
            for (NameValuePair nvp : headers) {
                if(nvp != null){
                    connection.setRequestProperty(nvp.getName(), nvp.getValue());
                }
            }
        }
    
        connection.setFixedLengthStreamingMode(contentLength);
        OutputStream outputStream = null;
        try {
            if(body != null){
                outputStream = connection.getOutputStream();
                BufferedOutputStream stream = new BufferedOutputStream(outputStream);
                stream.write(body.getBytes()); // <<<< No effect ?!
                stream.flush();
    
            }
        } finally {
            if (outputStream != null) 
                try { 
                    outputStream.close(); 
                }
            catch (IOException logOrIgnore) {
                // ...
            }
        }
    
        InputStream inputStream = connection.getInputStream();
    
        // .... Normal case ....
    
    }
    catch(IOException e){
        // ... Exception! Check Error stream and the response code ...
    
    
    }
    finally{
        connection.disconnect();
    }
    

    }


person Patrick    schedule 07.04.2012    source источник
comment
Просто проверьте все базы, так как не весь ваш код показан. Вы проверили body != null и contentLength > 0? Вы действительно регистрируете/проверяете любые исключения, которые могут возникать в блоке try? Какой код ответа сервера?   -  person kaliatech    schedule 20.04.2012
comment
код ответа сервера - 400 - неверный запрос, я их регистрирую, но для краткости опустил их здесь... Он не работает в getInputStream. body проверяется на нуль перед записью в поток. ContentLength здесь явно не проверяется (спасибо за подсказку), но она соответствует переданному телу всякий раз, когда я его проверяю.   -  person Patrick    schedule 21.04.2012


Ответы (2)


Мне кажется, что порядок, в котором вы вызываете DoSetOutput и т. д., является причиной странного поведения...

Некоторые ссылки/источник с рабочим кодом для HTTP POST на Android:

person Yahia    schedule 14.04.2012
comment
Привет Яхия - спасибо за ваш вклад. Я догадался что-то вроде этого, но я не могу найти намека на то, что я неправильно упорядочиваю вызовы... API Doc не является явным в своем примере кода. В настоящее время я сравниваю его с образцами, которые вы предоставляете, но есть ли у вас интуиция, какой вызов может быть не в том месте? - person Patrick; 14.04.2012
comment
@ Патрик, добро пожаловать :-) Я не совсем уверен ... но я бы порекомендовал попробовать образцы на основе DefaultHttpClient ... - person Yahia; 14.04.2012
comment
Я действительно очень хотел использовать HttpURLConnection, так как их использование специально рекомендуется для новых приложений командой Android... (android-developers.blogspot.com/2011/09/) Но я бы избавился от многих хлопот с DefaultHttpClient, это правда;) - person Patrick; 14.04.2012
comment
@Patrick, в большинстве приведенных выше примеров используется HttpURLConnection, а некоторые даже могут использоваться в качестве замены копирования и вставки для вашего кода ... - person Yahia; 14.04.2012
comment
@Patrick в предоставленной вами ссылке прямо указано, что DefaultHttpClient имеет меньше ошибок ... - person Yahia; 14.04.2012
comment
... меньше ошибок в Eclair и Froyo. но я нацелен на Gingerbread, для которого они рекомендуют HttpURLConnection... ;) Я пытаюсь использовать образцы, но некоторые из них не используют параметры запроса, ни один из них не использует верификатор имени хоста. Так что довольно сложно понять, куда поставить это снова;) - person Patrick; 14.04.2012

Ах, я наконец нашел его. Ищу не в том месте... Проблема заключалась в заголовках:

Мой приведенный выше код на самом деле работает нормально - причина в том, что я включил заголовок, закодированный в Base64. На моем Android 2.3.5 указанная опция (Base64.DEFAULT), кажется, вставляет дополнительную новую строку в конце, что преждевременно завершает запрос, не давая мне времени отправить фактическое тело. На Android 4 значение по умолчанию, похоже, изменено на что-то вроде BASE64.NO_WRAP, как объяснено в сообщении, указанном ниже...

На самом деле на это уже был дан ответ здесь.

Спасибо за ваши старания.

person Patrick    schedule 21.04.2012