APP_INITIALIZER引发“无法实例化循环依赖性! ApplicationRef_";与重定向的自定义Http提供程序一起使用时 [英] APP_INITIALIZER raises "Cannot instantiate cyclic dependency! ApplicationRef_" when used with a custom Http provider that is redirecting

查看:86
本文介绍了APP_INITIALIZER引发“无法实例化循环依赖性! ApplicationRef_";与重定向的自定义Http提供程序一起使用时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用自定义Http提供程序来处理API身份验证错误.在我的CustomHttp中,当API发出401状态错误时,我需要将用户重定向到登录页面.很好!

I am using a custom Http provider to handle API authentication error. In my CustomHttp, I need to redirect the user to the login page when a 401 status error is emitted by the API. That works fine!

app.module.ts

app.module.ts

export function loadCustomHttp(backend: XHRBackend, defaultOptions: AppRequestOptions,
  router: Router, dataHelper: DataHelperService) {
  return new CustomHttp(backend, defaultOptions, router, dataHelper);
}

@NgModule({
// some declarations, imports, ...
providers: [
// some services ...
 {
      provide: Http,
      useFactory: loadCustomHttp,
      deps: [XHRBackend, RequestOptions, Router, DataHelperService] 
    }
});

custom-http.ts

custom-http.ts

import { Injectable } from '@angular/core';
import { Http, RequestOptions, RequestOptionsArgs, ConnectionBackend, Request, Response } from '@angular/http';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';

import { DataHelperService } from '../helpers/data-helper.service';
import { AuthStorage } from '../services/auth/auth-storage';

import 'rxjs/add/observable/throw';
import 'rxjs/add/observable/empty';

@Injectable()
export class CustomHttp extends Http {
  constructor(backend: ConnectionBackend, defaultOptions: RequestOptions,
    private router: Router, private dataHelper: DataHelperService) {
    super(backend, defaultOptions);
  }


  request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
    return this.intercept(super.request(url, options));
  }

  get(url: string, options?: RequestOptionsArgs): Observable<Response> {
    return this.intercept(super.get(url, options));
  }

  post(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
    return this.intercept(super.post(url, body, options));
  }

  put(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
    return this.intercept(super.put(url, body, options));
  }

  delete(url: string, options?: RequestOptionsArgs): Observable<Response> {
    return this.intercept(super.delete(url, options));
  }



  intercept(observable: Observable<Response>): Observable<Response> {
    return observable.catch((err, source) => {
      let token = AuthStorage.getToken();

      if (err.status === 401 && token && AuthStorage.isTokenExpired())    { 
        // token has expired -> redirecting user to login
        AuthStorage.clearAll();
        this.router.navigate(['auth/login']);
      }
      return Observable.throw(err);
    });
  }
}

然后,我尝试使用APP_INITIALIZER不透明令牌来获取初始化我的应用程序所需的设置.

Then, I tried to use the APP_INITIALIZER opaque token to get the required settings to initialize my app.

app.module.ts

app.module.ts

@NgModule({
// some declarations, imports, ...
providers: [
// some services ...
    ConfigService,
    { 
      provide: APP_INITIALIZER, 
      useFactory: (config: ConfigService) => () => config.load(), 
      deps:[ConfigService, Http],
      multi: true
    }
});

config.service.ts

config.service.ts

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { AppSettings } from '../../environments/app-settings';
import { Observable } from 'rxjs/Observable';

import 'rxjs/add/operator/toPromise';

@Injectable()
export class ConfigService {

  public settings:AppSettings;

  constructor(private http:Http) { }

  load() : Promise<AppSettings> {
    let url = '/settings/';

    var observable= this.http.get(url)
            .map(res => res.json());

    observable.subscribe(config => this.settings = config);
    return observable.toPromise();
  }

}

这会产生一个错误:

Uncaught Error: Provider parse errors:
Cannot instantiate cyclic dependency! ApplicationRef_: in NgModule AppModuleNgModuleProviderAnalyzer.parse @ provider_analyzer.js:291NgModuleCompiler.compile @ ng_module_compiler.js:54RuntimeCompiler._compileModule @ runtime_compiler.js:102RuntimeCompiler._compileModuleAndComponents @ runtime_compiler.js:65RuntimeCompiler.compileModuleAsync @ runtime_compiler.js:55PlatformRef_._bootstrapModuleWithZone @ application_ref.js:303PlatformRef_.bootstrapModule @ application_ref.js:285(anonymous function) @ main.ts:18__webpack_require__ @ bootstrap 0e2b412…:52(anonymous function) @ main.bundle.js:86665__webpack_require__ @ bootstrap 0e2b412…:52webpackJsonpCallback @ bootstrap 0e2b412…:23(anonymous function) @ main.bundle.js:1

如果我注释掉自定义Http提供程序,则不会显示错误,并且APP_INITIALIZER会按预期工作. 如果我从Http provider deps声明中删除了Router,我将不再有错误,但是我的ConfigService.load()函数被调用了两次.

If I comment out the custom Http provider, the error is not shown and the APP_INITIALIZER works as expected. If I remove the Router from the Http provider deps declaration, I don't have the error anymore but the my ConfigService.load() function is called twice.

有人知道为什么此路由器依赖性导致此循环依赖性错误吗? 如何防止ConfigService.load()函数被调用两次?

Does anyone knows why this router dependency is causing this cyclic dependency error ? How can I prevent my ConfigService.load() function to be called twice ?

如果需要,我创建了一个公共存储库,以重现错误: https://github.com/haia212/AngularErrorTestProject

If needed, I have created a public repository reproducing the error : https://github.com/haia212/AngularErrorTestProject

推荐答案

问题是Router可以异步加载某些路由.这就是为什么它需要Http的原因.您的Http取决于Router,而Router取决于Http. Angular注入器无法创建任何这些服务.

The problem is that Router can async load some routes. This is why it needs Http. Your Http depends on Router and Router depends on Http. Angular injector is not able to create any of these services.

我遇到了类似的问题,解决方案之一可能是注入Injector而不是提供服务,然后再获得服务.

I had similar problems and one of the solutions can be injecting Injector instead of service and getting service afterwards.

代码:

@Injectable()
export class CustomHttp extends Http {
  constructor(backend: ConnectionBackend, defaultOptions: RequestOptions,
    private injector: Injector, private dataHelper: DataHelperService) {
    super(backend, defaultOptions);
  }

  public get router(): Router { //this creates router property on your service.
     return this.injector.get(Router);
  }
  ...

因此,基本上,您不需要Router即可获得Http服务的实例.当您访问router属性时才完成注入-仅当您要重定向用户时. router属性对代码的其他部分透明.

So, basically, you do not need Router to get instance of Http service. The injection is done when you access router property - only when you want to redirect user. router property is transparent to other parts of code.

如果不能解决问题,则可以对其余注入的服务(除调用super的这些服务之外)做同样的事情.

If it will not solve problem, you can do the same thing to the rest of injected services (except these to call super).

这篇关于APP_INITIALIZER引发“无法实例化循环依赖性! ApplicationRef_";与重定向的自定义Http提供程序一起使用时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆