ReactiveX observable使用另一个observable

时间:2016-07-10 05:47:32

标签: typescript angular reactive-programming

我正在尝试编写一个Angular服务,该服务将包装常规Http服务,并在Bearer令牌不可用或无效时自动进行身份验证调用。

以下是通过该服务进行的常规GET调用的说明:

this.httpWithAutomagicAuth
    .get("http://localhost:5001/books")
    .map(res => res.json())
    .subscribe(
        data => {
            self.data = data;
        }
    );

这是我对这种服务非常草率的实施。显然,我没有在代码中使用正确的ReactiveX习语。

您可以很容易地注意到我正在尝试构建一个使用this.login()返回的另一个observable的observable(Observable.create)。我确信在这种情况下有一种更好的链接/嵌套可观察方式。

请建议改进​​代码:

  • 简洁
  • 易于阅读和理解

    @Injectable()
    export class HttpWebApiAuthService {
    
    constructor(private http: Http) { }
    
    // ...
    
    public get(url: string, options?: RequestOptionsArgs): Observable<Response> {
        // TODO this code is for test purpose only (basically, it required to enforce the jwtToken retrieval branch execution)
        // this.clearJwtToken();
    
        if (!this.getJwtToken()) {
            return Observable.create(
                (result: Observer<Response>) => {
                    this.login()
                        .map(res => res.json())
                        .subscribe(
                            data => {
                                this.saveJwtToken(data.id_token);
    
                                this.executeGet(url, options)
                                    .subscribe(authenticationResult => {
                                        result.next(authenticationResult);
                                    });
                            },
                            error => { console.error("Authentication error", error); },
                            () => { console.info("Authentication complete"); }
                        );
                    },
                    error => { console.error("OBSERVABLE error: ", error); },
                    () => { console.info("OBSERVABLE complete");
                }
            );
        } else {
            return this.executeGet(url, options);
        }
    }
    
    private login() : Observable<Response> {
        const authBody =
            {
                "client_id": "...",
                "username": "...",
                "password": "...",
                // ...
            };
        const headers = new Headers();
        headers.append("Content-Type", "application/json");
    
        return this.http.post("https://AUTH_URL", JSON.stringify(authBody), { headers: headers });
    }
    
    // ...
    
    }
    

1 个答案:

答案 0 :(得分:0)

让我们调用一个Observable,它发出一个任务的单个值,通常是一个异步任务。 让我们打破流程到更小的任务:

首先,您已经拥有返回login()的{​​{1}}任务。 让我们扩展它以保存jwtToken:

Observable<Response>

现在进入let loginAndSaveToken = login().map(res => res.json()) .do((data: any) => this.saveJwtToken(data.id_token)/*, onError, onComplete if neccessary*/); 功能。如果您还没有有效的令牌,则需要先登录并保存令牌,然后继续调用http.get,对吗?要get(url: string, options?: RequestOptionsArgs)您需要连接 Observable,或使用do this, then that。但是,如果您已经拥有有效令牌,则不必重新登录并返回concatMap。它相当于http.get,其中A是虚拟任务。我正在将虚拟任务实现为do A, then B

Observable.of(1)

现在,您的流程更加清晰。但它可能有一些警告,例如:   如果您的第二个public get(url: string, options?: RequestOptionsArgs): Observable<Response> { return (this.tokenValid() ? Observable.of(1) : loginAndSaveToken) .concatMap(x => this.executeGet(url, options)); } 呼叫在第一个成功登录之前发出,它将尝试再次登录。如果正在进行身份验证,请等待身份验证结果更好,特别是如果您的身份验证服务器配置为在时间窗口中拒绝来自特定用户的多个请求。