如何从返回observable的函数中订阅observable?

时间:2018-11-08 11:40:09

标签: angular typescript rxjs observable switchmap

因此,我有一个put方法返回一个可观察的值,在此方法中,我需要检查令牌是否有效,如果无效,则需要调用另一个可以创建新的刷新令牌的方法,需要订阅此方法,以便我可以更新本地刷新令牌的值,然后返回put方法的observable。

这是我到目前为止所拥有的:

    public putRequest(endpoint: string, body: any):
     Observable < APIResponseModel < any >> {
      if (this.authService.isValidToken()) {
       //     . . .
      }));
    }
    else {

     // get a new token 
     const refresh = this.authService.refreshToken();

     return refresh.switchMap(response => {

      this.authService.setRefreshToken(response.results.refreshToken);

      return this.httpClient.put < any > (`${endpoint}`, body).pipe(map(result => {
       this.hideLoader();
       return result;
      }));
     }).catch(err => {
      console.log("error occurred! " + err)
      this.authService.redirectToLogin();
      return this.getNullResponse();
     });

    }

AuthService方法:

  isValidToken(): boolean {
        const token = this.getAuthToken();
        if (!token && this.firstload) {

          return true; }
        if (token && !this.firstload) {
          if (this.jwtHelper.isTokenExpired(token)) { 
            console.log("Token is expired  ");

            return false;
          } else {
            return true;
          }
        } else {
          return false;
        }
      }

  refreshToken(): Observable<APIResponseModel<any>> {

    console.log("refresh token:" + this.getRefreshToken());
    const url = `${environment.baseAPIUrl}/${environment.version}/login/token/refresh`;
    const body = {
      refreshToken: `${this.getRefreshToken()}`
    };
    return this.httpClient.post(url, body).map((response: APIResponseModel<any>) => {
      this.setAuthToken(response.results.token);
      this.setRefreshToken(response.results.refreshToken);
      this.tokenBeingRefreshed = false;
      return response;
    }, err => err);
  }

请注意,我尝试使用SwitchMap和MergeMap,但是却收到服务器错误,表明会话已过期。似乎在等待生成新令牌之前遇到此错误。如何在调用httpClient.put之前确保创建了新令牌?

2 个答案:

答案 0 :(得分:0)

所以,如果我正确地收到您的问题,那就是

  • 放置
  • 如果(!token)→GET(令牌)
  • 重试PUT

在这种情况下,正确的流量应该是

getData() {
  return this.http.put(...);
}

getToken() {
  return this.http.get(...);
}

isValidToken() {
  return ...;
}

securedCall() {
  const data$ = this.isValidToken ? 
    this.getData() : 
    this.getToken().pipe(
      switchMap(token => this.getData())
    );

  return data$;
}

比使用运算符更清洁,更容易阅读!

答案 1 :(得分:0)

不必担心在服务器中的任何HTTP请求之前确保刷新令牌,但是在服务器中在请求标头中的设置令牌中进行请求并在服务器端检查令牌是否过期或签名是否正确时,请使用Http Interceptor。无效或其他错误设置响应标头状态401,并使用catchError捕获错误并检查状态是否为401,然后调用刷新令牌方法,该方法将使用switchMap在服务器中发送请求以刷新令牌,如下所示:

这是httpInterceptor

JOIN

在app.module中提供http拦截器:

export class HttpInterceptorService implements HttpInterceptor {

constructor(private auth : AuthService ) { }

isRefreshingToken: boolean = false;

tokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);

intercept(request: HttpRequest<any>, next: HttpHandler) : Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any> | any> {

  if( this.auth.token ) {

      if( this.isRefreshingToken ){ //  next handle when make request to refresh token .........

          return next.handle(this.setRequestHeaders( request ));

      }else{ // request and catch Error when token is expired or any else Error ...........

          return next.handle(this.setRequestHeaders( request ))
              .pipe(
                  catchError(err => { // catch response error from server
                      if (err instanceof HttpErrorResponse) {
                          switch ((<HttpErrorResponse>err).status) {
                              case 401: // if is 401 error
                                  return this.handle401Error(request, next);  // return handle401Error method
                          }
                      } else {
                          return throwError(err);
                      }
                  })
              );

      }

  }else{
      return next.handle(this.setRequestHeaders( request ))
  }

  private setRequestHeaders(request: HttpRequest<any> ) : HttpRequest<any> { // set request headers ......

     if( this.isRefreshingToken ){

         return request.clone({headers :request.headers.set('Refresh-Token',this.auth.refresh_token || '')});

     } 

     else if( this.auth.token ){

           return request.clone({headers :request.headers.set('Token', this.auth.token || '' ) });

     }
  }

  private handle401Error(request: HttpRequest<any>, next: HttpHandler) { // 401 error from server when token is expired ..

  if (!this.isRefreshingToken) {

      this.isRefreshingToken = true;
      // Reset here so that the following requests wait until the token
      // comes back from the refreshToken call.

      this.tokenSubject.next(null);

      return this.auth.refreshToken(clientSignature)  // make request to refresh token return false or new token and new refresh token

          .pipe(  // get result from refresh token ............................

              switchMap((result: any) => {

                  if ( result ) {

                      this.isRefreshingToken = false;

                      this.auth.refresh_tokens( result );

                      this.tokenSubject.next( result );

                      return next.handle(this.setRequestHeaders( request ) );
                  }

                  this.isRefreshingToken = false;

                  this.auth.logout();

                  this.tokenSubject.next(false);

                  return next.handle(request);
              }),
              catchError( err => {

                  this.isRefreshingToken = false;

                  this.auth.logout();

                  this.tokenSubject.next(false);

                  return next.handle(request);

              } ),

              finalize(() => {
                  this.isRefreshingToken = false;
              }),
          );

  } else {
      return this.tokenSubject
          .pipe(filter(token => token != null),
              take(1),
              switchMap( token => {

                  if( token ){

                      return next.handle(this.setRequestHeaders( request ));

                  }else{
                      return next.handle(request);
                  }

              })
        );
  }
 }
}