next.handle(请求)在多个 HTTP 拦截器中失败:未定义

next.handle(request) in multiple HTTP interceptors fails: undefined

在一个项目中,我使用 2 个 HTTP 拦截器:1 个用于向每个请求添加 JWT 令牌,另一个用于拦截传入的 401 错误状态。

我调用一个单独的程序来获取我的应用程序在此服务中的所有反馈:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { environment } from '@environments/environment';
import { Feedback } from '@app/_models/feedback';

@Injectable({ providedIn: 'root' })
export class FeedbackService {
    constructor(
        private http: HttpClient
    ) {}

    getAll() {
        return this.http.get<Feedback[]>(`${environment.apiUrl}/feedback`);
    }

    getById(id: string) {
        return this.http.get<Feedback>(`${environment.apiUrl}/feedback/${id}`);
    }

    delete(id: string) {
        return this.http.delete(`${environment.apiUrl}/feedback/${id}`);
    }
}

JWT 拦截器:

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';

import { environment } from '@environments/environment';
import { AuthorizationService } from 'src/shared/authorization.service';

@Injectable()
export class JwtInterceptor implements HttpInterceptor {
    constructor(private auth: AuthorizationService) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // add auth header with jwt if user is logged in and request is to the api url
        const authenticatedUser = this.auth.getAuthenticatedUser();
        if (authenticatedUser == null) {
            return;
        }
        authenticatedUser.getSession( (err, session) => {
            if (err) {
                console.log(err);
                return;
            }
            const isApiUrl = request.url.startsWith(environment.apiUrl);
            const token = session.getIdToken().getJwtToken();
            const headers = new Headers();
            headers.append('Authorization', token);
            if (this.auth.isLoggedIn() && isApiUrl) {
                request = request.clone({
                    setHeaders: {
                        Authorization: token,
                    }
                });
            }

            return next.handle(request);
        });
    }
}

错误拦截器:

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

import { AccountService } from '@app/_services';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
    constructor(private accountService: AccountService) {}

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        console.log(next.handle(request));
        return next.handle(request).pipe(catchError(err => {
            if (err.status === 401) {
                // auto logout if 401 response returned from api
                this.accountService.logout();
            }

            const error = err.error.message || err.statusText;
            return throwError(error);
        }));
    }
}

当我在 app.module 中提供两个拦截器时,

{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },

我总是收到下面的错误提示。发生这种情况是因为 next.handle(request) 显然是 undefined,我真的不知道为什么。仅使用错误拦截器没有问题。

ERROR TypeError: Cannot read property 'pipe' of undefined
    at ErrorInterceptor.intercept (error.interceptor.ts:14)
    at HttpInterceptorHandler.handle (http.js:1958)
    at HttpXsrfInterceptor.intercept (http.js:2819)
    at HttpInterceptorHandler.handle (http.js:1958)
    at HttpInterceptingHandler.handle (http.js:2895)
    at MergeMapSubscriber.project (http.js:1682)
    at MergeMapSubscriber._tryNext (mergeMap.js:46)
    at MergeMapSubscriber._next (mergeMap.js:36)
    at MergeMapSubscriber.next (Subscriber.js:49)
    at Observable._subscribe (subscribeToArray.js:3)

仅使用 JwtInterceptor 会出现以下错误,我无法弄清楚它的来源。当然,我想同时使用两者。配置多个拦截器时我是否遗漏了什么?

ERROR TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
    at subscribeTo (subscribeTo.js:27)
    at subscribeToResult (subscribeToResult.js:11)
    at MergeMapSubscriber._innerSub (mergeMap.js:59)
    at MergeMapSubscriber._tryNext (mergeMap.js:53)
    at MergeMapSubscriber._next (mergeMap.js:36)
    at MergeMapSubscriber.next (Subscriber.js:49)
    at Observable._subscribe (subscribeToArray.js:3)
    at Observable._trySubscribe (Observable.js:42)
    at Observable.subscribe (Observable.js:28)
    at MergeMapOperator.call (mergeMap.js:21)

重写你的 JwtInterceptor:

import { HttpInterceptor, HttpHandler, HttpRequest, HttpEvent } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, from } from 'rxjs';

import { environment } from '@environments/environment';
import { AuthorizationService } from 'src/shared/authorization.service';


@Injectable()
export class JwtInterceptor implements HttpInterceptor {
    constructor(private auth: AuthorizationService) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return from(this.getSessionWithAuthReq(request, next));
    }

    async getSessionWithAuthReq(request: HttpRequest<any>, next: HttpHandler){
        const authenticatedUser = this.auth.getAuthenticatedUser();
        
        if (authenticatedUser) {
            const authRequest:  HttpRequest<any>  = await new Promise( (resolve) => {
                authenticatedUser.getSession( (err, session) => {
                    if (err) {
                        console.log(err);
                        // want to go on without authenticating if there is an error from getting session 
                        return resolve(request);
                    }
                    const isApiUrl = request.url.startsWith(environment.apiUrl);
                    const token = session.getIdToken().getJwtToken();
                    const headers = new Headers();
                    headers.append('Authorization', token);
                    if (this.auth.isLoggedIn() && isApiUrl) {
                        const req = request.clone({
                            setHeaders: {
                                Authorization: token,
                            }
                        });
                        return resolve(req);
                    }
                    return resolve(request);
                });
            });
            
            
            return next.handle(authRequest).toPromise();
        }

        return next.handle(request).toPromise();
    }
}