Retrofit/Rxjava和基于会话的服务

mid*_*ght 9 android rx-java retrofit

我正在实现基于会话的服务.所有请求都必须使用cookie会话参数进行订阅,然后使用单独的rest api检索该参数.因此,基本的工作流程是获取会话cookie并继续查询服务.有时cookie会过期,这会导致另一个会话cookie请求.

我正在尝试使客户端代码与会话无关,因此它不必担心维护会话,而是希望它隐藏在服务层内.

你能提出实施它的想法Retrofit/RxJava吗?我认为SessionService必须由所有其他服务封装,以便他们可以在需要时查询它,但我不知道如何使用RetrofitRestAdapter.create

kjo*_*nes 27

我之前做过类似的事情但是使用了OAuth授权.基本上,您有一个使用RequestInterceptor初始化的RestAdapter,它将会话cookie添加到每个请求.每当会话被授权时,RequestInterceptor都会获得一个新的会话cookie.

以下示例代码中使用了以下Retrofit REST接口定义:

interface ApiService {
    @GET("/examples/v1/example")
    Observable<Example> getExample();
}
Run Code Online (Sandbox Code Playgroud)

Request拦截器可以查看每个REST请求,并可以添加标头,查询参数或修改URL.此示例假定cookie已添加为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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是您提到的SessionService.它的职责是制作授权/刷新会话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");
    }
}
Run Code Online (Sandbox Code Playgroud)

RestService类包装Retrofit接口,以便可以将请求重试逻辑添加到每个Retrofit Observable.

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)); 
    }
}
Run Code Online (Sandbox Code Playgroud)

下面的重试逻辑将使用SessionService更新会话cookie,然后如果发送到服务器的会话cookie返回HTTP Unauthorized(401)错误,则重试失败的REST请求.

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);
                    }
                });
    }
}
Run Code Online (Sandbox Code Playgroud)

客户端初始化代码看起来类似于:

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);
Run Code Online (Sandbox Code Playgroud)

然后从RestService获取一个REST observable并订阅它以启动网络请求.

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.
            }
        });
Run Code Online (Sandbox Code Playgroud)