I have the following auth service:
@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');
}
}
And the following Http
interceptor factory:
@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);
}
Basically the requirement here is that if any response is ever received from the backend with status 401, the logout procedure should start.
The setup in App module is as follows:
@NgModule({
imports: [
HttpModule,
//...
],
declarations: [
AppComponent,
//...
],
providers: [
{
provide: Http,
useFactory: myHttpInterceptorFactory,
deps: [XHRBackend, RequestOptions, AuthService]
},
AuthService,
//...
],
bootstrap: [AppComponent]
})
export class AppModule {
}
This creates a cyclic dependency error where the Http
interceptor needs AuthService
, but AuthService
needs Http
.
Error: Provider parse errors:
Cannot instantiate cyclic dependency! Http: in NgModule AppModule in ./AppModule
I tried using forwardRef
to inject Http
in AuthService
, but that didn't change anything.
Any help on how to restructure would be great.
If the goal is to handle HTTP errors a specific way, this is what I would do: I would not extend the HTTP service, but rather create a base class for my services to extend that handle the repetitive HTTP functions such as extracting data or handling errors. It looks something like this:
Then use it like so:
This solves the cyclical dependency.
Hope that helps.