Angular2: устранение циклической зависимости между фабрикой перехвата Http и сервисом аутентификации

У меня есть следующий сервис аутентификации:

@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, процедура выхода из системы должна начаться.

Настройка в модуле приложения выглядит следующим образом:

@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

Я пытался с помощью forwardRef вводить Http в AuthService, но это ничего не изменило.

Любая помощь о том, как реструктурировать, будет отличной.

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

}

Это решает циклическую зависимость.

Надеюсь, это поможет.

Другие вопросы по тегам