java.io.IOException: обнаружено не было
Я новичок в андроиде, и это мой первый проект на Android. Я борюсь с проблемой «проверки подлинности» более чем на один день. Я попробовал несколько вариантов, но никто из них не работал.
В принципе, я хочу вызвать REST API и получить ответ. Я уверен, что в API нет проблем, поскольку я использую один и тот же в другом приложении iOS.
Я передаю заголовок авторизации, но все же аутентификация не отображается. Я нашел несколько вопросов о stackoverflow, связанных с этим, но некоторые из них не работали, и некоторые из них не имеют смысла для меня.
- Шифрование видеофайлов?
- Создание видеофайла с изображений с помощью ffmpeg
- ksoap2 org.xmlpull.v1.xmlpullparserexception Ожидаемая ошибка start_tag
- Динамические имена переменных Java
- Проблемы с https (нет сертификата партнера) в android
Я получаю код статуса 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:
Любая помощь будет оценена по достоинству. Спасибо.
- Как использовать интерфейс для связи между двумя действиями
- Android Studio - установка JVM не найдена
- Изменение языка ввода клавиатуры Programmatically
- Map.clear () vs new Map: Какой из них будет лучше?
- Внутри OnClickListener я не могу получить доступ ко многим вещам - как подойти?
- Android - предотrotation перезагрузки WebView при повороте
- Измените значки отмеченных и непроверенных для флажка для Android
- Как увеличить размер кучи JVM
Эта ошибка возникает из-за того, что сервер отправляет 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» (с обновленными именами пакетов), поэтому кажется, что это довольно солидная вещь