我以前做过类似的事情,但有 OAuth 授权。基本上,您有一个使用 RequestInterceptor 初始化的 RestAdapter,它将会话 cookie 添加到每个请求。每当会话被授权时,RequestInterceptor 都会获取一个新的会话 cookie。
以下示例代码中使用了以下 Retrofit REST 接口定义:
interface ApiService {
@GET("/examples/v1/example")
Observable<Example> getExample();
}
请求拦截器可以查看每个 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);
}
}
这是您提到的 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");
}
}
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));
}
}
下面的重试逻辑将使用 SessionService 更新会话 cookie,然后如果发送到服务器的会话 cookie 返回 HTTP 未授权 (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);
}
});
}
}
客户端初始化代码将如下所示:
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);
然后从 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.
}
});