【问题标题】:delay observable to return value until i get result from my HTTP request in angular 5延迟可观察到返回值,直到我从我的 HTTP 请求中以 Angular 5 获得结果
【发布时间】:2019-03-24 23:25:49
【问题描述】:

我想延迟 observable 返回值,直到我从订阅中的 HTTP 请求中获得 new_token。我也在使用延迟时间,但我无法成功。

错误:返回未定义的值

预期:从服务器返回的 new_token

  refreshToken(): Observable<string> {

    const token_refreshed = localStorage.getItem("refresh_token");
  
    let new_token: string;
    if (token_refreshed) {
        console.log("this refreshed token" + token_refreshed);

        const headers = new HttpHeaders({
            'Authorization': "Basic " + btoa("clientId:client-secret"),
            'Content-Type': 'application/x-www-form-urlencoded',
            'grant_type': 'refresh_token',
            'refresh_token': token_refreshed
        });
        var creds = "grant_type=refresh_token" + "&credentials=true" + "&refresh_token=" + token_refreshed;

        this.httplclient.post<UserToken>('/api/oauth/token', creds, { headers: headers })
            .subscribe(response => {
               

                localStorage.setItem('access_token', response.access_token);
             
                new_token = response.access_token;
               
            }, err => {
                console.log("User authentication failed!");
            });
    }
    console.log('i am returning');
    return Observable.of(new_token).delay(3000);
}

更新:正在消耗 refresh_token 的方法,我正在使用拦截器,所以下面是 401 方法

handle401Error(req: HttpRequest<any>, next: HttpHandler) {
    if (!this.isRefreshingToken) {
        this.isRefreshingToken = true;
        console.log('I am handler 401');
        // Reset here so that the following requests wait until the token
        // comes back from the refreshToken call.
        this.tokenSubject.next(null);

        return this.authService.refreshToken()
            .switchMap((newToken: string) => {
                console.log('map token' + newToken);
//I'm getting null new token here from authService.refreshToken()           
              if (newToken) {
                    this.tokenSubject.next(newToken);


                    return next.handle(this.addToken(req, newToken));

                }

                return this.logoutUser();
            })
            .catch(error => {

                console.log('bad news its catch');
                return this.logoutUser();
            })
            .finally(() => {
                this.isRefreshingToken = false;
            });
    } else {
        return this.tokenSubject
            .filter(token => token != null)
            .take(1)
            .switchMap(token => {
                console.log('i am switch map else ');
                return next.handle(this.addToken(req, token));
            });
    }
}

【问题讨论】:

    标签: angular rxjs observable angular5 angular-http-interceptors


    【解决方案1】:

    不要订阅post 请求,如果您想执行一些副作用,请改用do 运算符。

    if (token_refreshed) {
      return this.httplclient.post(...)
        .do(response => {
          localStorage...
        })
        .map(response => ...) // map the response to return only the new token?
        .delay(3000); // or maybe you don't need this?
    }
    return Observable.of(new_token).delay(3000);
    

    【讨论】:

    • (我认为delay 是 OP 拼命修复它的尝试,实际上不应该存在)
    • 感谢 @martin 和 lngoBurk 是的,我不想延迟操作员,这可以更改,有时服务器可能需要更多时间,那么更好的解决方案是什么?我怎样才能在没有时间提及或延迟等的情况下实现
    【解决方案2】:

    您不需要延迟。只要值可用就传递它,因为您不能确定该值在 3000 毫秒后是否可用。

      refreshToken(): Observable<string> {
        const tokenObsr = new Subject<string>();
        const token_refreshed = localStorage.getItem("refresh_token");
    
        if (token_refreshed) {
            console.log("this refreshed token" + token_refreshed);
    
            const headers = new HttpHeaders({
                'Authorization': "Basic " + btoa("clientId:client-secret"),
                'Content-Type': 'application/x-www-form-urlencoded',
                'grant_type': 'refresh_token',
                'refresh_token': token_refreshed
            });
            var creds = "grant_type=refresh_token" + "&credentials=true" + "&refresh_token=" + token_refreshed;
    
            this.httplclient.post<UserToken>('/api/oauth/token', creds, { headers: headers })
                .subscribe(response => {
    
    
                    localStorage.setItem('access_token', response.access_token);
    
                    tokenObsr.next(response.access_token);
    
                }, err => {
                    console.log("User authentication failed!");
                });
        }
        console.log('i am returning');
        return tokenObsr.asObservable();
    }
    

    更新:在深入研究了您的代码后,我进行了必要的更改。 BehavoiurSubjectSubject。尝试使用一次。

    【讨论】:

    • 它不起作用 :) 在新令牌到达之前返回 null
    • 你能提供你如何订阅它的代码吗?此代码将首先发送null,但随后将提供新值,因为 ASA 可用。
    • 我已经更新了添加该方法的问题。 PS:我已经拿到了 token 但是在 null 之后,所以我想在 token 到达后返回 not null 首先
    • 您好,我已经更新了代码。请让我知道这是否有效。
    • 进行了更改。是的,它完全可以使用。您也可以将答案标记为已接受。会很有帮助:D
    【解决方案3】:

    如果我正确理解您想要实现的目标,我认为您应该重新组织您的代码,以便能够利用 RxJs 的 Obsersable 模式。

    这是我的建议

    refreshToken(): Observable<string> {
    
        const token_refreshed = localStorage.getItem("refresh_token");
    
        let new_token: string;
        if (token_refreshed) {
            console.log("this refreshed token" + token_refreshed);
    
            const headers = new HttpHeaders({
                'Authorization': "Basic " + btoa("clientId:client-secret"),
                'Content-Type': 'application/x-www-form-urlencoded',
                'grant_type': 'refresh_token',
                'refresh_token': token_refreshed
            });
            var creds = "grant_type=refresh_token" + "&credentials=true" + "&refresh_token=" + token_refreshed;
    
            return this.httplclient.post<UserToken>('/api/oauth/token', creds, { headers: headers })
    // Do not subscribe here - rather chain operators to transform the Observable returned by http.post into what you really want to emit and return the Observable transformed
               .map(response => response.access_token)
               .do(token => localStorage.setItem('access_token', response.token))
               .do(token => console.log('I am returning', token)
    
    }
    

    如果你这样做,那么任何使用 refreshToken() 方法的人都必须订阅返回的 Observable 并在那里管理结果,例如

    this.tokenService.refreshToken()
    .subscribe(
       token => {// do something with the token, maybe move here the localStore.setItem logic},
       err => {// handle the error condition}
    )
    

    【讨论】:

    • 我通过添加消费方法更新了我的问题
    • 我明白了。我认为我的建议仍然应该为您提供所需的内容。
    【解决方案4】:

    如果您必须等待令牌返回,为什么不使用 Promise 而不是 Observable?

    this.httplclient.post<UserToken>('/api/oauth/token', creds, { headers: headers }).toPromise().then((t: UserToken) => { 
       localStorage.setItem('access_token', t.access_token);
       new_token = t.access_token;
    }).catch(() => {
       console.log("User authentication failed!");
    });
    

    如果您想在其他地方订阅更改,请使用主题。

    // in your constructor
    this.sub = new Subject();
    
    // in your method to get the token
    this.httplclient.post<UserToken>('/api/oauth/token', creds, { headers: headers }).toPromise().then((t: UserToken) => { 
       localStorage.setItem('access_token', t.access_token);
       new_token = t.access_token;
       this.sub.next(new_token);
    }).catch(() => {
       console.log("User authentication failed!");
    });
    
    // getter for the subject
    // use asObservable so other components can't use next(..) to push data
    get tokenSub(): Observable<String> {
        return this.sub.asObservable();
    }
    
    // somewhere else
    this.yourServer.tokenSub.subscribe((token: String) => {
        ....
    }
    

    【讨论】:

    • 我不想在 Angular 5 中使用 Promise :)
    • 为什么不呢?您也可以订阅 post 调用,然后在主题不为空的情况下发出带有主题的令牌,这也可以,但如果您在继续之前需要一个值,我看不出为什么不使用承诺的理由。跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-30
    相关资源
    最近更新 更多