为啥 switchMap 在 Angular 拦截器(Angular 9)中不起作用

Posted

技术标签:

【中文标题】为啥 switchMap 在 Angular 拦截器(Angular 9)中不起作用【英文标题】:why switchMap not working in Angular interceptor ( Angular 9 )为什么 switchMap 在 Angular 拦截器(Angular 9)中不起作用 【发布时间】:2020-09-18 19:54:29 【问题描述】:

我写了一个拦截器来为我刷新令牌。但是使用我检查的调试器,它根本没有进入 switchMap ,并且请求不会再次发送它。 有谁知道问题出在哪里?

    import HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse from '@angular/common/http';
import Injectable, Injector from '@angular/core';
import BehaviorSubject, Observable from 'rxjs';
import catchError, switchMap, tap from 'rxjs/operators';
import ResponseModel from '../Models/responseModel';
import UserDataModel from '../Models/UserDataModel';
import RoleEnum from '../Enums/RoleEnum';
import AuthService from '../../auth/auth.service';
import AuthModel from '../Models/authModel';
import LoginType from '../Enums/LoginType';
import LoginResponseModel from '../Models/LoginResponseModel';

@Injectable(providedIn: 'root')
export class TokenInterceptor implements HttpInterceptor 
    httpSubject: BehaviorSubject<LoginResponseModel> = new BehaviorSubject<LoginResponseModel>(null);

    constructor(private injector: Injector) 
    

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> 
        let modifiedReq;
        const userData: 
            username: string,
            access_token: string,
            refresh_token: string,
            role: RoleEnum,
            _tokenexpirationDate: string
         = (JSON.parse(localStorage.getItem('userData')));
        if (userData.access_token !== null && !req.headers.has('X-Skip-Interceptor')) 
            modifiedReq = req.clone(
                headers: req.headers.set('access_token', `$userData.access_token`),
            );
         else 
            modifiedReq = req.clone(
                setHeaders: 
                    'Content-Type': 'application/json',
                    'Access-Control-Allow-Origin': '*'
                
            );
        
        return next.handle(modifiedReq).pipe(tap(res => 
            if (res instanceof HttpResponse) 
                const response = (<HttpResponse<ResponseModel<string>>> res).body;
                /*const userdata: 
                    username: string,
                    access_token: string,
                    refresh_token: string,
                    role: RoleEnum,
                    _tokenexpirationDate: string
                 = (JSON.parse(localStorage.getItem('userData')));*/
                if (!response.is_successfull) 
                    if (new Date() > new Date(userData._tokenexpirationDate)) 
                        this.refreshToken(req, next, userData.refresh_token, userData.username);
                    
                
            
        ));
    

    refreshToken(req: HttpRequest<any>, next: HttpHandler, refreshtoken: string, username: string) 
        this.httpSubject.next(null);
        const authService = this.injector.get(AuthService);
        const authmodel: AuthModel = 
            email_address: '', grant_type: LoginType.RefreshToken, password: '', phone_number: '', phone_number_countery_iso2_code: '',
            refresh_token: refreshtoken, username: ''
        ;

        debugger;
        return authService.Signin(authmodel).pipe(
            switchMap(result => 
                debugger;
                if (result.is_successfull && result.response.access_token !== null) 
                    const expirationDate = new Date(new Date().getTime() + +result.response.expires_in * 60000);
                    const user = new UserDataModel(username, result.response.access_token, result.response.refresh_token
                        , result.response.role, expirationDate);
                    localStorage.setItem('userData', JSON.stringify(user));
                    this.httpSubject.next(result.response);
                    req = req.clone(
                        setHeaders: 
                            access_token: `$result.response.access_token`
                        
                    );
                    return next.handle(req);

                

            ) , catchError(err => 
                console.log(err);
                return Observable;
            ));
    

我还要说,如果我用subscribe代替pipe,switchMap,token会被刷新,只有request不会从头开始重复。

【问题讨论】:

@AakashGarg 您无需评论您已发布答案。 OP 在他们的收件箱中收到警报。无需惹恼任何人。 【参考方案1】:

你的代码应该是:-

   import HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse from '@angular/common/http';
import Injectable, Injector from '@angular/core';
import BehaviorSubject, Observable from 'rxjs';
import catchError, switchMap, tap from 'rxjs/operators';
import ResponseModel from '../Models/responseModel';
import UserDataModel from '../Models/UserDataModel';
import RoleEnum from '../Enums/RoleEnum';
import AuthService from '../../auth/auth.service';
import AuthModel from '../Models/authModel';
import LoginType from '../Enums/LoginType';
import LoginResponseModel from '../Models/LoginResponseModel';

@Injectable(providedIn: 'root')
export class TokenInterceptor implements HttpInterceptor 
    httpSubject: BehaviorSubject<LoginResponseModel> = new BehaviorSubject<LoginResponseModel>(null);

    constructor(private injector: Injector) 
    

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> 
        let modifiedReq;
        const userData: 
            username: string,
            access_token: string,
            refresh_token: string,
            role: RoleEnum,
            _tokenexpirationDate: string
         = (JSON.parse(localStorage.getItem('userData')));
        if (userData.access_token !== null && !req.headers.has('X-Skip-Interceptor')) 
            modifiedReq = req.clone(
                headers: req.headers.set('access_token', `$userData.access_token`),
            );
         else 
            modifiedReq = req.clone(
                setHeaders: 
                    'Content-Type': 'application/json',
                    'Access-Control-Allow-Origin': '*'
                
            );
        
        return next.handle(modifiedReq).pipe(tap(res => 
            if (res instanceof HttpResponse) 
                const response = (<HttpResponse<ResponseModel<string>>> res).body;
                /*const userdata: 
                    username: string,
                    access_token: string,
                    refresh_token: string,
                    role: RoleEnum,
                    _tokenexpirationDate: string
                 = (JSON.parse(localStorage.getItem('userData')));*/
                if (!response.is_successfull) 
                    if (new Date() > new Date(userData._tokenexpirationDate)) 
                        this.refreshToken(req, next, userData.refresh_token, userData.username).then((request)=> 


                                   modifiedRequest = request.clone();
                                   throw "replay";
                     );
                    
                

        ),retryWhen(errors => errors);
    

refreshToken(req: HttpRequest<any>, next: HttpHandler, refreshtoken: string, username: string) 
            this.httpSubject.next(null);
            const authService = this.injector.get(AuthService);
            const authmodel: AuthModel = 
                email_address: '', grant_type: LoginType.RefreshToken, password: '', phone_number: '', phone_number_countery_iso2_code: '',
                refresh_token: refreshtoken, username: ''
            ;

            debugger;
            return await authService.Signin(authmodel).pipe(
                map(result => 
                    debugger;
                    if (result.is_successfull && result.response.access_token !== null) 
                        const expirationDate = new Date(new Date().getTime() + +result.response.expires_in * 60000);
                        const user = new UserDataModel(username, result.response.access_token, result.response.refresh_token
                            , result.response.role, expirationDate);
                        localStorage.setItem('userData', JSON.stringify(user));
                        this.httpSubject.next(result.response);
                        req = req.clone(
                            setHeaders: 
                                access_token: `$result.response.access_token`
                            
                        );
                        return req;

                    

                ) , catchError(err => 
                    console.log(err);
                    return Observable;
                )).toPromise();
        
    

原因:- Tap 没有订阅您返回的内部 observable。合并地图就是这样做的。流组合应使用适当的运算符。

【讨论】:

mergeMap 将标记添加到其顶部的标头会毁了我。难道没有别的办法了吗?还是其他运营商?我只想访问响应并能够理解我需要刷新令牌。然后重复请求。 是的,我试过了。但是一些问题,例如向标头添加令牌,被打乱了。没有别的办法吗? 我添加了没有合并映射的新代码你试过了吗? 我现在测试了它。就好像我说如果我订阅就会发生。它进入了Switchmap,刷新了token,但是没有重复之前的请求。如果我再次手动重复,那很好,因为我们有一个新令牌,但我希望这自动发生。我读到 switchMap 可以为我做到这一点。 对不起,我正在纠正。请求再次完成。但答案并不在我问的地方。我在哪里订阅我的服务。

以上是关于为啥 switchMap 在 Angular 拦截器(Angular 9)中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

在 Angular 5 中使用 SwitchMap 订阅

如何在 Angular 中使用 Jasmine 测试 RxJS switchMap?

来自 Angular Redux Store 的 SwitchMap 结果

在Angular App中的Obseable POST请求中使用switchMap

使用 switchmap 和 forkjoin 链接 observables 不能按预期工作 angular typescript rxjs

Angular 6 - 为啥生产构建中缺少承载令牌? (在开发版本中工作正常)