Angular 7 错误拦截器 – 原始调用有问题

Posted

技术标签:

【中文标题】Angular 7 错误拦截器 – 原始调用有问题【英文标题】:Angular 7 Error Interceptor – Problem with original call 【发布时间】:2019-06-03 22:11:22 【问题描述】:

Angular 7 错误拦截器 - 原始调用有问题 我编写了一个拦截器来捕获所有 401 错误。如果发生这样的错误,它应该尝试获取新的 JWT 令牌,如果成功,则重复原始请求。到目前为止,拦截器工作。我的问题是,在我给它新令牌之后的原始请求不会再次出现在订阅中,它附加到原始可观察对象。

原始请求

组件:

this.serviceModel.getSearchItems(si).subscribe(res => 
   this.resultData = res;
);

服务模型

public getSearchItems(si: SearchInfo): Observable<VoList<SearchResultItemVO>> 
   return this.post<VoList<SearchResultItemVO>>(`/api/Search/GetSearchItems`, si, null,  SearchResultItemVO);

拦截器

@Injectable()
export class ErrorInterceptor implements HttpInterceptor 
  constructor(private serviceModel: SharedAccountServiceModel) 

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> 
    return next.handle(request).catch(err => 
      return this.handleError(err, request, next);
    );
  
  handleError(err, request: HttpRequest<any>, next: HttpHandler) 
    if (err.status === 401) 
      if (!!localStorage.getItem('auth_token') &&     !!localStorage.getItem('refresh_token')) 
        this.serviceModel.refreshlogin().switchMap(res => 
          return next.handle(this.addAuthenticationToken(request));
    )
    .subscribe();
  
  else 
    localStorage.removeItem('accessinfo');
    localStorage.removeItem('auth_token');
    localStorage.removeItem('userid');
    location.reload(true);
  

const error = err.error.message || err.statusText;
return throwError(error);
  
  addAuthenticationToken(request) 
const accessToken = localStorage.getItem('auth_token');
if (!accessToken) 
  return request;    
return request.clone(
  setHeaders: 
    Authorization: `Bearer $localStorage.getItem('auth_token')`
  
    );
  

据我了解,switchMap 应该有助于再次执行原始订阅,但它不起作用。调用已执行,但未到达订阅。

【问题讨论】:

【参考方案1】:

我面临着非常接近的问题,我使用“管道”中的“switchmap”解决了这个问题,HttpInterceptor 像这样:

HttpErrorFilter :

import  BehaviorSubject, Observable, of, throwError  from 'rxjs';
import  HttpEvent, HttpHandler, HttpInterceptor, HttpRequest  from '@angular/common/http';
import  catchError, filter, finalize, switchMap, take  from 'rxjs/operators';

import  AuthenticationService  from './../services/authentication.service';
import  Injectable  from '@angular/core';

@Injectable()
export class HttpErrorFilter implements HttpInterceptor 

    isRefreshingToken = false;
    tokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);

    constructor(private authenticationService: AuthenticationService) 

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> 
        return next.handle(request).pipe(
            catchError((error: any) => 
                if (error.status === 401 && !request.url.includes('auth/signin')) 
                    return this.handle401Error(request, next);
                
                return throwError(error);
            )
        );
    

    private handle401Error(request: HttpRequest<any>, next: HttpHandler) 

        if (request.url.includes('auth/refreshtoken')) 
            this.isRefreshingToken = false;
            return of(<any>this.authenticationService.logout());
        
        if (!this.isRefreshingToken) 
            this.isRefreshingToken = true;
            this.tokenSubject.next(null);

            return this.authenticationService.refresh().pipe(switchMap(token => 
                if (token) 
                    this.tokenSubject.next(token.value);
                    return next.handle(request);
                
                return of(<any>this.authenticationService.logout());
            ),
            catchError(err => 
                this.authenticationService.logout();
                return throwError(err.error);
            ),
            finalize(() => 
                this.isRefreshingToken = false;
            ));
         else 
            this.isRefreshingToken = false;

            return this.tokenSubject
                .pipe(filter(token => token != null),
                take(1),
                switchMap(token => 
                    return next.handle(request);
                ));
        
    

HttpFilter :

import  HttpEvent, HttpHandler, HttpInterceptor, HttpRequest  from '@angular/common/http';

import  Injectable  from '@angular/core';
import  Observable  from 'rxjs';
import  environment  from './../../../environments/environment';

@Injectable()
export class HttpFilter implements HttpInterceptor 

    private apiUrl = environment.apiUrl;

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> 
        return next.handle(this.addAuthentication(request));
    

    private addAuthentication(request: HttpRequest<any>): HttpRequest<any> 

        if (!request.url.includes('/auth/')) 
            const token =  localStorage.getItem('access.token');
            if (token) 
                request = request.clone(
                    setHeaders: Authorization: 'Bearer ' + token
                );
            
        
        return request.clone(url: `$this.apiUrl$request.url`);

    


NgModule 提供:

@NgModule(
...
providers: [
     provide: HTTP_INTERCEPTORS, useClass: HttpErrorFilter, multi: true ,
     provide: HTTP_INTERCEPTORS, useClass: HttpFilter, multi: true ,
    ...
  ],
...
)

【讨论】:

以上是关于Angular 7 错误拦截器 – 原始调用有问题的主要内容,如果未能解决你的问题,请参考以下文章

Angular 7:拦截器中的等待函数

Angular 7 - 如何在某些响应状态代码上重试 http 请求?

TypeError:无法使用拦截器读取 Angular 中未定义的属性“数据”

Angular 7 自动刷新不记名令牌

具有 Firebase 身份验证的拦截器

原始WkWebview调用后如何拦截http / https调用?