在我当前的项目中,我正在使用角度为5的Adal.js来处理身份验证。
但是现在我在http拦截器和响应数据上遇到了一个很奇怪的问题。
拦截器的基本逻辑是,我将尝试使用缓存的令牌进行api调用,如果它失败并返回401,则我将获取一个新的令牌,然后使用新的令牌再次尝试该调用。
但是发生的是,在第二次调用之后,除非我以某种方式与页面交互,否则我的视图将不会更新任何接收到的数据。 (点击,滚动等)
有什么想法会导致这种情况,并且要解决吗?
修改
我刚刚发现我可以注入ChangeDetectorRef并检测出导致视图渲染的更改。我不喜欢这种解决方案,因为这意味着每个api调用都需要更改检测,这听起来像是个坏主意。还有更优雅的解决方案吗?
组件
import { Component, OnInit, Injector } from '@angular/core';
import { AppBaseComponent } from '../../../../global/app-base.component';
import { VendorVM } from "../../../../proxy/service-proxies";
import { VendorsService } from '../vendors.service';
@Component({
selector: 'vendors-list',
templateUrl: './vendors.list.component.html',
styleUrls: []
})
export class VendorsListComponent extends AppBaseComponent implements OnInit {
public vendors: VendorVM[];
constructor(
injector: Injector,
public service: VendorsService) {
super(injector);
}
ngOnInit(): void {
this.getVendors();
}
public getVendors() {
this.service.getVendorsForList().subscribe(data => {
this.vendors = data;
this.vendorsUnfiltered = data;
console.log('vendors loaded');
console.log(this.vendors);
});
}
}
拦截器
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpHandler, HttpEvent, HttpResponse, HttpErrorResponse, HttpRequest } from '@angular/common/http';
import { AuthService } from '../global/services/auth.service';
import { Observable } from 'rxjs/Observable';
import { AdalService } from './../global/services/adal.service';
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(private _auth: AuthService, private adal: AdalService) { }
addToken(request: HttpRequest<any>, token: string): HttpRequest<any> {
return request = request.clone({
setHeaders: {
Authorization: 'Bearer ' + token
}
});
}
public intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const authReq = this.addToken(request, this._auth.getToken());
return next.handle(authReq)
.map((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
return event;
}
})
.catch((err: any) => {
console.log('http error');
if (err instanceof HttpErrorResponse) {
if (err.status === 401) {
console.log('401 get new token');
return this.adal
.getNewToken()
.flatMap((res) => {
console.log(res);
return next.handle(this.addToken(request, res))
});
} else if (err.status === 403) {
console.log('403', err);
}
} else {
console.log('unknown error', err);
return Observable.throw(err);
}
})
}
}
仅重申以下两件事。
组件调用->拦截器->获取缓存令牌->身份验证并进行调用->成功加载数据并呈现视图。
组件调用->拦截器->获取缓存令牌->身份验证并进行调用-> 401身份验证失败的令牌已过期->获取新令牌->身份验证并使用新令牌进行调用->成功加载数据,但视图未呈现
答案 0 :(得分:0)
这个问题困扰了很久-最终提出了我自己的问题。
我终于能够根据以下答案找到解决方案: Http requests made from Ngxs state doesn't get detected by Angular (zone related issue)
将next.handle包装在ngZone run()中:
return this._ngZone.run(() => {
return next.handle(req).pipe(enterZone(this._ngZone));
});
function enterZone<T>(zone: NgZone) {
return (source: Observable<T>) => {
return new Observable((sink: Observer<T>) => {
return source.subscribe({
next(x) { zone.run(() => sink.next(x)); },
error(e) { zone.run(() => sink.error(e)); },
complete() { zone.run(() => sink.complete()); }
});
});
};
}
我不确定为什么在标准zone.run()方法之上需要enterZone功能,但是它对我来说可以确保从拦截器返回时刷新视图。
在此处详细了解有关ngZone的信息:https://angular.io/api/core/NgZone
鉴于httpInterceptor是Angular的一部分,我不确定为什么这是必要的。