Angular: HttpInterceptorの循環依存エラー

作成日 2017年07月19日  ·  88コメント  ·  ソース: angular/angular

私は提出しています...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[x] Bug report  <!-- Please search GitHub for a similar issue or PR before submitting -->
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

現在の動作

公式ドキュメントと同じAuthInterceptorの例を実行して、すべてのリクエストにAuthorizationヘッダーを追加しようとしています。

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private auth: AuthService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler) {
    const authHeader = this.auth.getAuthorizationHeader();
    const authReq = req.clone({headers: req.headers.set('Authorization', authHeader)});
    return next.handle(authReq);
  }
}

問題:実際のコードでは、 AuthServiceはログイン、サインインなどでサーバーを要求する必要があるため、サーバー自体がHttpClient挿入します。

@Injectable()
export class AuthService {

  constructor(private http: HttpClient) {}

}

コンソールでこの致命的なエラーが発生します:

Uncaught Error: Provider parse errors:
Cannot instantiate cyclic dependency! InjectionToken_HTTP_INTERCEPTORS ("[ERROR ->]"): in NgModule AppModule in ./AppModule@-1:-1

予想される行動

非常に一般的なケースなので、機能するはずです。 それ以外の場合、インターセプターはHttpなしでサービスを使用するように制限されます。

環境


Angular version: 4.3.0
commohttp high bufix

最も参考になるコメント

コンストラクターでauthServiceを設定するのではなく、インターセプト関数を使用することで解決しました。


  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // Get the auth header from the service.
    const auth = this.inj.get(AuthenticationService);
    const authToken = auth.getAuthorizationToken();
    ...
   }

全てのコメント88件

このように動作しますか?

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(@Inject(forwardRef(() => AuthService)) private auth: AuthService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler) {
    const authHeader = this.auth.getAuthorizationHeader();
    const authReq = req.clone({headers: req.headers.set('Authorization', authHeader)});
    return next.handle(authReq);
  }
}

@tytskyi試してみましたが、うまくいきませんでした

  constructor(inj: Injector) {
    this.auth = inj.get(AuthService)
  }

でも動作します

https://plnkr.co/edit/8CpyAUSJcCiRqdZmuvt9?p=preview

@tytskyiソリューションが機能しないことを確認し

参考までに、今のところ、ログイン/サインイン/ ...(HttpClientを使用)用とAuthToken(Httpなし)の管理用の2つのサービスに分けています。

しかし、さらに進んで、401エラーをキャッチするためのエラーハンドラーを追加しました(トークンが無効な場合)。 しかし、そのような場合、ログアウトを呼び出す必要があるので、HttpClientを使用したサービスが必要です。したがって、最初の問題に再び陥るので、非常に問題があります。

これは真の循環依存であり、 @ Toxicableの例はそれを処理する正しい方法です。

ただし、 AuthServiceが独自のHTTPリクエストを作成していて、 AuthInterceptorがそのリクエストの詳細を確認せずにすべてのリクエストにヘッダーを追加したかどうかが心配です(記述されているとおり)。 つまり、リクエストはインターセプターを通過し、インターセプターは認証サービスに送られ、リクエストはインターセプターを通過し、無限に再帰します。

いいえ、 AuthServiceにはいくつかのHTTPリクエスト( login )を実行する必要があるメソッドが含まれていますが、 AuthInterceptorはそれらを使用せず、 AuthServiceが必要です。トークンを取得するには

私のコードは@cyrilletuziと非常によく似ています。 Toxicableソリューションを試し

ERROR Error: Uncaught (in promise): RangeError: Maximum call stack size exceeded
RangeError: Maximum call stack size exceeded
    at _createProviderInstance$1 (core.es5.js:9480)
    ...   

HttpClientを使用し、このインターセプターを作成してjwtトークンを追加します。 すべてが完璧に機能しますが、私には悪い習慣があります。 HttpClientインターセプター内でHttpを使用します。 変えたら

private http: Http,

private http: HttpClient
このサイクルエラーが発生します

循環依存をインスタンス化できません! InjectionToken_HTTP_INTERCEPTORS( "[ERROR->]")
どうすればそれを機能させることができますか?

import {Injectable} from "@angular/core";
import {HttpEvent, HttpHandler, HttpInterceptor} from "@angular/common/http";
import {HttpRequest} from "@angular/common/http";
import {Observable} from "rxjs/Observable";
import {Http} from "@angular/http";
import {SiteService} from "../services/site.service";
import {Router} from "@angular/router";

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

constructor(
    private http: Http,
    private router: Router,
    private siteService: SiteService
) {}

refreshToken() {
    return this.http.get(this.siteService.apiDomain() + '/api/token?token=' + localStorage.getItem('JWToken'), {})
        .map((response: any) => {
            let data = response.json();
            return {
                token: data.token,
                permissions: data.permissions,
                user: data.user,
            };
        })
}

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const clonedRequest = req.clone({
        headers: req.headers.set('Authorization', 'Bearer ' + localStorage.getItem('JWToken'))
    });

    return next.handle(clonedRequest).catch((res) => {

        if (res.status === 401 || res.status === 403) {
            return this.refreshToken().flatMap((data) => {
                if (data.token !== '') {
                    localStorage.setItem('currentUser', JSON.stringify(data.user));
                    localStorage.setItem('currentUserPermissions', JSON.stringify(data.permissions));
                    localStorage.setItem('JWToken', data.token);
                } else {
                    localStorage.removeItem('currentUser');
                    localStorage.removeItem('currentUserPermissions');
                    localStorage.removeItem('JWToken');
                    this.router.navigate(['./auth/login']);
                    return Observable.throw(res);
                }
                const clonedRequestRepeat = req.clone({
                    headers: req.headers.set('Authorization', 'Bearer ' + localStorage.getItem('JWToken'))
                });
                return next.handle(clonedRequestRepeat);
            })
        } else {
            return Observable.throw(res);
        }

    });

}
}

プロジェクトにこのインターセプターを使用するが、現在の問題とは無関係な人にとってもう1つの重要なことは、少なくとも数秒はヘッダーを更新トークン応答に設定することです。

-> header( 'Cache-Control'、 'public、max-age = 45')
-> header( 'Expires'、date( 'D、d MYH:i:s'、time()+ 45)。 'GMT');

これは面白い、

トークンのアクセシビリティを管理し、ログインと更新のリクエストを実行するAuthServiceのようなサービスがあることは、一般的なユースケースだと思いました。

つまり、基本的にここにシナリオがあります。 HttpClientに依存するAuthServiceがあります(明らかに)。 getAccessToken、getRefreshToken、login、refreshSession、logoutのいくつかのメソッドがあります。

さらに、トークンが存在する場合、リクエストにトークンを追加するAuthInterceptorが必要です。 そして明らかにそれはAuthServiceに依存します、原因サービスはトークンにアクセスできます。

したがって、ここに問題があります。HttpClientはHTTP_INTERCEPTORS(基本的にはAuthInterceptor)に依存し、AuthInterceptorはAuthServiceに依存し、AuthServiceはHttpClientに依存します。 そして周期的なエラー。

したがって、この一般的な使用例は、現在の実装では不可能です。

しかし、なぜ角度チームは最初の角度インターセプターが機能するのと同じ方法でインターセプターを実装できなかったのですか? たとえば、追加のストアサービスがあり、それを使用して新しいインターセプターを登録できます。たとえば、 httpInterceptors.push(interceptor)

私はng4-httpと呼ばれる同様のライブラリを

だから問題は、私は間違っているのですか? はいの場合、どこですか?

ここに投稿された例と非常によく似たコードがありますが、同じ問題が発生しています。 実際、 @ cyrilletuziがやろうとしていることに非常に近いようです。私は、AuthServiceで設定したトークンプロパティの値を取得しようとしているだけで、他の方法でそれを利用しようとはしていません。 インターセプターでアクセスするだけです。 インターセプターのコンストラクターにサービスを追加すると、循環依存エラーが発生します。基本的には、 @ mixalistzikasが取得したメッセージに至るまで、 @ Toxicableが提供するソリューションを試してみると、 @ perusopersonaleが取得したのと同じ問題、つまり多くの再帰、およびアプリのクラッシュ。

最終的には、トークンをサービスから取得するのではなく、保存したセッションストレージから直接アクセスすることにしましたが、実際の循環依存関係がないように見えるときにこの問題が発生しているようです。 私が説明した回避策は今のところうまくいきますが、間違いなくどこかでサービスにアクセスする必要がある多くの状況であり、それはこの方法では不可能と思われます

この例も私には有効ですが、将来的にhttpが非推奨になる可能性があり、アプリが同じものに2つの異なるライブラリを挿入するのも良くないので心配しました。

誰かが解決策を見つけたら、答えてください...

一時的な解決策として、Authトークンを静的プロップとしてAuthServiceクラスに保存できます。 その後、インスタンス化する必要はありません。

次のように、サービスを開始するために少しハックしてみることもできます。

constructor(private injector: Injector) {
  setTimeout(() => {
    this.authService = this.injector.get(AuthService);
  })
}

このようにして、最大コールスタック超過エラーが発生することはありません。

コンストラクターでauthServiceを設定するのではなく、インターセプト関数を使用することで解決しました。


  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // Get the auth header from the service.
    const auth = this.inj.get(AuthenticationService);
    const authToken = auth.getAuthorizationToken();
    ...
   }

それは完璧です@perusopersonale .....あなたは私の問題を解決します...

これは、いくつかのロギング機能を備えた最後のインターセプターであり、ローダーアニメーションのステータスをロードし続けます

import {Injectable, Injector} from "@angular/core";
import {HttpEvent, HttpHandler, HttpInterceptor, HttpResponse} from "@angular/common/http";
import {HttpRequest} from "@angular/common/http";
import {Observable} from "rxjs/Observable";
import {SiteService} from "../services/site.service";
import {Router} from "@angular/router";
import {LoadingService} from "../../components/loading/loading.service";
import {AuthenticationService} from "../services/authentication.service";

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

    constructor(private router: Router,
                private siteService: SiteService,
                private loadingService: LoadingService,
                private injector: Injector) {
    }



    private fixUrl(url: string) {
        if (url.indexOf('http://') >= 0 || url.indexOf('https://') >= 0)
            return url;
        else
            return this.siteService.apiDomain() + url;
    }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

        const clonedRequest = req.clone({
            headers: req.headers.set('Authorization', 'Bearer ' + localStorage.getItem('JWToken')),
            url: this.fixUrl(req.url)
        });

        let authenticationService = this.injector.get(AuthenticationService);

        this.loadingService.start();
        const started = Date.now();
        return next.handle(clonedRequest)
            .do(event => {
                if (event instanceof HttpResponse) {

                    const elapsed = Date.now() - started;
                    console.log('%c Request for ' + this.fixUrl(req.urlWithParams) + ' took ' + elapsed + ' ms.', 'background: #222; color: yellow');
                }
            })
            ._finally(() => {
                this.loadingService.stop();
            })
            .catch((res) => {
                if (res.status === 401 || res.status === 403) {
                    this.loadingService.start();
                    return authenticationService.refreshToken().flatMap((data: any) => {
                        this.loadingService.stop();
                        if (data.token !== '') {
                            localStorage.setItem('currentUser', JSON.stringify(data.user));
                            localStorage.setItem('currentUserPermissions', JSON.stringify(data.permissions));
                            localStorage.setItem('JWToken', data.token);
                        } else {
                            localStorage.removeItem('currentUser');
                            localStorage.removeItem('currentUserPermissions');
                            localStorage.removeItem('JWToken');
                            this.router.navigate(['./auth/login']);
                            return Observable.throw(res);
                        }
                        let clonedRequestRepeat = req.clone({
                            headers: req.headers.set('Authorization', 'Bearer ' + localStorage.getItem('JWToken')),
                            url: this.fixUrl(req.url)
                        });
                        return next.handle(clonedRequestRepeat).do(event => {
                            if (event instanceof HttpResponse) {

                                const elapsed = Date.now() - started;
                                console.log('%c Request for ' + req.urlWithParams + ' took ' + elapsed + ' ms.', 'background: #222; color: yellow');
                            }
                        });
                    })
                } else {
                    return Observable.throw(res);
                }

            });

    }
}

そして忘れないでください...

プロジェクトでこのインターセプターを使用する人にとってもう1つの重要なことは、ヘッダーを更新トークン応答に少なくとも数秒設定することです。

-> header( 'Cache-Control'、 'public、max-age = 45')
-> header( 'Expires'、date( 'D、d MYH:i:s'、time()+ 45)。 'GMT');

また...
そしてこれは私のloadingServiceです

`` `import {Injectable} from '@ angular / core';

@Injectable()
エクスポートクラスLoadingService {

public count = 0;

constructor() { }

start(): void {
    this.count++;
}

stop(): void {
    this.count--;
}

}
`` `

誰かが知らない場合に思い出させるためだけに。 RefreshTokenは、メイントークンの有効期限が切れていない場合にのみ取得できます。 トークンエンドポイントを呼び出し、更新トークンを投稿して、新しいaccessTokenを取得できます(たとえば、メイントークンの有効期限が切れる1時間前)。問題は次のとおりです。 このような場合、メソッドgetAccessToken()は、httpを公開し、必要に応じて更新トークンを使用して新しいトークンを取得するため、観察可能な結果を​​返します。
では、次をどのように処理しますか:循環依存の問題を処理し、オブザーバブルをインターセプターに注入しますか?

@perusopersonaleのスタックオーバーフローエラーは、循環依存関係が_ビルド時に_解決されたが、実行時にまだ非常に存在していることを意味します。インターセプター内から新しいリクエストを作成し、それがインターセプターを通過し、それが永久に繰り返されます。

私が使用することをお勧めnext.handle()従属要求を行う代わりに、のネタを取得しようとしてHttpClientそこは。

リクエストを行うのではなく、サービスから格納された値を取得する目的でサービスを注入したいのですが、そのサービスはHttpClientを使用してリクエストを行います。 この問題は解決されるべきではないと思います。

@nazarkryp手動インジェクション方式を使用すると、サービスの別のインスタンスを取得しているようです。 それは私がサービスのプロバイダーをどのように設定したかについての私のせいかもしれません(私はそれを一度だけ提供することに気を配ったと思いますが)。 あなたは同じことを見ていますか?

私は同じ問題を抱えていますが、上記の満足のいく答えはまだないようです。 これは閉じるべきではありません。

@ will-copperleafに同意しました。 私はまだこれをHttpClient / Interceptors一般の設計上の問題と見なしています。 インターセプターが自分でリクエストを行う必要があるのは、かなり一般的なケースです(特に、認証に使用されている場合は、ここでのほとんどのケースに当てはまると思います)。 インジェクターを注入してアドホックにサービスを要求するのは少しハッキーなようです。

Injectorインスタンスを取得してもうまくいきません。

エラー:

AppComponent_Host.html:1 ERROR TypeError: Cannot set property 'injector' of undefined
    at AuthHttp (authhttp.interceptor.ts:10)
    at eval (module.ngfactory.js? [sm]:1)
    at _callFactory (core.es5.js:9550)
    at _createProviderInstance$1 (core.es5.js:9489)
    at resolveNgModuleDep (core.es5.js:9471)
    at _callFactory (core.es5.js:9550)
    at _createProviderInstance$1 (core.es5.js:9489)
    at resolveNgModuleDep (core.es5.js:9471)
    at _createClass (core.es5.js:9514)
    at _createProviderInstance$1 (core.es5.js:9486)

AuthHttpクラス:

1.  import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
2.  import { Injectable, Injector } from '@angular/core';
3.  import { Observable } from 'rxjs/Rx';
4.  import { AuthService } from './auth.service';
5.
6.  @Injectable()
7.  export class AuthHttp implements HttpInterceptor {
8.     constructor(private injector: Injector) { }
9.
10.    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
11.        return next.handle(req);
12.    }
13.  }

@ will-copperleafどの例を試しましたか?

たとえば、これを実行できるはずです。

import { Injectable, Injector } from '@angular/core';
import { HttpInterceptor, HttpEvent, HttpRequest, HttpHandler } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { AuthService } from '../shared/auth.service';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  private authService: AuthService;

  constructor(
    private injector: Injector
  ) { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    this.authService = this.injector.get(AuthService); // get it here within intercept

    const authRequest = request.clone({
      headers: request.headers.set('Authorization', this.authService.getHeaders())
    });

    return next.handle(authRequest);
  }

}

したがって、基本的に、authServiceがトークンと更新トークンの両方を取得してlocalStorageに保存するためにhttpリクエストを実行する必要がある場合、これをどのように実装することを提案しますか?
上記の例では、interceptメソッド内のユーザーがauthServiceからヘッダーを追加するか、localStorageからトークンを直接追加していることがわかります(つまり、すでにトークンがあります)。
getHeaders()メソッドが非同期の場合はどうなりますか?

私が理解していることから、インターセプターから特定のリクエストを除外してインターセプターされないようにする方法はありません。特定のコンポーネントまたはモジュールのインターセプターをロードする方法もありません(アプリモジュールで定義する必要があります)。 。

私はauth.interceptor.tsファイルでこのソリューションを使用しています:

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

  if (req.url.includes('/token')) {
    return next.handle(req);
  }
  this.tokenService = this.injector.get(TokenService);
...
}

@twoheadedがやったことをやったのわけ

TokenServiceをHttpに依存させました。 少しハッキーですが、それが私のために働いた唯一の解決策でした。

このInterceptorsの概念全体は、現時点では実際には本番環境に対応していないように感じます。

@twoheadedのソリューションは私のために機能し、私はHttpClientを使用します。

同じ問題、引き続き懸念

@twoheadedのソリューションを使用した以下のセットアップは私にとってはreturn next.handle(req)役立つところです。 あなたがそれを返さない限り、このすべては無限にループするでしょう。 これは、 @ alxhubも上記で提案したもの

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

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  private authService: AuthService;

  constructor(private inj: Injector) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const headers = {'Content-Type': 'application/json'};

    if (req.url.includes('/token-refresh')) {
      console.log("Refresh token request");
      return next.handle(req);
    }

    this.authService = this.inj.get(AuthService);

    // Example of the token-refresh call
    if (this.authService.getToken()) {
      this.authService.refreshToken().subscribe(
        res => console.log(res)
      )
    }
    const copiedReq = req.clone({setHeaders: headers});
    console.log("Last");
    return next.handle(copiedReq);
  }
}

データを取得するためにインターセプターからhttpリクエストを送信する必要があり(つまり、再検証ロジック)、ベアxhr / fetchを使用したくない場合は、いつでもクラスインターフェイスHttpBackend使用してインターセプターを省略し、その方法でリクエストを送信できます。 。 さらに、テストのためにバックエンドをモックするときに機能します:smiley:

import { Inject, Injectable, InjectionToken } from '@angular/core';
import { HttpBackend } from '@angular/common/http';
import {
  HttpInterceptor,
  HttpEvent,
  HttpHandler,
  HttpRequest,
  HttpEventType,
  HttpErrorResponse,
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/retryWhen';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import 'rxjs/add/observable/of';

export const MY_REQUEST = new InjectionToken<HttpRequest<any>>('MY_REQUEST');

@Injectable()
export class MyInterceptor implements HttpInterceptor {
  constructor(
    @Inject(MY_REQUEST) private myRequest: HttpRequest<any>,
    private backend: HttpBackend
  ) {}

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    return next.handle(req).retryWhen(errors => {
      return errors.mergeMap((error: HttpEvent<any>) => {
        if (error instanceof HttpErrorResponse && error.status === 401) {
          return (
            this.backend
              .handle(this.myRequest)
              // Get response.
              .filter(event => event.type === HttpEventType.Response)
              // Retry on success.
              .mergeMap(() => Observable.of(null))
              // Stop and throw on error.
              .catch(() => Observable.throw(error))
          );
        }

        // Throw error otherwise.
        return Observable.throw(error);
      });
    });
  }
}

HttpHandlerすると、サードパーティライブラリで問題が発生する場合があります。これは、ユーザーがリクエストを変更できるインターセプターを追加できるためです。

プロバイダー解析エラー:循環依存をインスタンス化できません! InjectionToken_HTTP_INTERCEPTORS( "[ERROR->]"):in NgModule AppModule in ./ AppModule @ -1 :-1

httpclientを持つ別のサービスを使用しているとき。

Angular ^ 5.0.0-rc.2を試しましたが、まだ問題があります:(((((((((((((((

@perusopersonale完璧に動作しました。 インジェクターから直接認証サービスを取得することは素晴らしいオプションです。

HttpClientは、インターセプターなしでHttpBackend直接呼び出すオプションがあるはずだと思います。
追加機能として、カスタムインターセプターを使用するオプションがあります。

あなたはそれについてどう思いますか?

@vadjs

HttpClientには、インターセプターなしでHttpBackendを直接呼び出すオプションが必要だと思います。

今これを行うことができます- HttpBackend注入します。 または、 next: HttpHandlerを使用して、チェーン内の以前のインターセプターを経由せずに呼び出しを行うこともできます。

@alxhubがすでに可能である場合は、文書化する必要があるかもしれません。
その間に、「フェッチ」を使用してauthTokenを更新するインターセプターを実装しました...

コンストラクターにルーターを挿入するのと同じ問題があります。

@Injectable()
エクスポートクラスForbiddenInterceptorはHttpInterceptorを実装します{

コンストラクター(プライベートルーター:ルーター){
}

インターセプト(要求:HttpRequest、次:HttpHandler):Observable> {
next.handle(req).do(event => {を返す
console.log( "インターセプターが呼び出されました");
}、err => {
console.log( "インターセプター呼び出しエラー");

});

}
}

循環依存をインスタンス化できません! ApplicationRef( "[ERROR->]"):in NgModule AppModule in ./ AppModule @ -1 :-1

トークンの有効期限(HTTP 403)でログインにリダイレクトするルーターが必要です。 ルーターを手動で挿入します。例: injector.get(Router)が機能しませんでした(未定義を返します)

インジェクターを注入してgetメソッドを追加することで修正しました。

public get router():ルーター{
this.injector.get(Router);を返します。
}

Angular 4.3.0より前は、残念ながらinterceptメソッド内でInjector手動で使用することはできません。

ERROR Error: Uncaught (in promise): RangeError: Maximum call stack size exceeded

しかし、 rxjsを使用するもう1つの回避策があります:

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return Observable.create((observer: any) => {
        setTimeout(() => {
            const authService = this.injector.get(AuthenticationService)
            observer.next(authService.getAuthorizationHeader())
            observer.complete()
        })
    })
        .mergeMap((Authorization: string) => {
            let authReq = req

            if (Authorization) {
                authReq = req.clone({
                    setHeaders: {
                        Authorization
                    }
                })
            }

            return next.handle(authReq)
        })
}

@olegdunkan "Angular 4.3.0 "? 4.4.6を実行していますが、同じ問題が発生しています。 今のところ、 @ perusopersonaleの回避策を使用しています。

メインモジュールに{ deps:Http ]を追加しましたが、それは私のために機能しますが、なぜ必要なのか

プロバイダー:[
{{
提供:HTTP_INTERCEPTORS、
useClass:AuthTokenInterceptor、
マルチ:真、
deps:[Http]
}]、

それは完全に@perusopersonaleで動作しました。 どうもありがとう !

@perusopersonaleのソリューションを試しましたが、機能しません。 Maximum call stack size exceededエラーが発生します。 5.0.0ます。 他の誰かがこれをバージョン5で動作させることができますか?

@ vlad-ursan AuthServiceをインターセプターに挿入し、HttpClientをAuthServiceに手動で挿入していることを確認してください。 また、非推奨のHttpオブジェクトではなくHttpClientを使用していることを再確認してください。 これらの両方の理由により、5.0.0では失敗します。

Angularチームが実際に修正しようとしているのは、問題を回避するために他のものに注入されたものに他のものを注入する必要がないようにするためですか、それとも膨大なバックログの最下部にあるものですか?おそらく決して見られないので、これに対する独自のソリューションに永遠に頼ることができますか?
2番目のことが当てはまる場合は、おそらく、インターセプターがHttpClientに依存する非同期依存関係を受け入れることは想定されていないというドキュメントのメモが役立ちます。

@ vlad-ursanこれを試して、最大コールスタックサイズ超過エラー

screen shot 2017-11-14 at 12 58 03 pm

@ rbj325私は実際に `HttpClientを使用するAuthServiceを注入した別のサービス(ApiService)を使用しています。 手動で注入することの意味がわかりませんが。

@luillyfeインターセプターでは、まったく同じコードを使用しています。

@mixalistzikasインターセプターを使用しようとしましたが、実際には同様のインターセプターを使用していますが、ルートの解決

こんにちはみんな、私はリフレッシュトークンを使用するためのソリューションを作成し、ここに投稿しました: https

フィードバックは大歓迎です。

##私も

カスタムヘッダーでhttpを使用すると、AuthServiceが機能します。 しかし、HttpClientを追加すると、機能せず、同じ問題が発生します。

質問:応答とヘッダーを@ angular / httpから他のライブラリに変更する必要がありますか、または他の変更が必要ですか?

2位

サーバーレストサービスとの対話中に、json、observableなどにマッピングする必要はありませんか? 疑問に思う...。

これはバージョン5.1.1の問題であるというコメントを追加するだけです

AuthProviderをAuthProviderとAuthTokenProviderに分割することで、アプリケーションでこの問題を解決しました。

AuthTokenProviderの唯一の役割は、ストレージからトークンをフェッチする(またはフェッチしない)ことです。

インターセプターは、HTTPを使用しないAuthTokenProviderを使用するため、循環依存関係を作成しません。

AuthProviderもAuthTokenProviderを使用します。

@Injectable()
export class AuthTokenProvider {
  private tokenSubject: ReplaySubject<AuthToken> = new ReplaySubject(1);
  private fetchTokenObservable: Observable<AuthToken>;

  readonly TOKEN_STORAGE_KEY = 'auth.token';

  constructor(
    public storage: StorageProvider,
  ) {
    this.fetchToken().subscribe(() => {});
  }

  /**
   * <strong i="9">@returns</strong> {Observable<AuthToken>}
   */
  observeToken(): Observable<AuthToken> {
    return this.tokenSubject.asObservable();
  }

  /**
   * Return an observable that resolves when authProvider
   * is ready (token loaded from storage).
   * <strong i="10">@returns</strong> {Observable<AuthToken>}
   */
  observeReady(): Observable<AuthToken> {
    return this.fetchTokenObservable;
  }

  /**
   * <strong i="11">@param</strong> {AuthToken} token
   */
  public setToken(token: AuthToken): Observable<any> {
    return this.storage.set(this.TOKEN_STORAGE_KEY, token).map(() => {
      this.tokenSubject.next(token);

      return token;
    });
  }

  /**
   * Logout
   * <strong i="12">@returns</strong> {Promise<any>}
   */
  public removeToken(): Observable<any> {
    return this.storage.remove(this.TOKEN_STORAGE_KEY).map(() => {
      this.tokenSubject.next(null);
    });
  }

  /**
   * Fetch token from storage if available
   */
  private fetchToken(): Observable<AuthToken> {
    // This avoids multiple unnecessary storage calls
    if (!this.fetchTokenObservable) {

      this.fetchTokenObservable = this.storage.get(this.TOKEN_STORAGE_KEY).map((token) => {
        this.tokenSubject.next(token);
        return token;
      });

      return this.fetchTokenObservable;
    }
  }
}

こんにちは、私にとってはこのように働きます

`エクスポートクラスSecurityFactoryはHttpInterceptorを実装します{
プライベートauthService:AuthService;
コンストラクター(@Inject(forwardRef(()=>インジェクター))プライベートインジェクター:インジェクター){
}
インターセプト(要求:HttpRequest、次:HttpHandler):Observable> {
this.authService = this.injector.get(AuthService);
next.handle(req).do(event => {を返す
if(event ['status'] === 401){
this.authService.logout( 'expired');
}
});
}
}

エクスポート関数tokenGetter(){
sessionStorage.getItem( 'token');を返します。
}

@NgModule({
輸入:[
JwtModule.forRoot({
構成:{
tokenGetter:tokenGetter、
skipWhenExpired:true、
whitelistedDomains:['localhost:4200']
}
})
]、
プロバイダー:[
{提供:HTTP_INTERCEPTORS、useClass:SecurityFactory、multi:true}
]
})
エクスポートクラスAuthModule {
} `

私にとってはこのように機能します。

this.injector.get(AuthService).logout();

ねえ、私は何かを逃しているかもしれませんが、そのアプローチはサービスの場所を導入するので、 injector.getを使用することはお勧めしません。
これにより、インターセプターのテストが困難になります。

今のところ、 @angular/httpを使用する単一のサービスがあります。 このサービスは私のインターセプターによって使用されます。

この問題が解決したら、新しいHttpClient簡単に切り替えることができます。

AuthServiceをHttpClient(AuthHelper:ローカルストレージでのトークンの取得/設定と失敗したリクエストのキャッシュのみに関係するクラス)と元のAuthServiceに依存しないヘルパーサービスにリファクタリングすることで、@ iget-masterと同様にこれを解決しました。これには、HttpClientに依存する関数のみが含まれるようになりました。

元の依存関係グラフ:
HttpInterceptors --> AuthService --> HttpClient --> HttpInterceptors

固定依存グラフ:
HttpInterceptors --> AuthHelper <-- AuthService --> HttpClient -->HttpInterceptors

これにより、周期的な依存関係が修正されるだけでなく、関心の分離が改善され、コードがよりクリーンになります。 AuthServiceは、トークンをローカルに格納する実装と、アプリケーションサーバーからのトークンの要求/処理の実装の両方を懸念していました。 現在は後者のみを実行し、前者の責任をヘルパークラスに委任しています。

これは、インターセプター関数でhttpリクエストを作成したいというユースケースを解決しませんが( @serhiisolのコメントを参照)、非常に危険なユースケースのようです。 インターセプターが自身のリクエストをインターセプトするとどうなると思いますか?

今のところ、 @ GregOnNetと同じ方法で問題を解決しました。

トークンの更新リクエストが再び傍受されたため、このエラーが発生しました。 内部インターセプターからのHttp呼び出しは、インターセプターを再度起動します。これにより、別のhttp呼び出しが行われ、スタックがオーバーフローするまでインターセプターが再度起動されます。

@alxhubは、インターセプターの内部からHTTPリクエストを行う必要がある場合に、インターセプターへの再帰呼び出しを防ぐための優れたソリューションを投稿しました。

依存するサービス呼び出しで、HttpBackendを挿入し、HttpClientの代わりにそれを使用します。 ドキュメントごと

インターセプターは、HttpClientインターフェイスとHttpBackendの間にあります。 注入されると、HttpBackendは、インターセプターチェーンを経由せずに、リクエストをバックエンドに直接ディスパッチします。

このソリューションが機能しないユースケースは見当たらないので、Http Interceptorsのドキュメントを更新して、この状況でそれらをバイパスするためのHttpBackend使用法を指摘すれば、この問題を解決できると思います。

こんにちは、

私はAngularに少し慣れていないので、我慢してください:)

Angular 5.2.0にアップグレードした後、循環参照の問題が発生し、コンストラクターを介してHttpClientを挿入するのではなく、インジェクターを使用するというアドバイスに従い始めました。
JavaScriptのmsalを使用する認証サービス(Azure B2Cを使用)と、コンストラクターを介して「ConfigService」を挿入し、Httpgetを介してB2Cポリシー情報などを取得するために使用される認証サービスがあります。
B2CCallbackの戻り時にヘッダー「bearer」トークンを設定するインターセプターもあります

私の構成サービスは、コンストラクターを介してHttpClientを挿入し、メソッドを呼び出して、バックエンドAPIからデータをロードします。
AuthenticationServiceのConfigServiceインジェクションもインジェクターを使用するように変更しましたが、現在2つの問題が発生しています。

1)インターセプターはエラーのあるインジェクターにあまり満足していません:TypeError:this.inj.getは関数ではありません。

@Injectable()
export class AuthenticationHttpInterceptor implements HttpInterceptor {
    private authenticationService: AuthenticationService;

    constructor(private inj: Injector) {    }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

        this.authenticationService = this.inj.get(AuthenticationService);
        return Observable.fromPromise(this.authenticationService.getAuthenticationToken())
            .switchMap(token => {
                req = req.clone({
                    setHeaders: {
                        Authorization: `Bearer ${token}`
                    }
                });
                return next.handle(req);
            });
    }
}

2)AuthenticationServiceがあらゆる種類の未定義の型を返すため、構成サービスが呼び出されなくなりました。 具体的にはここに:

`コンストラクター(プライベートインジェクター:インジェクター){
this.configService = inj.get(ConfigService);

    this.b2CConfig = this.configService.getB2CConfig;
    this.authority = `https://login.microsoftonline.com/tfp/${this.b2CConfig.tenant}/${this.b2CConfig.signUpSignInPolicy}`;`

アドバイスをいただければ幸いです。

ありがとう
マイク

新しいHttpClientに切り替えることができ、それが修正されるとお考えの方のために、私は数か月間切り替えましたが、ここで問題が発生します。

新しいHttpClientに切り替えることをお勧めします...それはあなたにたくさんの素晴らしい機能を与えるでしょう。 しかし、アプリ全体にエラーハンドラー(ErrorHandlerインターフェイス)があり、HttpClientをErrorHandlerに挿入しようとしたときに同じエラーが発生しました。

    constructor(http: HttpClient) {
    }

ErrorHandlerは実際にはHttpからのエラーもインターセプトするため、ここで問題が発生しました。したがって、injector.getの方法がそれを解決する唯一の方法でした...そして、これは私の大きなアプリでInjectorを使用している唯一の場所です。 より良い方法を見つけたら、またはこれに対する修正があったら、それを削除したいと思います。

@Injectable()
export class ErrorHandlerService implements ErrorHandler {
    private http: HttpClient;

    constructor(private injector: Injector) {
        // Cyclic reference if I inject httpClient through constructor
        setTimeout(() => {
            this.http = this.injector.get(HttpClient);
        })
    }

    handleError(error: any) {
        console.log('Client Error:', error);
    }
}

これは5.2.3のリリースで終了したようです(変更ログに記載されています)。
しかし、それでも機能しません。
5.2.3に更新しました(最初にnode_modulespackage-lock.json削除しました)
次に、インターセプターの古いコードを置き換えました。

  constructor(
    private injector: Injector
  ) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const authService = this.injector.get(AuthService);
    const token = authService.getToken();
    const reqCopy = req.clone({
      headers: req.headers.append('Authorization', `Bearer ${token}`)
    });
    return next.handle(reqCopy);
  }

それがどうあるべきか:

  constructor(
    private authService: AuthService
  ) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = this.authService.getToken();
    const reqCopy = req.clone({
      headers: req.headers.append('Authorization', `Bearer ${token}`)
    });
    return next.handle(reqCopy);
  }

また、 --prodフラグを使用してビルドしようとすると、循環依存エラーが発生します。

@MrCroft角度5.2.3に更新しましたが、問題なく動作しています。
yarn upgrade --scope @angular
たぶんあなたのケースではもっと問題を引き起こしている何かがあります。

--prodフラグを使用してビルドしようとすると、循環依存エラーも発生します。

角度5.2.2を使用したときに同じ周期エラーが発生していました。
Visual Studio Codeでnpm updateし、5.2.4に更新しました。 これ以上のエラーはありません。 Angularチームが最新リリースで問題に対処したようです。

@jzahoor修正についてもっと知りたいのですが。
私の回避策(数ヶ月前)は、角度を完全にバイパスするためにインターセプター内で「フェッチ」を使用していました...
ありがとう

@melicerte応答が遅れて申し訳ありませんが、ホスティングプロバイダーに支払う必要があることに気づいていませんでした。 理由がわかるまで、数日間メール通知が届かないのは変でした:)

トピックについて:ユニバーサルアプリを使用している場合でも、エラーが発生します。 もちろん、新しいアプリを最初から作成することを決定するまで、これに気づきませんでした😄
ユニバーサルアプリを作成する必要はありません。必要なのはそこにあることだけです。
ng serve --prodまたはng build --prodを実行するだけでエラーがスローされます(これにより、apps配列の最初のアプリがビルドされます。私の場合はクライアントです)。

@alxhubここ
npm installng serve --prod (またはクライアントアプリをビルドする場合はng build --prod )を実行するだけで、エラーが発生します。
奇妙なことに、クライアントアプリを作成しているだけなのに、 app.server.moduleと表示されます。
これがAngularではなくCLIの問題であるかどうかわかりませんか?

Angular CLI: 1.6.8
角度: 5.2.4
ノード:8.9.4
OS:win32 x64

@marcincichockiMY_REQUESTがインターセプターにどのように送信されているかを知りたい。 authなどの特定の目的でインターセプターをバイパスするというソリューションが好きですが、既存のインターセプターで例を使用する方法がわかりません。

@jdhinesその特定の例では、依存性注入を介してMY_REQUESTトークンを提供する必要があります。

@NgModule({
  providers: [
    {
      provide: MY_REQUEST,
      useValue: new HttpRequest() // set your HttpRequest here, or use factory function if you need DI
    }
  ]
})

ただし、これは、移植性が高く再利用可能なコードが必要な場合に適しています(たとえば、異なるバックエンドを使用する複数のアプリがあるとします)。 インターセプター自体のプロパティとしてHttpRequestを設定するだけです。

@Injectable()
export class MyInterceptor  {
  myRequest = new HttpRequest()
}

@marcincichocki :ありがとう(そしてここのスレッドについてお詫びします)。 おそらく基本的な質問はご容赦ください。 これはすべて私にとってかなり異質ですが、 myRequestは、私の認証サービスのhttp.get('userInfo')のバックエンドの代役ですか? 実際にnew HttpRequest()何かを渡すと思いますか? もしそうなら、何?

@jdhinesはい、リクエストを説明する引数を渡す必要があります。 そのクラスにはオーバーロードがほとんどありません

https://github.com/angular/angular/blob/c8a1a14b87e5907458e8e87021e47f9796cb3257/packages/common/http/src/request.ts#L129 -L164

最も一般的には、次のようなものを使用します。

const myRequest = new HttpRequest('POST', 'url/here', payload, options) // post
const myRequest2 = new HttpRequest('GET', 'url/here', options) // get

Ionicでこの問題が発生した場合は、最大コールスタックサイズの超過に苦労している可能性があります。 platform.ready()。then(()=> [...]);内でinj.get(...)を実行するだけです。 インターセプターの内部!

私は同様の循環依存関係で立ち往生し、サービスを手動で注入する回避策を実行することにしましたhttps://stackoverflow.com/questions/49240232/getting-a-cyclic-dependency-error

私はまだこの問題を抱えています、とにかく誰かが解決しましたか?
ヘッダーに承認を含めるためのインターセプターを作成しようとしています。

エラー:エラーエラー:キャッチされていません(約束されています):RangeError:最大コールスタックサイズを超えました

私のコード:

`` `
インターセプト(リクエスト:HttpRequest、次:HttpHandler):Observable> {
tokenProvider =this.injector.get(TokenProvider);

return Observable.create((observer: any) => {
  setTimeout(() => {
    observer.next(tokenProvider.getAsyncToken());
    observer.complete();
  });
}).mergeMap((token) => {
  if(token){
    request = request.clone({
      setHeaders: {
        Authorization: `Bearer ${token}`
      }
    });
  }
  return next.handle(request);
});

}

私のTokenProvider:

async getAsyncToken():約束{{
新しいPromise((resolve)=> {を返す
トークン= localStorage.getItem( 'token');
時間= Math.round(+ new Date()/ 1000);
if(!token){
this.http.post${this.urlApi}/${this.urlAuth} 、{タイムスタンプ:時間})。subscribe(data => {
resolve(data.token);
}、err => {
console.log(err);
});
}そうしないと{
resolve(token);
}
});
}

@thiagodamicoこれで、あなたとまったく同じエラーメッセージが表示され、ソースコードの何にも触れていないと思います。

私も。

他の誰もこれを指摘していない理由はわかりませんが、 HttpInterceptingHandlerを導入することで、Angular 6で修正されているようです。これは、インターセプターを遅延ロードし、循環依存を回避します。

HttpInterceptingHandlerの使用例はありますか?

@ ronen1malkaこのクラスはパブリックAPIの一部ではないため、使用しないでください。 6.0.2バージョンにアップグレードして、HttpInterceptorクラスを使い続けるだけです。

@simeylaはHttpInterceptorHandlerについて指摘しました。良い点です。
しかし、なぜAngularチームは、HttpClientシグネチャを使用してインターフェイスを追加するだけでなく、循環依存の問題を解決するためにさらに別の具象クラスを作成することにしたのでしょうか。

悪い部分はそれをアップグレードする何かが機能していないことです!! NPMにたくさんの汚れが押し付けられている

Ionicでこの問題が発生した場合は、最大コールスタックサイズの超過に苦労している可能性があります。 platform.ready()。then(()=> [...]);内でinj.get(...)を実行するだけです。 インターセプターの内部!

イオン3および角度5で動作します

角度6で同じ問題が発生しましたが、この方法で解決しましたhttps://github.com/angular/angular/issues/18224#issuecomment -329939990

@Toxicableありがとう、あなたの解決策は私のために働きます!

角度5.0.xでも同じ問題が発生しましたが、角度5.2.11で問題が修正されているようです。 @Toxicableのsulotionは5.0.xと5.2.11の両方で機能します

これも機能します:

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

@Injectable({
  providedIn: 'root'
})
export class JwtInterceptorService implements HttpInterceptor {

  constructor(private authService: AuthService) {
  }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // Add authorization header with bearer token if available.
    const token: string = this.authService.tokenValue;
    if (token) {
      request = request.clone({
        setHeaders: {
          Authorization: `Bearer ${token}`
        }
      });
    }

    return next.handle(request).pipe(catchError((error: HttpErrorResponse) => {
      // Auto logout if 401 response returned from api
      if (error.status === 401) {
        this.authService.unAuthUser();
      }
      return throwError(error);
    }));
  }
}

この問題は、非アクティブのために自動的にロックされています。
同様の問題または関連する問題が発生した場合は、新しい問題を提出してください。

自動会話ロックポリシーの詳細をご覧ください。

_このアクションはボットによって自動的に実行されました。_

このページは役に立ちましたか?
0 / 5 - 0 評価