Angular2:解决Http拦截工厂和auth服务之间的循环依赖关系

时间:2017-05-26 11:13:00

标签: angular typescript cyclic-dependency

我有以下身份验证服务:

@Injectable()
export class AuthService {

  //...

  constructor(private http: Http, private router: Router) {
    //...
  }

  public login(username: string, password: string): Observable<boolean> {
    // perform login
  }

  public logout() {
    // perform cleanup
    this.router.navigateByUrl('/login');
  }
}

以下Http拦截工厂:

@Injectable()
class MyHttpInterceptor extends Http {

  constructor(backend: ConnectionBackend, defaultOptions: RequestOptions, private authService: AuthService) {
    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, this.getRequestOptionArgs(options)));
  }

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

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

  getRequestOptionArgs(options?: RequestOptionsArgs): RequestOptionsArgs {
    if (options == null) {
      options = new RequestOptions();
    }
    if (options.headers == null) {
      options.headers = new Headers();
    }
    // add headers required by our backend
    return options;
  }

  intercept(observable: Observable<Response>): Observable<Response> {
    return observable.catch((err, source) => {
      if (err.status == 401) {
        this.authService.logout();
        return Observable.empty();
      } else {
        return Observable.throw(err);
      }
    });

  }
}

export function myHttpInterceptorFactory(backend: ConnectionBackend, options: RequestOptions, authService: AuthService): MyHttpInterceptor {
  return new MyHttpInterceptor(backend, options, authService);
}

基本上,这里的要求是如果从状态401的后端收到任何响应,则应该开始注销过程。

App模块中的设置如下:

@NgModule({
  imports: [
    HttpModule,
    //...
  ],
  declarations: [
    AppComponent,
    //...
  ],
  providers: [
    {
      provide: Http,
      useFactory: myHttpInterceptorFactory,
      deps: [XHRBackend, RequestOptions, AuthService]
    },
    AuthService,
    //...
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

这会产生循环依赖性错误,其中Http拦截器需要AuthService,但AuthService需要Http

Error: Provider parse errors:
Cannot instantiate cyclic dependency! Http: in NgModule AppModule in ./AppModule

我尝试使用forwardRefHttp中注入AuthService,但这并没有改变任何内容。

任何关于如何重组的帮助都会很棒。

1 个答案:

答案 0 :(得分:1)

  

基本上,这里的要求是如果从状态401的后端收到任何响应,则应该开始注销过程。

如果目标是以特定方式处理HTTP错误,这就是我要做的:我不会扩展HTTP服务,而是为我的服务创建一个基类来扩展处理重复的HTTP函数,例如提取数据或处理错误。它看起来像这样:

import { Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';

export class HttpServiceBase {
    constructor(private authSvc: AuthService ) { }

    extractData(res: Response) {
        const body = res.json();
        return body || {};
    }

    handleError(error: Response | any) {
        switch (error.status) {
            .... //other cases

            case 401:
               this.authSvc.logout();

            default:
                //default handling
        }

    }
}

然后像这样使用它:

@Injectable()
export class SomeService extends HttpServiceBase {

    constructor(
        authSvc: AuthService,
        private http: AuthHttp
    )
    {
        super(authSvc);
    }


    sampleCall() {
        return this.http.get(...)
            .map(this.extractData)
            .catch(this.handleError);
    }

}

这解决了周期性的依赖性。

希望有所帮助。

相关问题