问题
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