10

Angular2 の HTTP クラスで認証失敗 (401) を返すことができるサーバーを呼び出したいと考えています。

リクエストの流れは次のようになります。

  • ユーザーは myService.getSomething().subscribe() を使用してサーバーにリクエストを送信します
  • サーバーが 401 を返す場合: モーダル ウィンドウを開き、ユーザーに資格情報を要求します。
  • ユーザーはアプリケーションに正常に再ログインします
  • モーダルが閉じてコールバックを実行します
  • コールバックは、最初のリクエスト (myService.getSomething().subscribe()) を再試行する必要があります。

これが私が今のところ持っているものです:

export class MyService {
    // ...
    public getSomething(): Observable<Response> {
        return this.http.get(url, options).catch((res: any, ob: any) => this.errorHandler(res, ob));
    }

    public errorHandler(res: Response, ob: Observable<any>): Observable<Response> {
        if (res.status === 401) {
            this.modalService.open(new ModalConfig({
                content: LoginModalComponent,
                close: () => { ob.retry(1); console.log("weow") } // <=close is the callback that should initiate the retry action on the initial request.
            }));
        }
        else {
            return Observable.throw(res.json());
        }
    }
}

doSomething() は次のように使用されます。doSomething().map((r) => r.json()).subscribe((r) => ....)

更新 1

@Thierry Templierのソリューションのようにコードを変更しました。

private errorHandler(res: Response, ob: Observable<any>): Observable<Response> {
    if (res.status === 401) {
        let closedSubject = new Subject();
        this.modalService.open(new ModalConfig({
            content: LoginModalComponent,
            close: () => { closedSubject.next(res);} // I also tried .complete(), .next(null), .next(true), .next(false)
        }));
        return ob.retryWhen(() => closedSubject);
    }
    else {
        return Observable.throw(res.json());
    }
}

悲しいことに、それはまだ機能しません。retryWhen はすぐに実行され、closedSubject.next() が呼び出されるのを待ちません。したがって、無限ループを開始し、元の Observable (getSomething() 関数) をスパムします。

更新 2

無限ループを示すためにプランカーを作成しました。

https://plnkr.co/edit/8SzmZlRHvi00OIdA7Bga

警告: plunker を実行すると、コンソールに文字列 'test' がスパムとして送信されます

アップデート 3

ティエリーの正解に続いて、ソースフィールドは保護されているため、使用しない方法を見つけようとしました。フィールドを公開するよう rxjs の問題トラッカーに依頼した後、寄稿者はより良い解決策を返信しました。

public get(url: string, options?: RequestOptionsArgs): Observable<Response> {
    return super.get(url, options).retryWhen((errors: any) => this.errorHandler(errors));
}
private errorHandler(errors): any {
    return errors.switchMap((err) => {
        if (err.status === 401) {
            let closedSubject = new Subject();
            this.modalService.open(new ModalConfig({
                content: LoginModalComponent,
                close: () => { closedSubject.next(err); }
            }));
            return <any>closedSubject;
        }
        else {
            return Observable.throw(err.json());
        }
    });
}

私は .catch を使用しないので、ソース フィールドを使用する必要はありません。

4

3 に答える 3