angular - 我的拦截器上未定义构造函数变量(Angular 8)

标签 angular typescript web ionic-framework mobile

我有一个名为拦截器的服务,它可以获取后端发送的所有错误响应。在这个服务上,我有一个名为 AuthService 的服务(向后端发出申请的服务),它在我的构造函数变量上声明。我使用这个另一个服务来调用向用户显示消息的方法。
同时,当拦截器被触发时,AuthService 未定义,并且我的消息没有显示。
在尝试解决该问题时,我尝试在 appModule 的提供者上声明 authService。它解决了未定义的问题,但出现了另一个问题。因此,我得出结论,这不是解决方案。
下面是我的字体:
拦截器.ts

import { LoadingController } from '@ionic/angular';
import { Injectable } from '@angular/core';
import {
    HttpEvent,
    HttpHandler,
    HttpInterceptor,
    HttpRequest,
    HttpHeaders,
    HttpErrorResponse
} from '@angular/common/http';

import { BehaviorSubject, throwError, Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { Router } from '@angular/router';
import { AuthService } from '../services/auth.service';

@Injectable()
export class HTTPStatus {
    private requestInFlight$: BehaviorSubject<boolean>;

    constructor() {
        this.requestInFlight$ = new BehaviorSubject(false);
    }
    setHttpStatus(inFlight: boolean) {
        this.requestInFlight$.next(inFlight);
    }
    getHttpStatus(): Observable<boolean> {
        return this.requestInFlight$.asObservable();
    }
}

@Injectable()
export class Interceptor implements HttpInterceptor {

    constructor(
        private _loadingController: LoadingController,
        private _authService: AuthService, //This is the variable that comes undefined
        private _router: Router
    ) {

    }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return this.handleAccess(request, next);
    }

    private handleAccess(request: HttpRequest<any>, next: HttpHandler):
        Observable<HttpEvent<any>> {
        const token = JSON.parse(localStorage.getItem('currentToken'));
        let changedRequest = request;
        // HttpHeader object immutable - copy values
        const headerSettings: { [name: string]: string | string[]; } = {};

        for (const key of request.headers.keys()) {
            headerSettings[key] = request.headers.getAll(key);
        }

        if (token && !(request.url.indexOf("viacep") != -1)) {
            headerSettings['Authorization'] = token;
        }
        //headerSettings['Content-Type'] = 'application/json';
        const newHeader = new HttpHeaders(headerSettings);

        changedRequest = request.clone({
            headers: newHeader
        });

        //console.log('Request', changedRequest);
        return next.handle(changedRequest).pipe(catchError(err => {
            this._authService.deuErro = true;
            let mensagem: string = 'Ocorreu um erro desconhecido ao tentar processar a operação!'
            switch (err.status){
                case 400: if (err instanceof HttpErrorResponse && err.error instanceof Blob && err.error.type === "application/json") {
                            mensagem = "Não existem dados para serem gerados. Erro: " + err.status;
                          } else {
                            mensagem = err.error.message;
                            this._loadingController.dismiss();
                          }        
                          break;
                
                case 401: if (err.error.message === "Unauthorized"){
                            if (this._authService.userLogged.value.user) {
                               mensagem = 'Sua sessão foi expirada. Por gentileza, logue novamente! Erro: ' + err.status;
                               this._router.navigate(['auth/login']);
                            }else{
                               mensagem = mensagem + ' Erro: ' + err.status;
                            }
                          }
                          break;
                
                case 500: if (err.error.message === "INVALID_CREDENTIALS") {
                               mensagem = 'Usuario ou senha incorretos. Digite novamente para realizar o login'
                             }else{
                               mensagem = mensagem + ' Erro: ' + err.status;   
                             }
                          break;
                default:  mensagem = mensagem + ' (' + err.status + ')' + ' - ' + err.error.message
                          break;            
                }
            this.exibir(mensagem)
            return [];
        }))
    }

    public exibir(msg) {
        this._authService.open(msg); //This is the method that it tries to call
    }
}
AuthService.ts
    import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';
import { Resolve, RouterStateSnapshot, ActivatedRouteSnapshot, Router } from '@angular/router';
import { ToastController, MenuController, AlertController, NavController } from '@ionic/angular';
import { environment } from 'src/environments/environment';
import { Storage } from '@ionic/storage';
import { promise } from 'protractor';
const API_STORAGE_KEY = 'spinopsstoragekey';

@Injectable({
    providedIn: 'root'
})
export class AuthService implements Resolve<any>{

    public userLogged: BehaviorSubject<any> = new BehaviorSubject({ imagem: null
                                                                  , user: null
                                                                  , nrCarteirinha: null 
                                                                  , listaBanners: null });
    public token: string;
    public users: BehaviorSubject<any[]> = new BehaviorSubject([]);
    public deuErro: boolean = false;
    public fl_comunicado: BehaviorSubject<boolean> = new BehaviorSubject(null);

    constructor(
        private _http: HttpClient,
        private toastController: ToastController,
        private _storage: Storage,
        private _navController: NavController,
        public _menuController: MenuController,
        public _router: Router,
        public alertController: AlertController,
    ) {
        if (JSON.parse(localStorage.getItem('currentToken'))) {        
            const currentUser = JSON.parse(localStorage.getItem('currentUser'));
            const currentToken = JSON.parse(localStorage.getItem('currentToken'));
            const currentNrCarteirinha = JSON.parse(localStorage.getItem('currentUserLogin'));
            const currentUserImage = JSON.parse(localStorage.getItem('currentUserImage'));
            const currentListaBanner = JSON.parse(localStorage.getItem('currentListaBanner'));

            this.token = currentUser && currentToken;

            this.userLogged.next({
                user: currentUser,
                imagem: currentUserImage,
                carteirinha: currentNrCarteirinha,
                listaBanners: currentListaBanner
            });
        }
    }

    private URL = `${environment.apiUrl}/login`;
    private User_URL = `${environment.apiUrl}/usuario`;

    resolve(
        route: ActivatedRouteSnapshot,
        state: RouterStateSnapshot
    ): Observable<any> | Promise<any> | any {

    }

    login(dados: any, acessoRapido: boolean = false): Observable<any> {
        let logon = dados;
        if (acessoRapido){
            logon = { ds_login: dados.dsLogin, ds_senha: dados.dsSenha, fl_salvar: dados.fl_salvar }
        }

        return this._http.post<any>(`${this.URL}`, logon)
            .pipe(map(async user => {
                    const toast = await this.toastController.create({
                        message: 'Login realizado com sucesso.',
                        duration: 2000
                    });

                    if (user && user.token) {
                        localStorage.setItem('currentToken', JSON.stringify(user.token));
                        localStorage.setItem('currentUser', JSON.stringify(user.nomeUsuario));
                        localStorage.setItem('currentUserLogin', JSON.stringify(user.dsLogin));
                        localStorage.setItem('currentUserIdSegurado', JSON.stringify(user.idSegurado));
                        localStorage.setItem('currentUserImage', JSON.stringify(user.imagem));
                        localStorage.setItem('currentListaBanner', JSON.stringify(user.listaBanners));

                        this.userLogged.next({
                            user: user.nomeUsuario,
                            imagem: user.imagem,
                            carteirinha: user.dsLogin,
                            listaBanners: user.listaBanners
                        });                
                        
                        await this.setUserLogged(user);                                        
                        await toast.present();
                    }

                    return user;
            }));
    }

    logout(): void {
        localStorage.removeItem('currentToken');
        localStorage.removeItem('currentUser');
        localStorage.removeItem('currentUserLogin');
        localStorage.removeItem('currentUserIdSegurado');
        localStorage.removeItem('currentUserImage');
        localStorage.removeItem('currentListaBanner');

        this._navController.navigateBack(['auth/login']).then(() => {
            this._menuController.enable(false);
            this.editUserLogged(null)
        });
    }

    cadastrarConta(dados: any): Observable<any> {
        this.deuErro = false
        return this._http.post(`${environment.apiUrl}/beneficiario/novo`, dados);
    }
    
    alterarSenha(dados: any): Observable<any> {
        this.deuErro = false
        return this._http.put(`${environment.apiUrl}/beneficiario/alterar-senha`, dados);
    }

    public getUserRemember(): Promise<any> {
        return this._storage.get(`${API_STORAGE_KEY}-lembrarUsuarios`);
    }

    public async setUserRemember(user): Promise<any> {
        return this._storage.set(`${API_STORAGE_KEY}-lembrarUsuarios`, user);
    }

    public getUsersLogged(): Promise<any> {
        return this._storage.get(`${API_STORAGE_KEY}-usuarioAutenticado`);
    }

    public setUserLogged(data: any) {
        if (data) {
            let users = [];
            users.push(data);
            return this._storage.set(`${API_STORAGE_KEY}-usuarioAutenticado`, users);
        }
    }

    public editUserLogged(data: any) {
        if (data.length > 0) {
            return this._storage.set(`${API_STORAGE_KEY}-usuarioAutenticado`, data);
        } else {
            return this._storage.set(`${API_STORAGE_KEY}-usuarioAutenticado`, []);
        }
    }


    async open(message, duration = 4000) {
        const toast = await this.toastController.create({ message: `${message}`, duration: duration });
        toast.present();
    }

    possuiComunicado() {
        this._http.get(`${environment.apiUrl}/comunicado/possui`).subscribe((resolve: any) => {
            this.fl_comunicado.next(resolve);
        });
    }

    getComunicados() {
        return this._http.get(`${environment.apiUrl}/comunicado/listar`);
    }

    setComunicado(nr_seq) {
        return this._http.post(`${environment.apiUrl}/comunicado/${nr_seq}/ler`, {});
    }
        carregaImagem() {
        const currentUserId = JSON.parse(localStorage.getItem('currentUserId'));
        return this._http.get(`${this.User_URL}/carrega-imagem/${currentUserId}`, {
            responseType: 'blob' as 'json'
        });
    }
    
    getUserAvatar() {
        return new Promise((resolve, reject) => {

            this.carregaImagem().subscribe((res: any) => {

                const file = new Blob([res], {
                    type: res.type
                });

                if (file.size !== 0) {
                    const reader = new FileReader();
                    reader.readAsDataURL(file);
                    reader.onload = e => resolve(reader.result);
                } else {
                    resolve('assets/avatars/profile.jpg');
                }

            }, reject);
        });
    }

}

The error
有人有什么想法吗?

最佳答案

这是 known issue由 HttpInterceptor 和 HttpClient 的循环依赖引起。
解释HttpClient依赖于所有 HttpInterceptors .AuthService依赖于 HttpClient .
在 HttpInterceptor 中注入(inject) AuthService 将创建一个无限循环。

HttpInterceptor -> AuthService -> HttpClient -> HttpInterceptor -> ∞
根据您的 Angular 版本,您将得到一个循环依赖错误,或者根本没有错误导致相关的注入(inject)服务为 undefined ,并且您的代码将默默地失败。
即使它应该是 fixed 自 Angular 5.2.3 以来,我最近在 Ionic 项目中也遇到了完全相同的问题。该修复可能允许 HttpClient 直接注入(inject) HttpInterceptor,但在通过服务注入(inject)时可能不起作用。
解决方案
注入(inject)Injector在拦截器构造函数而不是 AuthService 中,然后使用注入(inject)器手动检索服务。我不知道为什么,但你必须在这里使用某种延迟,否则 AuthService 可能仍然未定义。 platform.ready()setTimeout会做。
我还使用了 ready$ ReplaySubject确保在使用之前加载 AuthService。
...
import {Injectable, Injector} from '@angular/core';
import {AuthService} from '../services/auth.service';
import {Platform} from '@ionic/angular';
...

@Injectable()
export class Interceptor implements HttpInterceptor {

  private ready$ = new ReplaySubject<void>(1);
  private authService: AuthService;

  constructor(private platform: Platform, private injector: Injector ) {
    this.platform.ready().then(() => {
      this.authService = this.injector.get(AuthService);
      this.ready$.next();
    });
  }

  get ready$(): Observable<void> {
    return this.ready$.asObservable();
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return this.ready$.pipe(
      first(),
      switchMapTo(next.handle(req)),
      catchError((error: HttpErrorResponse) => {
        ...
        return throwError(error);
      })
    );
  }
  ...
}
这应该有效,如果您需要进一步的帮助,请告诉我。

关于angular - 我的拦截器上未定义构造函数变量(Angular 8),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65845155/

相关文章:

angular - 从 Angular 组件关闭 Electron 应用程序

TypeScript - 删除具有特定类型的所有属性

javascript - 编辑 Mobify 网站菜单

angular - 如何从 Modal 调用父方法

javascript - 如何拥有 Immutable.js 可选 (TypeScript) Record 类属性

css - 如何在twitter 的bootstrap 2.1.0 中使用新的词缀插件?

monitoring - 网站监控库

angular - 仅编译和发布 Angular 应用程序的一部分

html - 在最小化浏览器期间,网格标题将隐藏在 p-dialog 中

css - 从我们的组件 Angular 覆盖外部库的样式