2012-07-31 5 views
14

Używam HttpUrlConnection do wysyłania żądań sieciowych z mojej aplikacji Android. Wszystko działa dobrze, z wyjątkiem jednego, 401. Kiedy serwer zwraca odpowiedź z kodem stanu 401, moja aplikacja wyrzuca IOException z komunikatem: "no authentication challenge found". Po przeszukaniu go, nie znalazłem żadnego rozwiązania, ale tylko obejście (posługiwanie się nim przy użyciu try/catch, przy założeniu, że jest to odpowiedź 401).Jak uzyskać odpowiedź 401 bez posługiwania się nią przy użyciu funkcji prób/catch w Androidzie

oto fragment kodu:

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; 
} 

naprawdę chcę wiedzieć, dlaczego to jest wyjątek? Co oznacza wyzwanie uwierzytelniania? Jak zapewnić wyzwanie uwierzytelnienia? jaka zmiana muszę wprowadzić w moim kodzie, aby przezwyciężyć tę sytuację?

Proszę mnie oświecić .. :)

+0

Przypuszczam, oznacza to, że serwer odpowiedział kodem błędu 401, ale pominięto 'nagłówek WWW-Authenticate' który opisuje wyzwania uwierzytelnienia. – Jens

+0

Serwer ma nagłówek 'WWW-Authenticate', ale wciąż dostaję ten błąd. – jtanveer

+0

Po prostu wpadłem na to, a także nie mogę wymyślić, jak uzyskać ten kod odpowiedzi z powrotem bez konieczności założenia, że ​​jest to 401 po złapaniu wyjątku IOException (ponieważ 'getResponseCode()' sam rzuci ten sam wyjątek IOException co 'connect()/getInputStream()/getOutputStream() '). Z pliku stacktrace wydaje się, że 'org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.processAuthHeader (HttpURLConnectionImpl.java:1153)' jest odpowiedzialny za wyrzucenie wyjątku w górę łańcucha (chociaż nie mogłem zanurkować dalej). –

Odpowiedz

12

IOException jest dość ogólny wyjątek, a nie można bezpiecznie założyć, kod 401 o stanie za każdym razem jest to wyjątek.

Jeśli pierwszy raz poprosisz o kod stanu, zdarzy się, że zawiera 401, HttpURLConnection spowoduje zgłoszenie wyjątku IOException. W tym momencie zmieni się wewnętrzny status połączenia, a teraz będzie można podać kod statusu bez żadnych błędów.

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) { 
    // ... 
} 

Więcej szczegółów http://www.tbray.org/ongoing/When/201x/2012/01/17/HttpURLConnection

+0

Dzięki za odpowiedź. Jak już powiedziałeś, wyjątek IOException jest bardzo ogólnym wyjątkiem. Może zostać rzucony na wszystko. z twojej odpowiedzi zakładam, że muszę napisać kolejny blok try/catch wewnątrz głównego bloku try/catch, aby mieć pewność, że wyjątek zostanie zgłoszony z powodu pobrania kodu odpowiedzi. W takim przypadku jest to ponownie obejście problemu! Ale odkąd minął rok i nie znalazłem żadnego rozwiązania, akceptuję twoją odpowiedź! – jtanveer

+0

Naprawiono problem z Androidem w wersji 4.2.2 i poniżej –

-2

spróbuje użyć 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; 
    } 
+2

Dzięki za odpowiedź. Ale naprawdę nie chcę używać 'HttpClient', ponieważ nie jest tak lekki. W przypadku przesyłania plików będę musiał użyć innej biblioteki strony trzeciej ('MultipartEntity'), która nie jest dostarczana z Androidem SDK. Muszę zachować mały rozmiar apk. Co więcej, Google sugeruje użycie 'HttpUrlConnection', ponieważ jest lekki i aktywnie pracuje nad tym. – jtanveer

Powiązane problemy