6

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.`);
    }
  }

}
Simone Romano
  • 165
  • 3
  • 10

5 Answers5

7

Looks, like you have to add dependencies for your interceptor. In app.module.ts instead of just edding to provide section ErrorHandlerInterceptor declare you provider in such way:

{
  provide: HTTP_INTERCEPTORS,
  useClass: ErrorHandlerInterceptor,
  multi: true ,
  deps: [AuthService, StorageService]
},

Be careful, the order of services in deps should be the same, like in constructor.

PS. Not sure about Angular 6, but for Angular 8,9 it works perfectly.

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
Babyburger
  • 1,730
  • 3
  • 19
  • 32
3

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!

Simone Romano
  • 165
  • 3
  • 10
  • Could you paste the ErrorHandlerInterceptor code with the solution? I tried here in Angular 6 make similar solution and did'nt work for me. – Adriano Frota Dec 11 '18 at 18:48
  • Hello @AdrianoFrota, sorry for the delay; If you still have this problem, I put a code snippet about my solution to help you, if needed. – Simone Romano Dec 18 '18 at 13:20
  • 1
    This solution did not work for me. The problem has been shifted to `injector` being undefined in `errorHandler`. – Babyburger Apr 04 '20 at 10:18
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
    );
}

}

ryanovas
  • 402
  • 1
  • 5
  • 11
  • Thanks, this solved it for me as well! I wonder why the global variable gets lost when calling a separate function though? – Babyburger Apr 04 '20 at 10:10
1

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;
}
nelsontruran
  • 514
  • 4
  • 18