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