问题
I can't find any way to inject my AuthService inside ErrorHandlerInterceptor. It returns me either an "undefined" object after injection, or it throws an error.
This is my ErrorHandlerInterceptor:
import { Injectable } from '@angular/core';
import { AuthService } from '@app/auth.service';
import { StorageService } from '@app/storage.service';
@Injectable({
providedIn: 'root'
})
export class ErrorHandlerInterceptor implements HttpInterceptor {
constructor(private authService: AuthService, private storageService: StorageService) {
console.log(this.authService); // undefined
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(catchError(error => this.errorHandler(error)));
}
// Customize the default error handler here if needed
private errorHandler(response: HttpErrorResponse): Observable<HttpEvent<any>>
{
// ... Various code
}
}
And this is my AuthService:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { StorageService } from './storage.service';
import { Router } from '@angular/router';
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor(private _http: HttpClient,
private storageService: StorageService,
private router: Router) { }
}
I tried to list the service in the core.module.ts
providers, but errors are thrown:
ERROR RangeError: Maximum call stack size exceeded
at setCurrentInjector (core.js:1382)
at resolveNgModuleDep (core.js:8333)
at _createClass (core.js:8425)
at _createProviderInstance (core.js:8393)
at resolveNgModuleDep (core.js:8356)
at _createClass (core.js:8425)
at _createProviderInstance (core.js:8393)
at resolveNgModuleDep (core.js:8356)
at _createClass (core.js:8425)
at _createProviderInstance (core.js:8393)
Note that I am using the framework ngx-rocket
, created by ngx-rocket-generator
.
How can I do to fix this issue? Any advices?
UPDATE 1 -- CORE.MODULE.TS
Here is the core.module.ts file.
import { NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http';
import { RouteReuseStrategy, RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { ShellComponent } from './shell/shell.component';
import { HeaderComponent } from './shell/header/header.component';
import { RouteReusableStrategy } from './route-reusable-strategy';
import { AuthenticationService } from './authentication/authentication.service';
import { AuthenticationGuard } from './authentication/authentication.guard';
import { I18nService } from './i18n.service';
import { HttpService } from './http/http.service';
import { HttpCacheService } from './http/http-cache.service';
import { ApiPrefixInterceptor } from './http/api-prefix.interceptor';
import { ErrorHandlerInterceptor } from './http/error-handler.interceptor';
import { CacheInterceptor } from './http/cache.interceptor';
import { TokenInterceptor } from './http/token.interceptor';
import { StorageService } from '@app/storage.service';
import { AuthService } from '@app/auth.service';
@NgModule({
imports: [
CommonModule,
HttpClientModule,
TranslateModule,
NgbModule,
RouterModule
],
declarations: [
HeaderComponent,
ShellComponent
],
providers: [
AuthenticationService,
AuthenticationGuard,
I18nService,
HttpCacheService,
ApiPrefixInterceptor,
ErrorHandlerInterceptor,
CacheInterceptor,
TokenInterceptor,
{
provide: HttpClient,
useClass: HttpService
},
{
provide: RouteReuseStrategy,
useClass: RouteReusableStrategy
}
]
})
export class CoreModule {
constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
// Import guard
if (parentModule) {
throw new Error(`${parentModule} has already been loaded. Import Core module in the AppModule only.`);
}
}
}
回答1:
Finally, I solved the problem. In the error handler, the dependencies cannot be injected through the constructor. To solve it, you need to do this:
First, import the Injector from @angular/core
and your service:
import { Injector } from '@angular/core';
import { AuthService } from '@app/auth.service';
Then, you have to inject it in the constructor:
constructor(private modalService: NgbModal, private injector: Injector) { }
And then you have to instantiate your service and use it like this:
const authService = this.injector.get(AuthService);
authService.logout();
The code, is going to be similiar to this:
import { Injector } from '@angular/core';
import { Router } from '@angular/router';
@Injectable({
providedIn: 'root'
})
export class ErrorHandlerInterceptor implements HttpInterceptor {
private authService: AuthService;
constructor(private modalService: NgbModal, private router: Router, private injector: Injector) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(catchError(error => this.errorHandler(error)));
}
// Customize the default error handler here if needed
private errorHandler(response: HttpErrorResponse): Observable<HttpEvent<any>> {
this.authService = this.injector.get(AuthService);
... Other code ...
}
Hope you have been helped by this answer!
回答2:
I had this exact problem and tried the solution of using Injector in the error handler, but that didn't work for me either. Neither my service or injector were defined in my error handler. I tried a lot of things but what worked for me was using an anonymous function instead of writing a new one. I realized that in my intercept function my service was available, but something about moving to a new function was causing it to become undefined. For whatever reason, using an anonymous function kept my service in scope.
Below I put my code, the service in question is GlobalMessagesService. Hopefully this helps someone else through this headache.
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';
import { Router } from "@angular/router";
import { environment } from '../../environments/environment';
import { GlobalMessagesService } from '../global-messages/global-messages.service';
@Injectable()
export class CustomHTTPSettings implements HttpInterceptor {
constructor(
private router: Router,
private MessagesService: GlobalMessagesService
) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
request = request.clone({
setHeaders: headers
});
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => { // this has to be inline or for some reason services aren't available
if (environment.production != true) {
console.error(error);
}
switch (error.status) {
case 401:
this.router.navigate(['/login']);
return of(error);
case 500:
this.MessagesService.addGlobalMessage({
message: 'Oops something went wrong. Please try again',
color: 'red'
});
throw error;
}
}) as any // then handle the error
);
}
}
回答3:
I found a solution in github - Service with http call in constructor is injected as undefined in HttpInterceptor
static translateService;
constructor(translateService: TranslateService) {
ErrorInterceptor.translateService = translateService;
}
回答4:
The injected constructor variable is not available from the function you pass to the catchError
function. You need to access the router
directly in your ´intercept method´ like this:
constructor(private router: Router) {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((errorResponse: HttpErrorResponse) => {
// this.router is defined here
})
);
}
The problem seems to lie within the catchError
. If you print the current scope this
in both the intercept
and catchError
functions you get MyInterceptor and CatchSubscriber respectively. this.router
is not available from the CatchSubscriber. You can still use separate functions by adding a private method in your interceptor class:
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((errorResponse: HttpErrorResponse) => {
this.handleError(errorResponse);
})
);
}
private handleError(errorResponse: HttpErrorResponse) {
// this.router is defined here
}
To summarize:
catchError(this.handleError) // does not work because of different scope
catchError(err => this.handleError(err)) // works because of same scope
来源:https://stackoverflow.com/questions/52165619/angular-6-service-is-undefined-after-injecting-in-interceptor