Angular HttpInterceptor - Any Service Injected returns Undefined TypeError

霸气de小男生 提交于 2020-07-22 06:10:01

问题


Problem:

I'm unable to inject ANY service into the constructor of my HttpInterceptors. Any service that I attempt to inject into the service is met with the following error:

TypeError: Cannot set property 'authenticationService' of undefined

This goes for even a dummy foo service with a single function bar and no additional dependency injected into it.

THE CODE

interceptor.ts

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

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

    constructor(private authenticationService: AuthenticationService) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        if (request.url.includes(location.hostname) && this.authenticationService.getToken()) {
            console.log('Headers added to the HTTP Request');
            request = request.clone({
                setHeaders: {
                    Authorization: `Bearer ${this.authenticationService.getToken()}`
                }
            });
        }
        return next.handle(request);
    }
}

authentication.service.ts

import { OnInit, Injectable } from '@angular/core';
import { AuthServiceConfig, AuthService as SocialAuthService, FacebookLoginProvider, GoogleLoginProvider, SocialUser} from 'angularx-social-login';
import { HttpClient, HttpRequest } from  '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
import { environment } from '../../../environments/environment';
import { JwtHelperService } from '@auth0/angular-jwt';

@Injectable( { providedIn: "root" } )

export class AuthenticationService implements OnInit{

  jwtHelper: JwtHelperService = new JwtHelperService();
  socialLoginConfig: AuthServiceConfig;
  loggedIn: BehaviorSubject<Boolean> = new BehaviorSubject<Boolean>(false);
  loggedIn$: Observable<Boolean> = this.loggedIn.asObservable();
  user: BehaviorSubject<SocialUser> = new BehaviorSubject(null);
  user$: Observable<SocialUser> = this.user.asObservable();

  cachedRequests: Array<HttpRequest<any>> = [];

  constructor(private socialAuthService: SocialAuthService, private http: HttpClient) { }

  ngOnInit() {
    this.socialAuthService.authState.subscribe((user) => {
      this.user.next(user);
      this.loggedIn.next((user != null));
    });
  }

  provideConfig() {
    return this.socialLoginConfig;
  }

  getToken(): string {
    return localStorage.getItem('token');
  }

  refreshToken(): void {
    // Need to add a call to refresh the token (JWT) to the backend.
  }

  isAuthenticated(): boolean {
    const token = this.getToken();
    return token != null && !this.jwtHelper.isTokenExpired(token);
  }

  collectFailedRequest(request): void {
    this.cachedRequests.push(request);
  }

  retryFailedRequests(): void {
    // Once the token has been refreshed, we can send previous failed requests from the cahcedRequests array...
  }

  signInWithGoogle(cb): void {
    this.socialAuthService.signIn(GoogleLoginProvider.PROVIDER_ID).then(
      (userData) => { //on success
        console.log('google', userData);
        this.user.next(userData);
        this.sendToRestApiMethod(userData.idToken, 'google', cb);
      }
    ).catch(err => {
      console.log('Error logging into Google Services:', err);
    });
  }

  signInWithFB(cb): void {
    this.socialAuthService.signIn(FacebookLoginProvider.PROVIDER_ID).then(
      (userData) => { //on success
        console.log('facebook', userData);
        this.user.next(userData);
        this.sendToRestApiMethod(userData.authToken, 'facebook', cb);
      }
    ).catch(err => {
      console.log('Error logging into Facebook Services:', err);
    });
  }

  signOut(): void {
    this.socialAuthService.signOut();
    this.user.next(null);
  }

  sendToRestApiMethod(token: string, provider: string, cb) : void {
    this.http.post(environment.apiBaseUrl +'oauth2/authorization/' + provider, { token: token } )
      .subscribe(jwt => {
       console.log('login was successful', jwt);
       localStorage.setItem('token', jwt['jwt']);
       cb();
     }, onFail => {
        console.log('login was unsuccessful', onFail);
        //show an error message
     }
   );
 }
}

app.module.ts

import { environment } from '../environments/environment';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

import { LoginComponent } from './modules/public/pages/login/login.component';
import { SocialLoginModule, AuthServiceConfig } from "angularx-social-login";
import { GoogleLoginProvider, FacebookLoginProvider } from "angularx-social-login";

import { NavbarComponent } from './shared/components/navbar/navbar.component';
import { FooterComponent } from './shared/components/footer/footer.component';
import { HomeComponent } from './modules/public/pages/home/home.component';
import { UserComponent } from './modules/secure/pages/user/user.component';

import { HttpClientModule, HTTP_INTERCEPTORS} from '@angular/common/http';
import { Interceptor } from './core/interceptors/interceptor';
import { DashboardComponent } from './modules/secure/pages/dashboard/dashboard.component';

const socialLoginConfig = new AuthServiceConfig([
  { id: GoogleLoginProvider.PROVIDER_ID, 
    provider: new GoogleLoginProvider(environment.google.clientid) },
  { id: FacebookLoginProvider.PROVIDER_ID, 
    provider: new FacebookLoginProvider(environment.facebook.appid) }
]);

export function provideConfig() {
  return socialLoginConfig;
}
@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    LoginComponent,
    UserComponent,
    NavbarComponent,
    FooterComponent,
    DashboardComponent
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    AppRoutingModule,
    SocialLoginModule,
    HttpClientModule
  ],
  providers: [
    { provide: AuthServiceConfig, useFactory: provideConfig },
    { provide: HTTP_INTERCEPTORS, useFactory: Interceptor, multi: true }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

For people questioning the Angular version...

Angular CLI: 7.2.3
Node: 11.7.0
OS: darwin x64
Angular: 7.2.2
... animations, cdk, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.12.3
@angular-devkit/build-angular     0.12.3
@angular-devkit/build-optimizer   0.12.3
@angular-devkit/build-webpack     0.12.3
@angular-devkit/core              7.2.3
@angular-devkit/schematics        7.2.3
@angular/cli                      7.2.3
@ngtools/webpack                  7.2.3
@schematics/angular               7.2.3
@schematics/update                0.12.3
rxjs                              6.3.3
typescript                        3.2.4
webpack                           4.28.4

Research/Troubleshooting Performed:

I found various SO posts and other forum posts describing issues when injecting a service that contains a HttpClient reference will cause a cyclic dependency error leading to the undefined, and authenticationService DOES have a reference to HTTPClient. However, according to the posts, this was fixed in an Angular 5 patch. As you can see, I'm currently using Angular 7 for this project.

I have attempted to follow these instructions and instead of injecting the authenticationService into the constructor, injecting an Injector and then performing:

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

This lead to the error:

TypeError: Cannot set property 'injector' of undefined

I've also tried to change the app.module.ts provider for the HttpInterceptor to the following:

{ provide: HTTP_INTERCEPTORS, useFactory: Interceptor, multi: true, deps: [AuthenticationService] }

This resulted in, yet again, the same undefined error.

Conclusion:

Apologies for the novel, but I wanted to ensure everyone had all possible information to help solve this issue that's caused me 3 days worth of headaches. Thanks all in advance!


回答1:


I had the same problem of yours and I solved by switching from useFactory to useClass in the declaration of the module:

This was a working Angular 7.1 code

{
    provide: HTTP_INTERCEPTORS,
    useFactory(router: Router, authenticationStorageService: AuthenticationStorageService) {
        // a lot of stuff here

        return new AuthInterceptor(router, authenticationStorageService);
    },
    multi: true,
    deps: [Router, AuthenticationStorageService]
},

which is broken in Angular 8. Throwing away the factory solves the issue:

{
    provide: HTTP_INTERCEPTORS,
    useClass: AuthInterceptor,
    multi: true,
    deps: [Router, AuthenticationStorageService]
},

I had to shift the factory logic in the constructor of the class though. The factory approach was more flexible as you could change the returned instance from time to time, but probably an overkill in the standard case.



来源:https://stackoverflow.com/questions/54598097/angular-httpinterceptor-any-service-injected-returns-undefined-typeerror

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!