Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NG0200: Circular dependency in DI detected for InjectionToken HTTP_INTERCEPTORS

Angular CLI: 11.0.1
Node: 14.8.0
OS: darwin x64

Angular: 11.0.0
... animations, cdk, common, compiler, compiler-cli, core, forms
... language-service, material, platform-browser
... platform-browser-dynamic, router
Ivy Workspace: Yes

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1002.0
@angular-devkit/build-angular   0.1100.1
@angular-devkit/core            10.2.0
@angular-devkit/schematics      11.0.1
@angular/cli                    11.0.1
@schematics/angular             11.0.1
@schematics/update              0.1100.1
rxjs                            6.6.3
typescript                      4.0.5

when i use translateService with httpInterceptor, i got this error:

NG0200: Circular dependency in DI detected for InjectionToken HTTP_INTERCEPTORS

It works well if i don't use translateService,can anyone help me solve this problem,here is the codes

app.component.ts

    import { Component } from '@angular/core';
import { ElectronService } from './services';
import { TranslateService } from '@ngx-translate/core';
import { AppConfig } from '../environments/environment';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  constructor(
    private electronService: ElectronService,
    private translate: TranslateService
  ) {
    this.translate.setDefaultLang('en');
    console.log('AppConfig', AppConfig);
  }
}

core.module.ts

import {InjectionToken, NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {BrowserModule} from '@angular/platform-browser';
import {FormsModule} from '@angular/forms';
import {HttpClient, HttpClientModule} from '@angular/common/http';
import {SharedModule} from '../shared/shared.module';
import {AppRoutingModule} from '../app-routing.module';
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
import {TranslateHttpLoader} from '@ngx-translate/http-loader';
import {AppStoreModule} from '../store/app-store.module';
import {ViewsModule} from '../views/views.module';
import {httpInterceptorProvides} from '../services/http-interceptor';
import {API_BASE_URL} from '../services/service.module';

// AoT requires an exported function for factories
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
  return new TranslateHttpLoader(http, '../../assets/i18n/', '.json');
}

@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    BrowserModule,
    FormsModule,
    HttpClientModule,
    AppStoreModule,
    SharedModule,
    ViewsModule,
    AppRoutingModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: HttpLoaderFactory,
        deps: [HttpClient]
      }
    })
  ],
  providers: [
    {
      provide: API_BASE_URL,
      useValue: 'https://demo.xx.com'
    },
    httpInterceptorProvides,
  ],
  exports: [AppRoutingModule, SharedModule]
})
export class CoreModule {
}

httpinterceptor.ts

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

@Injectable()
export class CommonInterceptor implements HttpInterceptor {
  skipUrl: string[];

  constructor(private auth: AuthService, @Inject(API_BASE_URL) private uri: string) {
    this.skipUrl = ['/oauth/token', 'assets/i18n'];
  }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const req = request.clone({
      setHeaders: {
        'Accept': 'dddd'
      },
      url: this.uri + request.url
    });
    // return next.handle(req);

    if (this.isSkipAuth(req.url)) {
      return next.handle(req);
    } else {
      return this.auth.getToken()
        .pipe(
          switchMap((access_token) => {
            const reqA = req.clone({
              setHeaders: {
                'Authorization': 'Bearer ' + access_token
              }
            });
            return next.handle(reqA);
          })
        );
    }
  }

  isSkipAuth(url: string): boolean {
    let isMatch = false;
    this.skipUrl.forEach((reg_url: string) => {
      if (!isMatch) {
        if (url.search(reg_url) >= 0) {
          isMatch = true;
        }
      }
    });
    return isMatch;
  }
}

auth.service.ts

import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Observable, of} from 'rxjs';
import {ElectronService} from './electron/electron.service';
import {map, pluck, tap} from 'rxjs/operators';
import {ResponseType} from './data-types/common.type';

export interface TokenModel {
  access_token: string; // access_token
  expires: number; // 
  expires_in: number; // 
}



@Injectable({
  providedIn: 'root'
})
export class AuthService {
  elStoreIns: any;

  constructor(private http: HttpClient, private elService: ElectronService) {
    this.elStoreIns = this.elService.elStore;
  }

  getToken(): Observable<string> {
    const now = Date.now();
    const tokenStore: TokenModel = this.elStoreIns.get('token');
    if (tokenStore instanceof Object) {
      if ((now - tokenStore.expires) <= 7100 * 1000) {
        return of(tokenStore.access_token);
      }
    }
    return this.requestToken();
  }

  requestToken(): Observable<string> {
    return this.http.post<ResponseType>('/oauth/token', {
      client_ids: 1,
      client_secrets: 'ddd',
      grant_types: 'dddd'
    }).pipe(
      pluck('data'),
      tap((data: any) => {
        const tokenData: TokenModel = {
          access_token: data.access_token,
          expires_in: data.expires_in,
          expires: Date.now()
        };
        this.saveToken(tokenData);
      }),
      map(data => data.access_token)
    );
  }

  saveToken(tokenStore: TokenModel): void {
    this.elStoreIns.set('token', tokenStore);
  }
}
like image 483
yuemjng.liu Avatar asked Sep 13 '25 00:09

yuemjng.liu


2 Answers

At first, angular will raise a "NG0200: Circular dependency" error as soon as an error is thrown in a service constructor. Even if this is not related to any dependency injection.

Now regarding interceptors, in short you cannot use a service within your interceptor itself using HttpClientbecause the HttpClient has a dependency on the interceptor. A workaround is to provide a brand new instance of HttpClient free of any inteceptors.

like image 112
Flavien Volken Avatar answered Sep 15 '25 15:09

Flavien Volken


run this command and you will see in which files circular dependencies located.

npx madge --circular --extensions ts ./

Then fix imports.

like image 39
Jean Seven Avatar answered Sep 15 '25 15:09

Jean Seven