Повторно отправьте действие, если оно не авторизовано после refre sh -ken и получите новый токен доступа angular ngrx-store - PullRequest
0 голосов
/ 31 января 2020

теперь у меня есть хранилище с несколькими действиями, любая отправка действий может быть получена 401 неавторизованным, так как срок действия токена истек, теперь я обновляю токен sh и получаю свой новый токен доступа, но теперь мне нужно снова перезагрузить действия при сбое

мой перехватчик работает, но он вызовет запрос сбоя, а не действие

import { RefreshTokenModel } from './../../models/auth/other/refresh.token.model';
import { getRefreshToken } from './../../modules/auth/_selectors/auth.selectors';
import { AppState } from './../../modules/reducers/index';
import { ConstantsStrings } from './../../../dasaplanet/configurations/constants/constant';
import { TranslateService } from '@ngx-translate/core';
import { RefreshTokenRequested } from './../../modules/auth/_actions/auth.actions';
import { Injectable } from '@angular/core';
import {
    HttpEvent,
    HttpInterceptor,
    HttpHandler,
    HttpRequest,
    HttpResponse,
    HttpErrorResponse
} from '@angular/common/http';
import { Observable, throwError, BehaviorSubject, Subject, of } from 'rxjs';
import { catchError, map, tap, filter, take, switchMap, skipWhile, skip, finalize, flatMap, retryWhen, delay } from 'rxjs/operators';
import { Store, select } from '@ngrx/store';
import { ToastService } from './toast.service';
import { Router } from '@angular/router';
import { StatusCodes } from '../../enums/error.status.codes.enum';
import { UserLocalStorageService } from '../modules/partner/user.local.storage.service';
import { RouteStrings } from '../../configurations/routes/route';

@Injectable()
export class InterceptService implements HttpInterceptor {
    connected: boolean = false;
    subject: Subject<any> = new Subject();
    // for Refresh Token
    private isRefreshing = false;
    private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
    constructor(
        private toastService: ToastService,
        private translate: TranslateService,
        private _store: Store<AppState>,
        private userLocalStorage: UserLocalStorageService,
        private route: Router
    ) {
    }
    intercept(
        request: HttpRequest<any>,
        next: HttpHandler
    ): Observable<HttpEvent<any>> {
        let token: string = this.userLocalStorage.getToken();
        if (token) {
            request = request.clone({
                headers: request.headers.set('Authorization', 'Bearer ' + token)
            });
        }
        if (!token) {
            request = request.clone({
                headers: request.headers.set('Accept-Language', localStorage.getItem('language'))
            });
        }
        if (!request.headers.has('Content-Type') && !request.headers.has('form-data')) {
            request = request.clone({
                headers: request.headers.set('Content-Type', 'application/json')
            });
        }
        if (!request.headers.has('Accept') && !request.headers.has('form-data')) {
            request = request.clone({
                headers: request.headers.set('Accept', 'application/json')
            });
        }

        return next.handle(request).pipe(
            tap((event: HttpEvent<any>) => {
                if (event instanceof HttpResponse) {
                    if (event.status === StatusCodes.SUCCESS) {
                    }
                }
                return event;
            }),
            catchError((errors: HttpErrorResponse) => {
                if (errors.status === StatusCodes.BADREQUEST) {
                    if (RouteStrings.apiRoutes.ValidationErrors in errors.error && errors.error.message === ConstantsStrings.ValidationErrors)
                        this.toastService.showError("toast-top-right", errors.error.validationErrors[0]);
                    else
                        if (errors.error.message === RouteStrings.apiRoutes.ObjectError)
                            this.toastService.showError("toast-top-right", this.translate.instant('TRANSLATOR.THEREANERROR'));
                    if (errors.error.message === RouteStrings.apiRoutes.ForiegnKeyError)
                        this.toastService.showError("toast-top-right", this.translate.instant('TRANSLATOR.THEREANERROR'));
                    else
                        this.toastService.showError("toast-top-right", errors.error.message);
                }
                if (errors.status === StatusCodes.UNAUTHORIZED) {
                    // calling refresh token api and if got success extracting token from response and calling failed api due to 401
                    if (errors.headers.has(ConstantsStrings.TokenExpired)) {
                        if (!this.isRefreshing) {
                            this.isRefreshing = true;
                            this.refreshTokenSubject.next(null);
                            const data = new RefreshTokenModel();
                            data.accessToken = this.userLocalStorage.getToken();
                            data.refreshToken = this.userLocalStorage.getRefreshToken();
                            this._store.dispatch(new RefreshTokenRequested({ refreshTokenData: data }));
                            this._store.select(getRefreshToken).pipe(skipWhile(a => a.accessToken === undefined
                                && a.refreshToken === undefined), switchMap(tokens => {
                                    this.refreshTokenSubject.next(tokens.accessToken);
                                    window.location.href = this.route.url;
                                    return next.handle(this.userLocalStorage.addToken(request));
                                }),
                                finalize(() => {
                                    this.isRefreshing = false;
                                })).subscribe();
                        }
                    }
                }
                if (errors.status === StatusCodes.FORBIDDEN) {
                    console.log(request);
                    // this.route.navigateByUrl(`${RouteStrings.error}/${StatusCodes.UNAUTHORIZED}`);
                }
                return throwError(errors);
            }),
            retryWhen((errors) => errors.pipe(
                flatMap((error, index) => {
                    if (!error.status || error.status !== 401) {
                        return throwError(error);
                    }
                    if (index === 0) {
                        this.userLocalStorage.addToken(request)
                    }
                    if (index === 3) {
                        this.userLocalStorage.removeAccessTokenAndRefreshToken();
                        this.route.navigateByUrl(`${RouteStrings.auth.auth}/${RouteStrings.auth.login}`);
                    }
                    else {
                    }
                    return of(error).pipe(delay(5000));
                }),
                take(4)
            )),
        );
    }
}

i go для мета-редукторов, которые регистрируют все действия, поэтому я создал redurs.ts и получите все редукторы и зарегистрируйте его

// NGRX
import { ActionReducerMap, MetaReducer, ActionReducer } from '@ngrx/store';
import { routerReducer } from '@ngrx/router-store';
import { environment } from '../../../../environments/environment';
import { InjectionToken, inject } from '@angular/core';
import { HandlerService } from '../../services/util/handler.service';
// tslint:disable-next-line:no-empty-interface
export interface AppState { }

export const reducers: ActionReducerMap<AppState> = { router: routerReducer }; // it select all reducer maps in rounting
export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
    return function(state, action) {
      console.info('state', state);
      console.info('action', action);

      return reducer(state, action);
    };
  }
  export const REDUCER_TOKEN = new InjectionToken<ActionReducerMap<AppState>>('Registered Reducers', {
    factory: () => {
      const serv = inject(HandlerService);
      // return reducers synchronously
      return serv.loadUser();
    }
  });
  // export const metaReducers: MetaReducer<AppState>[] = [debug];
  // export const metaReducers: MetaReducer<AppState>[] = !environment.production ? [debug] : [];

, поэтому мне нужно внедрить службу в это промежуточное ПО и использовать ее, как если бы токен истек, поэтому я сделаю массив действий при сбое и отправлю снова

app.module.ts

  imports: [
    ValidatorsModule,
    // import fake-server
    environment.isMockEnabled
      ? HttpClientInMemoryWebApiModule.forRoot(FakeApiService, {
        passThruUnknownUrl: true,
        dataEncapsulation: false,
      }) : [],
    BrowserAnimationsModule,
    BrowserModule,
    AngularFireModule.initializeApp(firebaseConfig),
    AngularFireMessagingModule,
    ToastrModule,
    PasswordStrengthMeterModule,
    AppRoutingModule,
    CustomFormsModule,
    HttpClientModule,
    NgxUiLoaderModule.forRoot(ngxUiLoaderConfig),
    CdkTableModule,
    NgxMaskModule.forRoot(),
    ToastrModule.forRoot({
      preventDuplicates: true,
    }), 
    NgxPermissionsModule.forRoot(),
    SharedModule,
    MatSnackBarModule,
    MatDialogModule,
    CoreModule,
    MatStepperModule,
    OverlayModule,
    MatTableModule,
    NgbModule,
    StoreModule.forRoot(REDUCER_TOKEN),
    // StoreModule.forRoot(reducers, {
    //   metaReducers,
    //   // runtimeChecks: {
    //   //   strictStateImmutability: true,
    //   //   strictActionImmutability: true
    //   // }
    // }),
    // StoreModule.forRoot(reducers, { metaReducers }),
    EffectsModule.forRoot([]),
    StoreRouterConnectingModule.forRoot({ stateKey: 'router' }),
    AuthModule.forRoot(),
    // begin define Modules
    MerchantModule,
    SponsorModule,
    ProfileModule,
    SettingModule,
    // end define Modules
    TranslateModule.forRoot(),
    MatProgressSpinnerModule,
    InlineSVGModule.forRoot(),
  ],

Итак, что мне нужно сейчас, чтобы логики c работали хорошо

...