I have created an Angular Module called DataModule which contains services for accessing my backend through a token-secured HTTP API. On HTTP access control errors such as 401 Unauthorized and 403 Forbidden I want to be able to redirect to /login or try and refresh the token etc.
I want to re-use this DataModule in an Ionic application, so i don't want any coupling with the standard @angular/router
RouterModule or anything else.
Currently, I am passing in some primitive values via the forRoot method when importing the DataModule in my AppModule:
DataModule.forRoot({
url: environment.apiEndpoint,
token() {
return localStorage.getItem('auth_token')
},
onError(errors: any) {
console.log(this);
}
});
I need the onError callback to have the ability to call router.navigate('/login') or similar, but as this is a simple config object I don't think router can be injected at this stage.
I am looking for a way to pass something like this in and I have reviewed several other popular ng projects such as ngx-translate, angularitics2 and ngx-restangular which all pass in providers that seem to be then provided back by the components. Is this the way to achieve what I am after by passing in an AuthException service or similar that implements an interface kept in the DataModule package?
Update:
I've ended up taking the idea from the projects I mentioned which is for my DataModule to have a default service for error handling which basically does nothing. I then inject a better error handling class that is specific to the app in the forRoot which then gets used in the providers:
static forRoot(config: any): ModuleWithProviders {
return {
ngModule: DataModule,
providers: [
config.authExceptionService || AuthExceptionService,
{ provide: CONFIG, useValue: config },
{
provide: ApiService,
useFactory: ApiServiceFactory,
deps: [Http, config.authExceptionService || AuthExceptionService, CONFIG]
},
]
}
}
My ApiServiceFactory for reference:
export function ApiServiceFactory(http: Http, authException: AuthExceptionService, config: any) {
return new ApiService(http, authException, {
url: config.url,
token: config.token
});
}
I also had to make use of an InjectionToken in order to provide my config object as a dependency to the factory:
export const CONFIG = new InjectionToken<any>('CONFIG');
From my main app I do the following:
DataModule.forRoot({
authExceptionService: AppAuthExceptionService,
url: environment.apiEndpoint,
token() {
return localStorage.getItem('auth_token')
}
});
It would be great to hear from anyone if there are any potential issues with this approach. I am hoping I should be able to use this module in the Ionic app with something like the following:
DataModule.forRoot({
authExceptionService: MobileAppAuthExceptionService,
url: environment.apiEndpoint,
token() {
return storage.getItem('auth_token') // ionic uses different storage
}
})
You could extend
Httpand apply a filterOr alternatively use map and throw an error
and have a catch function that does the redirect (which imo is the better way to do it).