Typescript 使用另一个可观察对象的ReactiveX可观察对象
我正在尝试编写一个Angular服务,它将包装常规Http服务,并在承载令牌不可用或无效时自动进行身份验证调用 以下是通过该服务拨打的常规GET电话的示例:Typescript 使用另一个可观察对象的ReactiveX可观察对象,typescript,angular,reactive-programming,Typescript,Angular,Reactive Programming,我正在尝试编写一个Angular服务,它将包装常规Http服务,并在承载令牌不可用或无效时自动进行身份验证调用 以下是通过该服务拨打的常规GET电话的示例: this.httpWithAutomagicAuth .get("http://localhost:5001/books") .map(res => res.json()) .subscribe( data => { self.data = data;
this.httpWithAutomagicAuth
.get("http://localhost:5001/books")
.map(res => res.json())
.subscribe(
data => {
self.data = data;
}
);
这是我对这类服务非常草率的实现。显然,我在代码中没有使用正确的ReactiveX习惯用法
您可以很容易地注意到,我正在尝试构建一个observable(observable.create),它使用this.login()返回的另一个observable。我确信有一种更好的方法可以对这种情况进行链接/嵌套观察。
请提出改进建议,使代码:
- 简洁
- 易读易懂
@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 }); } // ... }
@Injectable() 导出类HttpWebApiAuthService{ 构造函数(私有http:http){} // ... 公共获取(url:string,options?:RequestOptionsArgs):可观察{ //TODO此代码仅用于测试目的(基本上,强制执行jwtToken检索分支需要它) //这个.clearJwtToken(); 如果(!this.getJwtToken()){ 返回可观察的。创建( (结果:观察者)=>{ this.login() .map(res=>res.json()) .订阅( 数据=>{ 这个.saveJwtToken(data.id_token); this.executeGet(url、选项) .订阅(authenticationResult=>{ 结果。下一步(authenticationResult); }); }, error=>{console.error(“身份验证错误”,error);}, ()=>{console.info(“身份验证完成”);} ); }, error=>{console.error(“可观察到的错误:”,error);}, ()=>{console.info(“可观察到的完成”); } ); }否则{ 返回此.executeGet(url、选项); } } 私有登录():可观察{ 常量authBody= { “客户id”:“…”, “用户名”:“…”, “密码”:“…”, // ... }; 常量头=新头(); headers.append(“内容类型”、“应用程序/json”); 返回此.http.post(“https://AUTH_URL“,JSON.stringify(authBody),{headers:headers}); } // ... }
login()
任务,该任务返回Observable
。
让我们扩展它以保存jwtToken:
let loginAndSaveToken = login().map(res => res.json())
.do((data: any) => this.saveJwtToken(data.id_token)/*, onError, onComplete if neccessary*/);
现在继续执行get(url:string,options?:RequestOptionsArgs)
函数。如果您还没有有效的令牌,则需要先登录并保存令牌,然后继续调用http.get,是吗?为了做到这一点,您需要concat观测值,或者使用concatMap
。但是,如果您已经拥有一个有效的令牌,则不必重新登录,只需返回http.get
。这相当于执行A,然后执行B,其中A是一个虚拟任务。我将虚拟任务作为(1)中的可观察任务来实现
公共获取(url:string,options?:RequestOptionsArgs):可观察
{
return(this.tokenValid()?Observable.of(1):loginAndSaveToken)
.concatMap(x=>this.executeGet(url,选项));
}
现在你有了一个更干净的流程。但它可能有一些警告,例如:
如果您的第二个get
呼叫在第一个成功登录之前到达,它将再次尝试登录。如果正在进行验证,最好让它等待验证结果,特别是如果您的验证服务器配置为在一个时间窗口内拒绝来自特定用户的多个请求。让我们将发出单个值的可观察对象称为任务,通常是异步任务。
让我们将流程分解为更小的任务:
首先,您已经有了login()
任务,该任务返回Observable
。
让我们扩展它以保存jwtToken:
let loginAndSaveToken = login().map(res => res.json())
.do((data: any) => this.saveJwtToken(data.id_token)/*, onError, onComplete if neccessary*/);
现在继续执行get(url:string,options?:RequestOptionsArgs)
函数。如果您还没有有效的令牌,则需要先登录并保存令牌,然后继续调用http.get,是吗?为了做到这一点,您需要concat观测值,或者使用concatMap
。但是,如果您已经拥有一个有效的令牌,则不必重新登录,只需返回http.get
。这相当于执行A,然后执行B,其中A是一个虚拟任务。我将虚拟任务作为(1)中的可观察任务来实现
公共获取(url:string,options?:RequestOptionsArgs):可观察
{
return(this.tokenValid()?Observable.of(1):loginAndSaveToken)
.concatMap(x=>this.executeGet(url,选项));
}
现在你有了一个更干净的流程。但它可能有一些警告,例如:
如果您的第二个get
呼叫在第一个成功登录之前到达,它将再次尝试登录。如果正在进行身份验证,最好让它等待身份验证结果,特别是如果您的身份验证服务器配置为在一个时间窗口中拒绝来自特定用户的多个请求