Reputation: 1143
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
}
})
Upvotes: 1
Views: 811
Reputation: 1431
One possible solution for this is to extend your DataModule in each project and override the onError and constructor methods respectively.
export class MyDataModule extends DataModule {
static ForRoot(...){
...
}
constructor(...args, private router: Router){
super(...args)
}
onError() {
this.router.navigate(errorPage);
}
}
Then in your ionic application you can handle the error in some other way.
Upvotes: 0
Reputation: 17327
You could extend Http
and apply a filter
export class MyHttp extends Http {
//...
constructor(){super()}
get(){
super.get()
.filter(x => {
// do your logic here
if(x.status === 403){
router.navigate( errorPage )
return false;
}
})
}
}
Or alternatively use map and throw an error
if (response.status === 403) {
throw Observable.throw(response);
}
and have a catch function that does the redirect (which imo is the better way to do it).
Upvotes: 0