java.io.IOException: обнаружено не было

Я новичок в андроиде, и это мой первый проект на Android. Я борюсь с проблемой «проверки подлинности» более чем на один день. Я попробовал несколько вариантов, но никто из них не работал.

В принципе, я хочу вызвать REST API и получить ответ. Я уверен, что в API нет проблем, поскольку я использую один и тот же в другом приложении iOS.

Я передаю заголовок авторизации, но все же аутентификация не отображается. Я нашел несколько вопросов о stackoverflow, связанных с этим, но некоторые из них не работали, и некоторые из них не имеют смысла для меня.

Я получаю код статуса 401 . Я знаю, это означает, что никакая аутентификация не прошла или не прошла, тогда они ошибаются. Здесь, я уверен, мои прошедшие правильные.

Ниже мой код:

 try { url = new URL(baseUrl); } catch (MalformedURLException me) { Log.e(TAG, "URL could not be parsed. URL : " + baseUrl + ". Line : " + getLineNumber(), me); me.printStackTrace(); } try { urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.setRequestMethod(method); urlConnection.setConnectTimeout(TIMEOUT * 1000); urlConnection.setChunkedStreamingMode(0); // Set HTTP headers String authString = "username:password"; String base64Auth = Base64.encodeToString(authString.getBytes(), Base64.DEFAULT); urlConnection.setRequestProperty("Authorization", "Basic " + base64Auth); urlConnection.setRequestProperty("Accept", "application/json"); urlConnection.setRequestProperty("Content-type", "application/json"); if (method.equals("POST") || method.equals("PUT")) { // Set to true when posting data urlConnection.setDoOutput(true); // Write data to post to connection output stream OutputStream out = urlConnection.getOutputStream(); out.write(postParameters.getBytes("UTF-8")); } try { // Get response in = new BufferedInputStream(urlConnection.getInputStream()); } catch (IOException e) { Log.e(TAG, "Exception in getting connection input stream. in : " + in); e.printStackTrace(); } // Read the input stream that has response statusCode = urlConnection.getResponseCode(); Log.d(TAG, "Status code : " + statusCode); } catch (ProtocolException pe) { pe.printStackTrace(); } catch (IllegalStateException ie) { ie.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { urlConnection.disconnect(); } 

Посмотрите на скриншот logcat:

LogCat

Любая помощь будет оценена по достоинству. Спасибо.

Эта ошибка возникает из-за того, что сервер отправляет 401 (неавторизованный), но не дает заголовок WWW-Authenticate который является подсказкой для клиента, что делать дальше. Заголовок WWW-Authenticate сообщает клиенту, какой тип аутентификации необходим (либо Basic, либо Digest ). Это, вероятно, не очень полезно в безгласных http-клиентах, но именно так определяется HTTP 1.1 RFC . Ошибка возникает из-за того, что lib пытается проанализировать заголовок WWW-Authenticate но не может.

Из RFC:

(…) Ответ ДОЛЖЕН включать поле заголовка WWW-Authenticate (раздел 14.47), содержащее вызов, применимый к запрашиваемому ресурсу. (…)

Возможные решения, если вы можете изменить сервер:

  • Добавьте поддельный заголовок «WWW-Authenticate», например: WWW-Authenticate: Basic realm="fake" . Это просто обходное решение, а не решение, но оно должно работать и клиент http удовлетворен ( см. Здесь обсуждение того, что вы можете поместить в заголовок ). Но будьте осторожны, что некоторые клиенты-клиенты могут автоматически повторить запрос, результатом которого является несколько запросов (например, слишком часто увеличивает неверный счет входа в систему). Это наблюдалось с клиентом iOS http.
  • Как предложил громвчар в этом блоге, чтобы избежать автоматической реакции на вызов, как всплывающая форма входа в браузере, вы можете использовать нестандартный метод аутентификации, например: WWW-Authenticate: xBasic realm="fake" . Важным моментом является то, что realm должна быть включена.
  • Используйте код статуса HTTP 403 вместо 401 . Это семантика не то же самое и обычно при работе с логином 401 это правильный ответ ( см. Здесь подробное обсуждение ), но более безопасное решение с точки зрения совместимости.

Возможные решения, если вы не можете изменить сервер:

  • Как писал @ErikZ в своем посте, вы можете использовать try & catch

     HttpURLConnection connection = ...; try { // Will throw IOException if server responds with 401. connection.getResponseCode(); } catch (IOException e) { // Will return 401, because now connection has the correct internal state. int responsecode = connection.getResponseCode(); } 
  • Используйте другой http-клиент, например, OkHttp

Какую версию Android вы тестируете?

У меня возникли трудности с аутентификатором Android во время некоторых разработок на Gingerbread (я не знаю, ведет ли оно по-другому в более поздних версиях Android). Я использовал Fiddler2 для проверки HTTP-трафика между моим приложением и сервером, обнаружив, что аутентификатор не отправил строку аутентификации для каждого HTTP-запроса. Мне это нужно.

Вместо этого я обратился к этому:

 urlConnection.setRequestProperty("Authorization", "Basic " + Base64.encodeToString("userid:pwd".getBytes(), Base64.NO_WRAP )); 

Это вырезание и вставка из моего кода. Обратите внимание, что urlConnection является объектом HttpURLConnection.

У меня была та же проблема на устройствах, на которых был установлен pre-KitKat Android, но я использовал библиотеку Volley, поэтому исправление на стороне клиента, предоставленное @ for3st, не работало для меня, мне пришлось настроить его для Volley, вот оно, надеюсь, это помогает кому-то бороться с этой проблемой:

 HurlStack hurlStack = new HurlStack() { @Override public HttpResponse performRequest(final Request request, final Map additionalHeaders) throws IOException, AuthFailureError { try { return super.performRequest(request, additionalHeaders); } catch (IOException e) { return new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1), 401, e.getMessage()); } } }; Volley.newRequestQueue(context.getApplicationContext(), hurlStack); 

Таким образом возвращается ошибка 401, и ваша политика повтора может выполнять свою работу (например, маркер запроса … и т. Д.). Хотя IOException может быть вызвано какой-то другой проблемой, кроме 401, поэтому вы можете выбрать синтаксический анализ сообщения об исключении для ключевого слова Authorization и вернуть другой код ответа для других.

Имел ту же проблему на некоторых старых устройствах (например, Huawei Y330-U11). Правильный способ исправить это – исправить ее на стороне сервера, как указано в наиболее популярном ответе.

Однако, это действительно разочаровывает, что проблема возникает только на некоторых устройствах. И я считаю, что это происходит из-за различных реализаций «UrlConnection». Различные версии Android – разные реализации «UrlConnection».

Таким образом, вы можете исправить это, используя всюду «UrlConnection». Попробуйте использовать okhttp и okhttp-urlconnection.

Вот способ добавить эти библиотеки к вашей конструкции gradleиента:

 compile 'com.squareup.okhttp:okhttp:2.5.0' compile 'com.squareup.okhttp:okhttp-urlconnection:2.5.0' 

Он решил проблему для меня на этих устаревших устройствах. (Я должен был использовать OkClient для RetoFit RestAdapter)

PS Последние андроиды на момент написания статьи используют старую версию библиотеки OKHTTP внутри себя как «UrlConnection» (с обновленными именами пакетов), поэтому кажется, что это довольно солидная вещь

  • Как измерить прошедшее время
  • Сравнение строк - Android
  • Как правильно строить банки от IntelliJ?
  • Как разрешить пользователям проверять последнюю версию приложения внутри приложения?
  • Что находится внутри пакета com.sun?
  • Как использовать «Статические методы фабрики» вместо конструкторов?
  • Java-коллекция пар ценностей? (кортежи?)
  • Как программно заблокировать экран в Android?
  • Максимальный предел примитивного типа Java Long
  • Добавить строки из-за использования общей ошибки 'extends' вызывает ошибку компилятора
  • Как создать classы Java из файла WSDL
  • Давайте будем гением компьютера.