Angular 6: 从 Http 拦截器调用服务 observer.next 导致无限请求循环
Angular 6: Calling service observer.next from Http Interceptor causes infinite request loop
我正在使用 JWT 进行身份验证的网站上工作。我创建了一个 HTTP 拦截器 class,它将令牌添加到所有请求 headers 并用于捕获 401 错误。
import {Injectable} from '@angular/core';
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable, of} from 'rxjs';
import {JwtService} from '../service/jwt.service';
import {catchError} from 'rxjs/operators';
import {AlertService} from '../../shared/service/alert.service';
import {Router} from '@angular/router';
import {AlertType} from '../../shared/model/alert.model';
@Injectable()
export class HttpTokenInterceptor implements HttpInterceptor {
constructor(private jwtService: JwtService, private alertService: AlertService, private router: Router) {
}
/**
* Intercept HTTP requests and return a cloned version with added headers
*
* @param req incoming request
* @param next observable next request
*/
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Add headers to all requests
const headersConfig = {
'Accept': 'application/json'
};
// Add token bearer to header when it's available
const token = this.jwtService.getToken();
if (token) {
headersConfig['Authorization'] = `Bearer ${token}`;
headersConfig['Content-Type'] = 'application/json';
}
const request = req.clone({setHeaders: headersConfig});
// Return adjusted http request with added headers
return next.handle(request).pipe(
catchError((error: any) => {
// Unauthorized response
if (error.status === 401) {
this.handleError();
return of(error);
}
throw error;
})
);
}
/**
* Handle http errors
*/
private handleError() {
// Destroy the token
this.jwtService.destroyToken();
// Redirect to login page
this.router.navigate(['/login']);
// This is causing infinite loops in HTTP requests
this.alertService.showAlert({
message: 'Your token is invalid, please login again.',
type: AlertType.Warning
});
}
}
class 使用我的 JwtToken class 从本地存储中删除令牌并使用 Angular 路由器将用户重定向到登录页面。 alertService 的 showAlert 方法导致 http 请求无限重复。
我认为这是由警报服务中的 Observer 实现引起的。但是我尝试了很多不同的实现,我真的不知道出了什么问题。
import {Injectable} from '@angular/core';
import {Alert} from '../model/alert.model';
import {Subject} from 'rxjs';
/**
* Alert Service: Used for showing alerts all over the website
* Callable from all components
*/
@Injectable()
export class AlertService {
public alertEvent: Subject<Alert>;
/**
* AlertService constructor
*/
constructor() {
this.alertEvent = new Subject<Alert>();
}
/**
* Emit event containing an Alert object
*
* @param alert
*/
public showAlert(alert: Alert) {
this.alertEvent.next(alert);
}
}
显示所有警报消息的警报组件正在使用 alertService class。该组件用于两个主要组件:仪表板和登录。
import {Component} from '@angular/core';
import {AlertService} from '../../shared/service/alert.service';
import {Alert} from '../../shared/model/alert.model';
@Component({
selector: '*brand*-alerts',
templateUrl: './alerts.component.html',
})
export class AlertsComponent {
// Keep list in global component
public alerts: Array<Alert> = [];
constructor(private alertService: AlertService) {
// Hook to alertEvents and add to class list
alertService.alertEvent.asObservable().subscribe(alerts => {
// console.log(alerts);
this.alerts.push(alerts);
});
}
}
下图中问题清晰可见:
video of loop
亲切的问候。
编辑:已解决
在发出请求的页面中,警报服务上的订阅已初始化,这导致 http 请求再次触发。我只是让警报组件成为 alertService 现在的唯一订阅者,并为刷新创建了一个新服务。 @incNick 的回答确实是一个正确的实现。谢谢!
抱歉,我的工作很忙,但希望我的消息来源能对您有所帮助。
import { Observable, throwError } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';
...
return httpHandler.handle(request).pipe(
tap((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
//this.loadingService.endLoading();
}
},
(err: any) => {
//this.loadingService.endLoading();
}),
catchError((err: any) => {
if (err.status === 401) {
/*
this.modalController.create({
component: LoginComponent,
componentProps: {returnUrl: this.router.url},
showBackdrop: true
}).then(modal => modal.present());
*/
} else {
//this.messageService.showToast(`Some error happen, please try again. (Error-${err.status})`, 'error');
}
return throwError(err);
})
);
我在最后 return throwError(err)。
我正在使用 JWT 进行身份验证的网站上工作。我创建了一个 HTTP 拦截器 class,它将令牌添加到所有请求 headers 并用于捕获 401 错误。
import {Injectable} from '@angular/core';
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Observable, of} from 'rxjs';
import {JwtService} from '../service/jwt.service';
import {catchError} from 'rxjs/operators';
import {AlertService} from '../../shared/service/alert.service';
import {Router} from '@angular/router';
import {AlertType} from '../../shared/model/alert.model';
@Injectable()
export class HttpTokenInterceptor implements HttpInterceptor {
constructor(private jwtService: JwtService, private alertService: AlertService, private router: Router) {
}
/**
* Intercept HTTP requests and return a cloned version with added headers
*
* @param req incoming request
* @param next observable next request
*/
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Add headers to all requests
const headersConfig = {
'Accept': 'application/json'
};
// Add token bearer to header when it's available
const token = this.jwtService.getToken();
if (token) {
headersConfig['Authorization'] = `Bearer ${token}`;
headersConfig['Content-Type'] = 'application/json';
}
const request = req.clone({setHeaders: headersConfig});
// Return adjusted http request with added headers
return next.handle(request).pipe(
catchError((error: any) => {
// Unauthorized response
if (error.status === 401) {
this.handleError();
return of(error);
}
throw error;
})
);
}
/**
* Handle http errors
*/
private handleError() {
// Destroy the token
this.jwtService.destroyToken();
// Redirect to login page
this.router.navigate(['/login']);
// This is causing infinite loops in HTTP requests
this.alertService.showAlert({
message: 'Your token is invalid, please login again.',
type: AlertType.Warning
});
}
}
class 使用我的 JwtToken class 从本地存储中删除令牌并使用 Angular 路由器将用户重定向到登录页面。 alertService 的 showAlert 方法导致 http 请求无限重复。
我认为这是由警报服务中的 Observer 实现引起的。但是我尝试了很多不同的实现,我真的不知道出了什么问题。
import {Injectable} from '@angular/core';
import {Alert} from '../model/alert.model';
import {Subject} from 'rxjs';
/**
* Alert Service: Used for showing alerts all over the website
* Callable from all components
*/
@Injectable()
export class AlertService {
public alertEvent: Subject<Alert>;
/**
* AlertService constructor
*/
constructor() {
this.alertEvent = new Subject<Alert>();
}
/**
* Emit event containing an Alert object
*
* @param alert
*/
public showAlert(alert: Alert) {
this.alertEvent.next(alert);
}
}
显示所有警报消息的警报组件正在使用 alertService class。该组件用于两个主要组件:仪表板和登录。
import {Component} from '@angular/core';
import {AlertService} from '../../shared/service/alert.service';
import {Alert} from '../../shared/model/alert.model';
@Component({
selector: '*brand*-alerts',
templateUrl: './alerts.component.html',
})
export class AlertsComponent {
// Keep list in global component
public alerts: Array<Alert> = [];
constructor(private alertService: AlertService) {
// Hook to alertEvents and add to class list
alertService.alertEvent.asObservable().subscribe(alerts => {
// console.log(alerts);
this.alerts.push(alerts);
});
}
}
下图中问题清晰可见:
video of loop
亲切的问候。
编辑:已解决
在发出请求的页面中,警报服务上的订阅已初始化,这导致 http 请求再次触发。我只是让警报组件成为 alertService 现在的唯一订阅者,并为刷新创建了一个新服务。 @incNick 的回答确实是一个正确的实现。谢谢!
抱歉,我的工作很忙,但希望我的消息来源能对您有所帮助。
import { Observable, throwError } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';
...
return httpHandler.handle(request).pipe(
tap((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
//this.loadingService.endLoading();
}
},
(err: any) => {
//this.loadingService.endLoading();
}),
catchError((err: any) => {
if (err.status === 401) {
/*
this.modalController.create({
component: LoginComponent,
componentProps: {returnUrl: this.router.url},
showBackdrop: true
}).then(modal => modal.present());
*/
} else {
//this.messageService.showToast(`Some error happen, please try again. (Error-${err.status})`, 'error');
}
return throwError(err);
})
);
我在最后 return throwError(err)。