У меня есть HttpInterceptor
, который прослушивает определенные события токена JWT (token_expired
, token_not_provided
и token_invalid
), которые могут происходить в разное время рабочего процесса.
Эти события могут происходить, когда пользователь переходит на другой маршрут ИЛИ когда запрос AJAX отправляется по тому же маршруту (например, получение данных, сохранение формы и т. Д.).
Когда перехватчик обнаруживает любое из этих конкретных событий, он предлагает пользователю снова ввести учетные данные для входа (с использованием модального окна) и ставит запрос в очередь для последующей обработки (после того, как пользователь снова вошел в систему). Это важно, поскольку отправленные данные не могут быть потеряны (например, при обновлении заказа или клиента).
Упрощенная версия моего кода перехватчика:
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private injector: Injector) {}
router: Router;
auth: AuthService;
api: APIService;
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.router = this.injector.get(Router);
this.auth = this.injector.get(AuthService);
let token = this.auth.getToken();
let headers = {
'Content-Type':'application/json',
};
if (token) {
(<any>headers).Authorization = `Bearer ${token}`;
}
request = request.clone({
setHeaders: headers
});
return next.handle(request).do((event: HttpEvent<any>) => {
}, (err: any) => {
if (err instanceof HttpErrorResponse) {
let msg = typeof(err.error) === 'string' ? JSON.parse(err.error) : err.error;
if (msg && msg.error && ['token_not_provided', 'token_expired','token_invalid'].indexOf(msg.error) > -1) {
this.auth.queueFailedRequest(request);
//set the intended route to current route so that after login the user will be shown the same screen
this.auth.setIntendedRoute(this.router.url);
//show the login popup
this.auth.promptLogin();
}
}
}
});
}
}
Соответствующая часть AuthService:
queue: Array<HttpRequest<any>> = [];
queueFailedRequest(request): void {
this.queue.push(request);
}
retryFailedRequests(): void {
this.queue.forEach(request => {
this.retryRequest(request);
});
this.queue = [];
}
retryRequest(request): void {
if (request.method === 'GET') {
this.apiService.get(request.urlWithParams);
}
else if (request.method === 'POST') {
this.apiService.post(request.urlWithParams, request.body || {});
}
}
И, конечно же, после успешного входа в систему я вызываю retryFailedRequests()
.
Пока все хорошо, и действительно, все HTTP-запросы ставятся в очередь и отправляются, если вход в систему прошел успешно.
А теперь к проблеме - если код структурирован как в этом примере (взят из компонента EditOrder
):
updateOrder() {
this.api.updateOrder(this.data).subscribe(res => {
if (res.status === 'success') {
alert('should be triggered even after login prompt');
}
});
}
Затем, если пользователю необходимо повторно войти в систему в процессе, предупреждение никогда не будет запущено после того, как метод retryFailedRequests()
завершит обработку очереди.
Итак, вопрос в том, как лучше всего убедиться, что исходное обещание ставится в очередь вместе с HTTP-запросом и разрешается после завершения обработки очереди?