|
|
|
@ -12,13 +12,14 @@ import { |
|
|
|
} from '@angular/core'; |
|
|
|
import { NavigationError, ResolveEnd } from '@angular/router'; |
|
|
|
import { Actions, ofActionSuccessful } from '@ngxs/store'; |
|
|
|
import { Observable, Subject } from 'rxjs'; |
|
|
|
import { filter, map } from 'rxjs/operators'; |
|
|
|
import { Observable, of, Subject, throwError } from 'rxjs'; |
|
|
|
import { catchError, filter, map, switchMap } from 'rxjs/operators'; |
|
|
|
import snq from 'snq'; |
|
|
|
import { HttpErrorWrapperComponent } from '../components/http-error-wrapper/http-error-wrapper.component'; |
|
|
|
import { ErrorScreenErrorCodes, HttpErrorConfig } from '../models/common'; |
|
|
|
import { Confirmation } from '../models/confirmation'; |
|
|
|
import { ConfirmationService } from '../services/confirmation.service'; |
|
|
|
import { HTTP_ERROR_HANDLER } from '../tokens/http-error.token'; |
|
|
|
|
|
|
|
export const DEFAULT_ERROR_MESSAGES = { |
|
|
|
defaultError: { |
|
|
|
@ -70,28 +71,32 @@ export const DEFAULT_ERROR_LOCALIZATIONS = { |
|
|
|
export class ErrorHandler { |
|
|
|
componentRef: ComponentRef<HttpErrorWrapperComponent>; |
|
|
|
|
|
|
|
protected httpErrorHandler = this.injector.get(HTTP_ERROR_HANDLER, (_, err: HttpErrorResponse) => |
|
|
|
throwError(err), |
|
|
|
); |
|
|
|
|
|
|
|
constructor( |
|
|
|
private actions: Actions, |
|
|
|
private routerEvents: RouterEvents, |
|
|
|
private confirmationService: ConfirmationService, |
|
|
|
private cfRes: ComponentFactoryResolver, |
|
|
|
private rendererFactory: RendererFactory2, |
|
|
|
private injector: Injector, |
|
|
|
@Inject('HTTP_ERROR_CONFIG') private httpErrorConfig: HttpErrorConfig, |
|
|
|
protected actions: Actions, |
|
|
|
protected routerEvents: RouterEvents, |
|
|
|
protected confirmationService: ConfirmationService, |
|
|
|
protected cfRes: ComponentFactoryResolver, |
|
|
|
protected rendererFactory: RendererFactory2, |
|
|
|
protected injector: Injector, |
|
|
|
@Inject('HTTP_ERROR_CONFIG') protected httpErrorConfig: HttpErrorConfig, |
|
|
|
) { |
|
|
|
this.listenToRestError(); |
|
|
|
this.listenToRouterError(); |
|
|
|
this.listenToRouterDataResolved(); |
|
|
|
} |
|
|
|
|
|
|
|
private listenToRouterError() { |
|
|
|
protected listenToRouterError() { |
|
|
|
this.routerEvents |
|
|
|
.getNavigationEvents('Error') |
|
|
|
.pipe(filter(this.filterRouteErrors)) |
|
|
|
.subscribe(() => this.show404Page()); |
|
|
|
} |
|
|
|
|
|
|
|
private listenToRouterDataResolved() { |
|
|
|
protected listenToRouterDataResolved() { |
|
|
|
this.routerEvents |
|
|
|
.getEvents(ResolveEnd) |
|
|
|
.pipe(filter(() => !!this.componentRef)) |
|
|
|
@ -101,113 +106,127 @@ export class ErrorHandler { |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
private listenToRestError() { |
|
|
|
protected listenToRestError() { |
|
|
|
this.actions |
|
|
|
.pipe( |
|
|
|
ofActionSuccessful(RestOccurError), |
|
|
|
map(action => action.payload), |
|
|
|
filter(this.filterRestErrors), |
|
|
|
switchMap(this.executeErrorHandler), |
|
|
|
) |
|
|
|
.subscribe(err => { |
|
|
|
const body = snq(() => err.error.error, { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, |
|
|
|
}); |
|
|
|
.subscribe(); |
|
|
|
} |
|
|
|
|
|
|
|
if (err instanceof HttpErrorResponse && err.headers.get('_AbpErrorFormat')) { |
|
|
|
const confirmation$ = this.showError(null, null, body); |
|
|
|
private executeErrorHandler = err => { |
|
|
|
const returnValue = this.httpErrorHandler(this.injector, err); |
|
|
|
|
|
|
|
if (err.status === 401) { |
|
|
|
confirmation$.subscribe(() => { |
|
|
|
this.navigateToLogin(); |
|
|
|
}); |
|
|
|
} |
|
|
|
} else { |
|
|
|
switch (err.status) { |
|
|
|
case 401: |
|
|
|
this.canCreateCustomError(401) |
|
|
|
? this.show401Page() |
|
|
|
: this.showError( |
|
|
|
{ |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError401.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.title, |
|
|
|
}, |
|
|
|
{ |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError401.details, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.details, |
|
|
|
}, |
|
|
|
).subscribe(() => this.navigateToLogin()); |
|
|
|
break; |
|
|
|
case 403: |
|
|
|
this.createErrorComponent({ |
|
|
|
title: { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError403.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError403.title, |
|
|
|
}, |
|
|
|
details: { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError403.details, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError403.details, |
|
|
|
}, |
|
|
|
status: 403, |
|
|
|
}); |
|
|
|
break; |
|
|
|
case 404: |
|
|
|
this.canCreateCustomError(404) |
|
|
|
? this.show404Page() |
|
|
|
: this.showError( |
|
|
|
{ |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError404.details, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.details, |
|
|
|
}, |
|
|
|
{ |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError404.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.title, |
|
|
|
}, |
|
|
|
); |
|
|
|
break; |
|
|
|
case 500: |
|
|
|
this.createErrorComponent({ |
|
|
|
title: { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError500.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError500.title, |
|
|
|
return (returnValue instanceof Observable ? returnValue : of(null)).pipe( |
|
|
|
catchError(err => { |
|
|
|
this.handleError(err); |
|
|
|
return of(null); |
|
|
|
}), |
|
|
|
); |
|
|
|
}; |
|
|
|
|
|
|
|
private handleError(err: any) { |
|
|
|
const body = snq(() => err.error.error, { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, |
|
|
|
}); |
|
|
|
|
|
|
|
if (err instanceof HttpErrorResponse && err.headers.get('_AbpErrorFormat')) { |
|
|
|
const confirmation$ = this.showError(null, null, body); |
|
|
|
|
|
|
|
if (err.status === 401) { |
|
|
|
confirmation$.subscribe(() => { |
|
|
|
this.navigateToLogin(); |
|
|
|
}); |
|
|
|
} |
|
|
|
} else { |
|
|
|
switch (err.status) { |
|
|
|
case 401: |
|
|
|
this.canCreateCustomError(401) |
|
|
|
? this.show401Page() |
|
|
|
: this.showError( |
|
|
|
{ |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError401.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.title, |
|
|
|
}, |
|
|
|
details: { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError500.details, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError500.details, |
|
|
|
{ |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError401.details, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.details, |
|
|
|
}, |
|
|
|
status: 500, |
|
|
|
}); |
|
|
|
break; |
|
|
|
case 0: |
|
|
|
if (err.statusText === 'Unknown Error') { |
|
|
|
this.createErrorComponent({ |
|
|
|
title: { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, |
|
|
|
}, |
|
|
|
details: err.message, |
|
|
|
isHomeShow: false, |
|
|
|
}); |
|
|
|
} |
|
|
|
break; |
|
|
|
default: |
|
|
|
this.showError( |
|
|
|
).subscribe(() => this.navigateToLogin()); |
|
|
|
break; |
|
|
|
case 403: |
|
|
|
this.createErrorComponent({ |
|
|
|
title: { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError403.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError403.title, |
|
|
|
}, |
|
|
|
details: { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError403.details, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError403.details, |
|
|
|
}, |
|
|
|
status: 403, |
|
|
|
}); |
|
|
|
break; |
|
|
|
case 404: |
|
|
|
this.canCreateCustomError(404) |
|
|
|
? this.show404Page() |
|
|
|
: this.showError( |
|
|
|
{ |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.details, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.details, |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError404.details, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.details, |
|
|
|
}, |
|
|
|
{ |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError404.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.title, |
|
|
|
}, |
|
|
|
); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case 500: |
|
|
|
this.createErrorComponent({ |
|
|
|
title: { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError500.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError500.title, |
|
|
|
}, |
|
|
|
details: { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError500.details, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError500.details, |
|
|
|
}, |
|
|
|
status: 500, |
|
|
|
}); |
|
|
|
break; |
|
|
|
case 0: |
|
|
|
if (err.statusText === 'Unknown Error') { |
|
|
|
this.createErrorComponent({ |
|
|
|
title: { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, |
|
|
|
}, |
|
|
|
details: err.message, |
|
|
|
isHomeShow: false, |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
break; |
|
|
|
default: |
|
|
|
this.showError( |
|
|
|
{ |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.details, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.details, |
|
|
|
}, |
|
|
|
{ |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, |
|
|
|
}, |
|
|
|
); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private show401Page() { |
|
|
|
protected show401Page() { |
|
|
|
this.createErrorComponent({ |
|
|
|
title: { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError401.title, |
|
|
|
@ -217,7 +236,7 @@ export class ErrorHandler { |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
private show404Page() { |
|
|
|
protected show404Page() { |
|
|
|
this.createErrorComponent({ |
|
|
|
title: { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError404.title, |
|
|
|
@ -227,7 +246,7 @@ export class ErrorHandler { |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
private showError( |
|
|
|
protected showError( |
|
|
|
message?: LocalizationParam, |
|
|
|
title?: LocalizationParam, |
|
|
|
body?: any, |
|
|
|
@ -256,7 +275,7 @@ export class ErrorHandler { |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
private navigateToLogin() { |
|
|
|
protected navigateToLogin() { |
|
|
|
this.injector.get(AuthService).initLogin(); |
|
|
|
} |
|
|
|
|
|
|
|
@ -304,13 +323,13 @@ export class ErrorHandler { |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
private filterRestErrors = ({ status }: HttpErrorResponse): boolean => { |
|
|
|
protected filterRestErrors = ({ status }: HttpErrorResponse): boolean => { |
|
|
|
if (typeof status !== 'number') return false; |
|
|
|
|
|
|
|
return this.httpErrorConfig.skipHandledErrorCodes.findIndex(code => code === status) < 0; |
|
|
|
}; |
|
|
|
|
|
|
|
private filterRouteErrors = (navigationError: NavigationError): boolean => { |
|
|
|
protected filterRouteErrors = (navigationError: NavigationError): boolean => { |
|
|
|
return ( |
|
|
|
snq(() => navigationError.error.message.indexOf('Cannot match') > -1) && |
|
|
|
this.httpErrorConfig.skipHandledErrorCodes.findIndex(code => code === 404) < 0 |
|
|
|
|