Как подписаться на наблюдаемое из функции, которая возвращает наблюдаемое?

Итак, у меня есть метод put, который возвращает наблюдаемый, внутри этого метода мне нужно проверить, является ли токен действительным, если он недействителен, тогда мне нужно будет вызвать другой метод, который может создать новый токен обновления, и мне нужно подписаться к этому методу, чтобы я мог обновить значения локального токена обновления, а затем вернуть наблюдаемое для метода put.

Это то, что я до сих пор:

    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 ответа

Не беспокойтесь, чтобы удостовериться, что токен обновляется перед любым http-запросом на сервере, но используйте перехватчик Http, когда вы делаете запрос в наборе токенов сервера в заголовке запроса, и проверяйте его на стороне сервера, если токен истек или подпись недействительна или что-то еще не так, установите статус заголовка ответа 401 и используйте catchError, чтобы перехватить ошибку и проверить, имеет ли статус 401, затем вызовите метод обновления токена, который отправит запрос на сервер, чтобы обновить токен, используя switchMap, как показано ниже:

вот httpInterceptor

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);
                  }

              })
        );
  }
 }
} 

укажите http-перехватчик в app.module:

providers: [

       {
          provide:HTTP_INTERCEPTORS,

          useClass:HttpInterceptorService,

          multi:true

      }
]

Так что, если я правильно понял вашу проблему

  • ПОЛОЖИЛ
  • if (! токен) → 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$;
}

Намного чище и легче читать, чем с помощью операторов!

Другие вопросы по тегам