Вопрос по android, httpurlconnection – Как получить ответ 401, не обрабатывая его, используя try / catch в Android

15

я используюHttpUrlConnection делать сетевые запросы из моего приложения для Android. Все работает нормально, кроме одной вещи, 401. Когда сервер возвращает ответ с кодом состояния 401, мое приложение выдаетIOException с сообщением о том,"no authentication challenge found", После поиска в Google, я не нашел единственного решения, но только обходной путь (обрабатывая его, используя try / catch, предполагая, что это ответ 401).

вот фрагмент кода:

public Bundle request(String action, Bundle params, String cookie) throws FileNotFoundException, MalformedURLException, SocketTimeoutException,
        IOException {

    OutputStream os;

    String url = baseUrl + action;
    Log.d(TAG, url);
    HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
    conn.setConnectTimeout(30 * 1000);
    conn.setReadTimeout(30 * 1000);
    conn.setRequestProperty("User-Agent", System.getProperties().getProperty("http.agent") + "AndroidNative");
    conn.setRequestMethod("POST");
    conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
    conn.setRequestProperty("Connection", "Keep-Alive");
    conn.setDoOutput(true);
    conn.setDoInput(true);
    if (cookie != null) {
        conn.setRequestProperty("Cookie", cookie);
    }

    if (params != null) {
        os = conn.getOutputStream();
        os.write(("--" + boundary + endLine).getBytes());
        os.write((encodePostBody(params, boundary)).getBytes());
        os.write((endLine + "--" + boundary + endLine).getBytes());
        uploadFile(params, os);
        os.flush();
        os.close();
    }

    conn.connect();

    Bundle response = new Bundle();
    try {
        response.putInt("response_code", conn.getResponseCode());
        Log.d(TAG, conn.getResponseCode() + "");
        response.putString("json_response", read(conn.getInputStream()));
        List<String> responseCookie = conn.getHeaderFields().get("set-cookie");
        // Log.d(TAG, responseCookie.get(responseCookie.size() - 1));
        response.putString("cookie", responseCookie.get(responseCookie.size() - 1));
    } catch (SocketTimeoutException e) {
        throw new SocketTimeoutException(e.getLocalizedMessage());
    } catch (FileNotFoundException e) {
        throw new FileNotFoundException(e.getLocalizedMessage());
    } catch (IOException e) {
        e.printStackTrace();
        response.putInt("response_code", HttpURLConnection.HTTP_UNAUTHORIZED);
        response.putString("json_response", read(conn.getErrorStream()));
    }

    // debug
    Map<String, List<String>> map = conn.getHeaderFields();
    for (String key : map.keySet()) {
        List<String> values = map.get(key);
        for (String value : values) {
            Log.d(key, value);
        }
    }

    conn.disconnect();

    return response;
}

Я действительно хочу знать, почему выбрасывается это исключение? Что означает проверка подлинности? Как обеспечить проверку подлинности? Какие изменения я должен сделать в своем коде, чтобы преодолеть эту ситуацию?

Пожалуйста, просветите меня .. :)

Эй спасибо! после установки неверного куки я получаю код ответа! Но сейчас это бросокFileNotFoundException и я не получаю содержимое ответа (я должен получать ответ JSON от сервера). Теперь, когда я звонюconn.getErrorStream()КидаетNullPointerException, Вы можете помочь мне с этим? О сообщении вы получаете другое сообщение, потому что ваш сервер не отправляетWWW-Authenticate заголовок с ответом. jamael
сервер получилWWW-Authenticate заголовок, но все же я получаю эту ошибку. jamael
Кроме того, сервер, к которому я подключаюсь, ожидает файл cookie сеанса вместе с моими http-запросами. Кажется, что пока я предоставляю один (даже не обязательно), я могу избежать IOException. Кроме того, мое сообщение об ошибке IOException немного отличается "Полученный запрос проверки подлинности равен NULL". Tony Chan
Я предполагаю, что это означает, что сервер ответил кодом ошибки 401, но пропустилWWW-Authenticate заголовок, который описывает запрос аутентификации. Jens
Я только что натолкнулся на это, и также не могу понять, как вернуть этот код ответа, просто не предполагая, что это 401 после перехвата IOException (так какgetResponseCode() сам выбросит то же IOException, что иconnect()/getInputStream()/getOutputStream()). Из стека трассировки похожеorg.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.processAuthHeader(HttpURLConnectionImpl.java:1153) отвечает за выдачу исключения по цепочке (хотя я больше не могу нырять). Tony Chan

Ваш Ответ

2   ответа
-2

попробуй использовать HttpClient

private void setCredentials(String login, String password) {
    if (login != null && password != null)
        httpClient.getCredentialsProvider().setCredentials(
                new AuthScope(URL_HOST, 80), new UsernamePasswordCredentials(login, password));

}



private InputStream execute(String url, String login, String password) {
        setCredentials(login, password);
        HttpGet get = new HttpGet(url);
        try {
            HttpResponse response = httpClient.execute(get);
            int code = response.getStatusLine().getStatusCode();
            if (code == HttpURLConnection.HTTP_OK) {
                InputStream stream = response.getEntity().getContent();
                return stream;
            } else {
                Log.e("TAG", "Wrong response code " + code + " for request " + get.getRequestLine().toString());
                return null;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
Спасибо за Ваш ответ. Но я действительно не хочу использоватьHttpClient так как его не так много, легкий вес. В случае загрузки файла мне придется использовать другую стороннюю библиотеку (MultipartEntity), который не поставляется с Android SDK. Мне нужно, чтобы размер apk был маленьким. Кроме того, Google предлагает использоватьHttpUrlConnection потому что это легкий и они активно работают над этим. jamael
13

IOException является довольно общим исключением, и вы не можете безопасно принимать код состояния 401 каждый раз, когда его выдают.

Если при первом запросе кода состояния он содержит 401, HttpURLConnection сгенерирует исключение IOException. На этом этапе внутренний статус соединения изменится, и теперь он сможет дать вам код состояния без каких-либо ошибок.

int status = 0;
try {
    status = conn.getResponseCode();
} catch (IOException e) {
    // HttpUrlConnection will throw an IOException if any 4XX
    // response is sent. If we request the status again, this
    // time the internal status will be properly set, and we'll be
    // able to retrieve it.
    status = conn.getResponseCode();
}
if (status == 401) {
    // ...
}

Больше подробностей,http://www.tbray.org/ongoing/When/201x/2012/01/17/HttpURLConnection

Это исправило проблему с Android версии 4.2.2 и ниже
Спасибо за Ваш ответ. Как вы сказали, IOException является очень общим исключением. Его можно бросить за что угодно. Исходя из вашего ответа, я предполагаю, что мне нужно написать еще один блок try / catch внутри основного блока try / catch, чтобы быть уверенным, что исключение выдается из-за получения кода ответа. В этом случае это снова обходной путь! Но с тех пор, как прошел год, и я не нашел никакого решения, я принимаю ваш ответ! jamael

Похожие вопросы