Как подписаться на наблюдаемое из функции, которая возвращает наблюдаемое? - PullRequest
0 голосов
/ 08 ноября 2018

Итак, у меня есть метод 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 ]

0 голосов
/ 08 ноября 2018

Не беспокойтесь, чтобы удостовериться, что токен обновляется перед любым 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

      }
]
0 голосов
/ 08 ноября 2018

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

  • PUT
  • 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$;
}

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

...