2012-07-31 8 views
14

Sto usando HttpUrlConnection per fare richieste di rete dalla mia applicazione Android. Tutto funziona bene tranne una cosa, 401. Ogni volta che il server restituisce la risposta con il codice di stato 401, la mia app genera IOException con un messaggio che indica, "no authentication challenge found". Dopo averlo fatto su Google, non ho trovato una soluzione singola, ma solo una soluzione alternativa (gestirla usando try/catch, assumendo una risposta 401).Come ottenere 401 risposta senza gestirlo utilizzando try/catch in Android

Ecco il frammento di codice:

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

voglio davvero sapere, perché è gettato questa eccezione? Che cosa significa la verifica dell'autenticazione? Come fornire una sfida di autenticazione? che cambiamento devo fare nel mio codice per superare questa situazione?

Si prega di illuminarmi .. :)

+0

Direi significa che il server ha risposto con un codice di errore 401, ma omette l'intestazione 'WWW-Authenticate' che descrive la richiesta di autenticazione. Il server – Jens

+0

ha l'intestazione 'WWW-Authenticate', ma sto ancora ricevendo questo errore. – jtanveer

+0

Mi sono solo imbattuto in questo, e inoltre non riesco a capire come ottenere questo codice di risposta senza dover presumere che sia un 401 dopo aver catturato IOException (poiché 'getResponseCode()' stesso genererà la stessa IOException di 'connect()/getInputStream()/getOutputStream() '). Da stacktrace sembra 'org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.processAuthHeader (HttpURLConnectionImpl.java:1153)' è responsabile per lanciare l'eccezione sulla catena (anche se non ho potuto immergersi ulteriormente). –

risposta

12

IOException è piuttosto un'eccezione generale, e non si può tranquillamente supporre un codice di stato 401 ogni volta che viene gettato.

Se la prima volta che si richiede il codice di stato si verifica un 401, HttpURLConnection genererà un'eccezione IOException. A questo punto, lo stato interno della connessione sarà cambiato e ora sarà in grado di darti il ​​codice di stato, senza alcun errore.

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

Per maggiori dettagli, http://www.tbray.org/ongoing/When/201x/2012/01/17/HttpURLConnection

+0

Grazie per la risposta. Come hai detto, IOException è un'eccezione molto generale. Può essere lanciato per qualsiasi cosa. dalla tua risposta, suppongo di dover scrivere un altro blocco try/catch nel blocco try/catch principale per essere sicuro che l'eccezione venga generata a causa del recupero del codice di risposta. In tal caso, questa è di nuovo una soluzione! Ma dato che è stato un anno e non ho trovato nessuna soluzione, accetto la tua risposta! – jtanveer

+0

Questo problema è stato risolto con Android versione 4.2.2 e sotto –

-2

tenta di utilizzare 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

Grazie per la risposta. Ma davvero non voglio usare 'HttpClient' dato che non è molto leggero. In caso di caricamento di file, dovrò utilizzare un'altra libreria di terze parti ('MultipartEntity'), che non viene fornita con Android SDK. Ho bisogno di mantenere le dimensioni dell'apk piccole. Inoltre, Google suggerisce di utilizzare 'HttpUrlConnection' perché è leggero e ci sta lavorando attivamente. – jtanveer

Problemi correlati