2015-05-28 17 views
7

Ho un app che richiede di sessione (cookie) per elaborare le chiamate web. Sto usando Retrofit+RxJava. Tuttavia, la sessione potrebbe scadere (errore di retrofit con stato non autorizzato 401) e voglio riautenticarlo (per ottenere nuovi cookie) e riprovare la chiamata precedente in questo caso. Come lo farei con RxJava?Implementare retryWhen logica

Il mio esempio:

getServerApi().getDialogs(offset, getCookies()) 
    .subscribeOn(Schedulers.newThread()) 
    .observeOn(AndroidSchedulers.mainThread()) 
    .retryWhen(observable -> {...}) // Need some logic 
    .subscribe(dialogsEnvelope -> getView().setDialogs(dialogsEnvelope), 
       throwable -> getView().setError(processFail(throwable))); 
+0

Se la sessione scade ed è necessario riprovare, si fa a ottenere un qualche tipo di risposta del server che indica che (e g a JSON con ' "successo":.. False') o si solo un' RetrofitError'? –

+0

@ david.mihola, ha aggiornato la mia domanda. Ottengo 401 non autorizzati (ottenendo errore Retorfit). – localhost

+0

Penso di avere un'idea, ma mi piacerebbe provarlo prima di pubblicare ... –

risposta

4

di estremamente potente Interceptor Usa OkHttp.

public class RecoverInterceptor implements Interceptor { 
    String getAuth() { 
    // check if we have auth, if not, authorize 
    return "Bearer ..."; 
    } 

    void clearAuth() { 
    // clear everything 
    } 

    @Override public Response intercept(Chain chain) throws IOException { 
    final Request request = chain.request(); 
    if (request.urlString().startsWith("MY ENDPOINT")) { 
     final Request signed = request.newBuilder() 
      .header("Authorization", getAuth()) 
      .build(); 
     final Response response = chain.proceed(signed); 
     if (response.code() == 401) { 
     clearAuth(); 
     return intercept(chain); 
     } else { 
     return response; 
     } 
    } else { 
     return chain.proceed(request); 
    } 
    } 
} 

Ricordarsi di sincronizzare il codice di processo di autenticazione, in modo che due richieste simultanee non invocano allo stesso tempo.

8

Mentre un Interceptor può essere una soluzione migliore per questo particolare problema, la questione espressamente richiesto una soluzione che utilizza retryWhen, ecco è un modo per farlo:

retryWhen(new Func1<Observable<Throwable>, Observable<?>>(){ 

    @Override 
    public void Observable<?> call(Observable<Throwable>> attempts) { 
     return attempts.flatMap(new Func1<Throwable, Observable<?>>() { 

      @Override 
      public Observable<?> call(Throwable throwable) { 
       if (throwable instanceof RetrofitError) { 
        RetrofitError retrofitError = (RetrofitError) throwable; 
        if (retrofitError.getKind() == RetrofitError.Kind.HTTP && retrofitError.getResponse().getStatus() == 401) { 
         // this is the error we care about - to trigger a retry we need to emit anything other than onError or onCompleted 
         return Observable.just(new Object()); 
        } else { 
         // some other kind of error: just pass it along and don't retry 
         return Observable.error(throwable); 
        } 
       } else { 
        // some other kind of error: just pass it along and don't retry 
        return Observable.error(throwable); 
       } 
      } 
     }); 
    } 
}) 

Tuttavia, il vostro getCookies non sarebbe stato chiamato di nuovo nel caso di un semplice retry. Che sarebbe solo sottoscrivere nuovamente allo stesso Observable ma getCookies è stato chiamato prima della creazione di quel Observable. Quindi penso che dovresti concludere la creazione della fonte Observable in un defer.

+0

Grazie, cercherò! – localhost

+0

Anche in questo esempio 'retry' sarebbe OK ... Salva una flatMap. –

0

Durante la navigazione in Internet per scoprire una risposta adeguata - ho trovato this cool gist descrive come aggiornare OAuth token con l'aiuto di OkHttp Interceptor (simile alla risposta accettato, ma più completo).

Non ha nulla a che fare con RxJava, ma per me è più accettabile perché non ho per avvolgere ogni osservabile con retryWith logica - tutto è fatto a livello inferiore (OkHttp biblioteca).