Implementing a custom Http provider in Angular to manage API authentication errors has been successful. The CustomHttp is able to redirect users to the login page upon receiving a 401 status error from the API.
app.module.ts
export function loadCustomHttp(backend: XHRBackend, defaultOptions: AppRequestOptions,
router: Router, dataHelper: DataHelperService) {
return new CustomHttp(backend, defaultOptions, router, dataHelper);
}
@NgModule({
// declarations and imports...
providers: [
// some services ...
{
provide: Http,
useFactory: loadCustomHttp,
deps: [XHRBackend, RequestOptions, Router, DataHelperService]
}
});
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 methods...
intercept(observable: Observable<Response>): Observable<Response> {
return observable.catch((err, source) => {
let token = AuthStorage.getToken();
if (err.status === 401 && token && AuthStorage.isTokenExpired()) {
AuthStorage.clearAll();
this.router.navigate(['auth/login']);
}
return Observable.throw(err);
});
}
}
Attempting to utilize the APP_INITIALIZER opaque token to initialize the app settings leads to an error.
app.module.ts
@NgModule({
// declarations and imports...
providers: [
// some services ...
ConfigService,
{
provide: APP_INITIALIZER,
useFactory: (config: ConfigService) => () => config.load(),
deps:[ConfigService, Http],
multi: true
}
});
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();
}
}
An error is generated:
Uncaught Error: Provider parse errors:
Cannot instantiate cyclic dependency! ApplicationRef_: in NgModule AppModuleNgModuleProviderAnalyzer.parse @ provider_analyzer.js:291...
The presence of the router dependency in the Http provider declaration seems to be causing the cyclic dependency error. Removing it resolves the issue but triggers the ConfigService.load() function to run twice.
Seeking insights on why the router dependency may lead to this error and how to prevent double execution of the ConfigService.load() function.
For reference, a repository demonstrating the error can be accessed here: https://github.com/haia212/AngularErrorTestProject