Angular 6 Service is undefined after injecting in Interceptor

烂漫一生 提交于 2020-04-11 07:54:33

问题


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

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