Angular2で始めたので、TのObservableを返すようにサービスを設定しました。サービスではmap()呼び出しがあり、これらのサービスを使用するコンポーネントはsubscribe()を使用して応答を待機します。これらの単純なシナリオでは、rxjsを実際に調べる必要はなかったので、すべて問題ありませんでした。
今、私は以下を達成したいと思います:リフレッシュトークンでOauth2認証を使用しています。他のすべてのサービスが使用するAPIサービスを構築し、401エラーが返されたときに更新トークンを透過的に処理します。そのため、401の場合、最初にOAuth2エンドポイントから新しいトークンを取得してから、新しいトークンでリクエストを再試行します。以下は、約束された状態で正常に動作するコードです。
request(url: string, request: RequestOptionsArgs): Promise<Response> {
var me = this;
request.headers = request.headers || new Headers();
var isSecureCall: boolean = true; //url.toLowerCase().startsWith('https://');
if (isSecureCall === true) {
me.authService.setAuthorizationHeader(request.headers);
}
request.headers.append('Content-Type', 'application/json');
request.headers.append('Accept', 'application/json');
return this.http.request(url, request).toPromise()
.catch(initialError => {
if (initialError && initialError.status === 401 && isSecureCall === true) {
// token might be expired, try to refresh token.
return me.authService.refreshAuthentication().then((authenticationResult:AuthenticationResult) => {
if (authenticationResult.IsAuthenticated == true) {
// retry with new token
me.authService.setAuthorizationHeader(request.headers);
return this.http.request(url, request).toPromise();
}
return <any>Promise.reject(initialError);
});
}
else {
return <any>Promise.reject(initialError);
}
});
}
上記のコードでは、authService.refreshAuthentication()は新しいトークンを取得してlocalStorageに保存します。 authService.setAuthorizationHeaderは、「Authorization」ヘッダーを以前に更新されたトークンに設定します。 catchメソッドを見ると、(リフレッシュトークンの)promiseが返され、順番に(実際の2回目のリクエストの)別のpromiseが返されることがわかります。
私は約束に頼らずにこれをやろうとしました:
request(url: string, request: RequestOptionsArgs): Observable<Response> {
var me = this;
request.headers = request.headers || new Headers();
var isSecureCall: boolean = true; //url.toLowerCase().startsWith('https://');
if (isSecureCall === true) {
me.authService.setAuthorizationHeader(request.headers);
}
request.headers.append('Content-Type', 'application/json');
request.headers.append('Accept', 'application/json');
return this.http.request(url, request)
.catch(initialError => {
if (initialError && initialError.status === 401 && isSecureCall === true) {
// token might be expired, try to refresh token
return me.authService.refreshAuthenticationObservable().map((authenticationResult:AuthenticationResult) => {
if (authenticationResult.IsAuthenticated == true) {
// retry with new token
me.authService.setAuthorizationHeader(request.headers);
return this.http.request(url, request);
}
return Observable.throw(initialError);
});
}
else {
return Observable.throw(initialError);
}
});
}
上記のコードは私が期待することをしません:200応答の場合、それは適切に応答を返します。ただし、401をキャッチすると、新しいトークンは正常に取得されますが、サブスクライブは最終的に応答ではなくオブザーバブルを取得します。これは、再試行を行う必要がある未実行のObservableであると推測しています。
約束の方法をrxjsライブラリに翻訳するのはおそらく最善の方法ではないことを理解していますが、「すべてがストリーム」であることを理解できていません。私は、flatmap、retryWhenなどを含む他のいくつかのソリューションを試してみましたが、うまくいきませんでした。
あなたのコードをざっと見てみると、あなたの問題は、あなたがObservable
サービスから返されたrefresh
を平坦化していないということだと思います。
catch
演算子は、ダウンストリームObservable
が違いを認識しないように、失敗したObservableの最後に連結するObserver
を返すことを期待しています。
401以外の場合、初期エラーを再スローするObservableを返すことにより、これを正しく実行しています。ただし、更新の場合は、Observable
を返すため、単一の値ではなくmoreObservables
が生成されます。
リフレッシュロジックを次のように変更することをお勧めします。
return me.authService
.refreshAuthenticationObservable()
//Use flatMap instead of map
.flatMap((authenticationResult:AuthenticationResult) => {
if (authenticationResult.IsAuthenticated == true) {
// retry with new token
me.authService.setAuthorizationHeader(request.headers);
return this.http.request(url, request);
}
return Observable.throw(initialError);
});
flatMap
は、中間のObservables
を単一のストリームに変換します。
RxJsの最新リリースでは、flatMap
演算子の名前がmergeMap
に変更されました。
これを作成して demo rxjsを使用して更新トークンを処理する方法を見つけました。これを行います:
このデモは、それが使用してそれらをシミュレートし(実際のHTTP呼び出しは行いませんObservable.create
)。
代わりに、catchError
およびretry
演算子を使用して問題を解決し(アクセストークンが最初に失敗した)、失敗した操作(API呼び出し)を再試行する方法を学習します。