2014-08-28 19 views
9

Wdrażam usługi sesyjne. Wszystkie żądania muszą być subskrybowane z parametrem sesji cookie, który z kolei jest pobierany z osobnym interfejsem do odpoczynku. Tak więc podstawowym przepływem pracy byłoby pobranie pliku cookie sesji i kontynuowanie sprawdzania usług. Czasami plik cookie wygasał i prowadził do innego żądania cookie dotyczącego sesji.Retrofit/Rxjava i usługi oparte na sesji

Próbuję uczynić kod klienta nieobsługiwanym przez sesję, aby nie musiał martwić się utrzymywaniem sesji, ale chciałbym, aby była ukryta w warstwie usług.

Czy możesz zasugerować pomysły na wdrożenie go pod numerem Retrofit/RxJava? Myślę, że SessionService musi być zamknięty przez wszystkich innych usług, tak aby mogli go zapytać, gdy jest to wymagane, ale nie jestem pewien jak to zrobić z modernizacją na RestAdapter.create

Odpowiedz

25

Robiłem coś podobnego do tego wcześniej, ale z autoryzacją OAuth. Zasadniczo masz Restartener zainicjalizowany za pomocą obiektu RequestInterceptor, który dodaje plik cookie sesji do każdego żądania. RequestInterceptor pobiera nowy plik cookie sesji za każdym razem, gdy sesja jest autoryzowana.

Poniższa definicja interfejsu Modernizacja reszta jest używany w przykładowym kodzie poniżej:

interface ApiService { 
    @GET("/examples/v1/example") 
    Observable<Example> getExample(); 
} 

Żądanie przechwytujących dostaje okiem na każde żądanie REST i można dodać nagłówki, params zapytań lub można zmodyfikować URL. W tym przykładzie założono, że plik cookie jest dodawany jako nagłówek HTTP.

class CookieHeaderProvider implements RequestInterceptor { 
    private String sessionCookie = ""; 

    public CookieHeaderProvider() { 
    } 

    public void setSesstionCookie(String sessionCookie) { 
     this.sessionCookie = sessionCookie; 
    } 

    @Override 
    public void intercept(RequestFacade requestFacade) { 
     requestFacade.addHeader("Set-Cookie", sessionCookie); 
    } 
} 

To jest usługa SessionServices, o której mówiliście. Odpowiedzialnością jest wykonanie żądania sieciowego, które autoryzuje/odświeża sesyjny plik cookie.

class SessionService { 
    // Modify contructor params to pass in anything needed 
    // to get the session cookie. 
    SessionService(...) { 
    } 

    public Observable<String> observeSessionCookie(...) { 
     // Modify to return an Observable that when subscribed to 
     // will make the network request to get the session cookie. 
     return Observable.just("Fake Session Cookie"); 
    } 
} 

Klasa RestService owija modyfikacyjne interfejs logiczny, tak że żądanie ponownej próby mogą być dodawane do każdego Retrofit widoczne.

class RestService { 
    private final apiService; 
    private final sessionSerivce; 
    private final cookieHeaderProvider; 

    RestService(ApiService apiService, 
       SessionService sessionSerivce, 
       CookieHeaderProvider cookieHeaderProvider) { 
     this.apiService = apiService; 
     this.sessionSerivce = sessionSerivce; 
     this.cookieHeaderProvider = cookieHeaderProvider; 
    } 

    Observable<Example> observeExamples() { 
     // Return a Retrofit Observable modified with 
     // session retry logic. 
     return 
      apiService 
       .observeExamples() 
       .retryWhen(new RetryWithSessionRefresh(sessionSerivce, cookieHeaderProvider)); 
    } 
} 

retry logika poniżej użyje SessionService zaktualizować cookie sesji i ponownie nieudane żądania odpoczynku, jeżeli ciasteczka sesji wysyłane do serwera HTTP zwraca Nieuprawnione (401) o błędzie.

public class RetryWithSessionRefresh implements 
     Func1<Observable<? extends Throwable>, Observable<?>> { 

    private final SessionService sessionSerivce; 
    private final CookieHeaderProvider cookieHeaderProvider; 

    public RetryWithSessionRefresh(SessionService sessionSerivce, 
            CookieHeaderProvider cookieHeaderProvider) { 
     this.sessionSerivce = sessionSerivce; 
     this.cookieHeaderProvider = cookieHeaderProvider; 
    } 

    @Override 
    public Observable<?> call(Observable<? extends Throwable> attempts) { 
     return attempts 
       .flatMap(new Func1<Throwable, Observable<?>>() { 
        public int retryCount = 0; 

        @Override 
        public Observable<?> call(final Throwable throwable) { 
         // Modify retry conditions to suit your needs. The following 
         // will retry 1 time if the error returned was an 
         // HTTP Unauthoried (401) response. 
         retryCount++; 
         if (retryCount <= 1 && throwable instanceof RetrofitError) { 
          final RetrofitError retrofitError = (RetrofitError) throwable; 
          if (!retrofitError.isNetworkError() 
            && retrofitError.getResponse().getStatus() == HttpStatus.SC_UNAUTHORIZED) { 
           return sessionSerivce 
             .observeSessionCookie() 
             .doOnNext(new Action1<String>() { 
              @Override 
              public void call(String sessionCookie) { 
               // Update session cookie so that next 
               // retrofit request will use it. 
               cookieHeaderProvider.setSesstionCookie(sessionCookie); 
              } 
             }) 
             .doOnError(new Action1<Throwable>() { 
              @Override 
              public void call(Throwable throwable) { 
               // Clear session cookie on error. 
               cookieHeaderProvider.setSesstionCookie(""); 
              } 
             }); 
          } 
         } 
         // No more retries. Pass the original 
         // Retrofit error through. 
         return Observable.error(throwable); 
        } 
       }); 
    } 
} 

Client kod inicjalizacji będzie wyglądać podobnie do tego:

CookieHeaderProvider cookieHeaderProvider = new CookieHeaderProvider(); 
SessionService sessionSerivce = new SessionService(); 

ApiService apiService = 
    new RestAdapter.Builder() 
     .setEndpoint(...) 
     .setClient(...) 
     .setRequestInterceptor(cookieHeaderProvider) 
     .build() 
     .create(ApiService.class); 

RestService restService = 
    new RestService(apiService, sessionSerivce, cookieHeaderProvider); 

Następnie dostać odpocząć zaobserwowania z RestService i zapisać się do niej skopać żądanie sieciowe.

Observable<Example> exampleObservable = 
    restService 
     .observeExamples(); 

Subsctiption subscription = 
    exampleObservable 
     .subscribe(new Observer<Example>() { 
      void onNext(Example example) { 
       // Do stuff with example 
      } 
      void onCompleted() { 
       // All done. 
      } 
      void onError(Throwalbe e) { 
       // All API errors will end up here. 
      } 
     }); 
+0

Wygląda całkiem nieźle. Dzięki! – midnight

+0

error: niezgodne typy: RetryWithSessionRefresh nie można przekonwertować na Func1 ,? extends Observable > faktycznie działa tylko z subversion RxJava z netflix – desgraci

+0

@desgraci Interfejs API retryWhen() został zmieniony w RxJava 1.0. Zaktualizowałem odpowiedź na zgodność z wersją 1.0+. – kjones

Powiązane problemy