|
|
@ -1,21 +1,25 @@ |
|
|
import { RestOccurError } from '@abp/ng.core'; |
|
|
import { Config, RestOccurError } from '@abp/ng.core'; |
|
|
import { HttpErrorResponse } from '@angular/common/http'; |
|
|
import { HttpErrorResponse } from '@angular/common/http'; |
|
|
import { |
|
|
import { |
|
|
ApplicationRef, |
|
|
ApplicationRef, |
|
|
ComponentFactoryResolver, |
|
|
ComponentFactoryResolver, |
|
|
EmbeddedViewRef, |
|
|
EmbeddedViewRef, |
|
|
|
|
|
Inject, |
|
|
Injectable, |
|
|
Injectable, |
|
|
Injector, |
|
|
Injector, |
|
|
NgZone, |
|
|
|
|
|
RendererFactory2, |
|
|
RendererFactory2, |
|
|
|
|
|
Type, |
|
|
|
|
|
ComponentRef, |
|
|
} from '@angular/core'; |
|
|
} from '@angular/core'; |
|
|
import { Router } from '@angular/router'; |
|
|
import { Navigate, RouterError, RouterState, RouterDataResolved } from '@ngxs/router-plugin'; |
|
|
import { Actions, ofActionSuccessful, Store } from '@ngxs/store'; |
|
|
import { Actions, ofActionSuccessful, Store } from '@ngxs/store'; |
|
|
import { Observable } from 'rxjs'; |
|
|
import { Observable, Subject } from 'rxjs'; |
|
|
import snq from 'snq'; |
|
|
import snq from 'snq'; |
|
|
import { ErrorComponent } from '../components/error/error.component'; |
|
|
import { ErrorComponent } from '../components/error/error.component'; |
|
|
|
|
|
import { HttpErrorConfig, ErrorScreenErrorCodes } from '../models/common'; |
|
|
import { Toaster } from '../models/toaster'; |
|
|
import { Toaster } from '../models/toaster'; |
|
|
import { ConfirmationService } from '../services/confirmation.service'; |
|
|
import { ConfirmationService } from '../services/confirmation.service'; |
|
|
|
|
|
import { HTTP_ERROR_CONFIG } from '../tokens/error-pages.token'; |
|
|
|
|
|
|
|
|
export const DEFAULT_ERROR_MESSAGES = { |
|
|
export const DEFAULT_ERROR_MESSAGES = { |
|
|
defaultError: { |
|
|
defaultError: { |
|
|
@ -36,82 +40,142 @@ export const DEFAULT_ERROR_MESSAGES = { |
|
|
}, |
|
|
}, |
|
|
defaultError500: { |
|
|
defaultError500: { |
|
|
title: '500', |
|
|
title: '500', |
|
|
details: { key: 'AbpAccount::InternalServerErrorMessage', defaultValue: 'Error detail not sent by server.' }, |
|
|
details: 'Error detail not sent by server.', |
|
|
}, |
|
|
|
|
|
defaultErrorUnknown: { |
|
|
|
|
|
title: 'Unknown Error', |
|
|
|
|
|
details: { key: 'AbpAccount::InternalServerErrorMessage', defaultValue: 'Error detail not sent by server.' }, |
|
|
|
|
|
}, |
|
|
}, |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
@Injectable({ providedIn: 'root' }) |
|
|
@Injectable({ providedIn: 'root' }) |
|
|
export class ErrorHandler { |
|
|
export class ErrorHandler { |
|
|
|
|
|
componentRef: ComponentRef<ErrorComponent>; |
|
|
|
|
|
|
|
|
constructor( |
|
|
constructor( |
|
|
private actions: Actions, |
|
|
private actions: Actions, |
|
|
private router: Router, |
|
|
|
|
|
private ngZone: NgZone, |
|
|
|
|
|
private store: Store, |
|
|
private store: Store, |
|
|
private confirmationService: ConfirmationService, |
|
|
private confirmationService: ConfirmationService, |
|
|
private appRef: ApplicationRef, |
|
|
private appRef: ApplicationRef, |
|
|
private cfRes: ComponentFactoryResolver, |
|
|
private cfRes: ComponentFactoryResolver, |
|
|
private rendererFactory: RendererFactory2, |
|
|
private rendererFactory: RendererFactory2, |
|
|
private injector: Injector, |
|
|
private injector: Injector, |
|
|
|
|
|
@Inject(HTTP_ERROR_CONFIG) private httpErrorConfig: HttpErrorConfig, |
|
|
) { |
|
|
) { |
|
|
actions.pipe(ofActionSuccessful(RestOccurError)).subscribe(res => { |
|
|
this.actions.pipe(ofActionSuccessful(RestOccurError, RouterError, RouterDataResolved)).subscribe(res => { |
|
|
const { payload: err = {} as HttpErrorResponse | any } = res; |
|
|
if (res instanceof RestOccurError) { |
|
|
const body = snq(() => (err as HttpErrorResponse).error.error, DEFAULT_ERROR_MESSAGES.defaultError.title); |
|
|
const { payload: err = {} as HttpErrorResponse | any } = res; |
|
|
|
|
|
const body = snq(() => (err as HttpErrorResponse).error.error, DEFAULT_ERROR_MESSAGES.defaultError.title); |
|
|
|
|
|
|
|
|
if (err instanceof HttpErrorResponse && err.headers.get('_AbpErrorFormat')) { |
|
|
if (err instanceof HttpErrorResponse && err.headers.get('_AbpErrorFormat')) { |
|
|
const confirmation$ = this.showError(null, null, body); |
|
|
const confirmation$ = this.showError(null, null, body); |
|
|
|
|
|
|
|
|
if (err.status === 401) { |
|
|
if (err.status === 401) { |
|
|
confirmation$.subscribe(() => { |
|
|
confirmation$.subscribe(() => { |
|
|
this.navigateToLogin(); |
|
|
this.navigateToLogin(); |
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
switch ((err as HttpErrorResponse).status) { |
|
|
|
|
|
case 401: |
|
|
|
|
|
this.showError( |
|
|
|
|
|
DEFAULT_ERROR_MESSAGES.defaultError401.details, |
|
|
|
|
|
DEFAULT_ERROR_MESSAGES.defaultError401.title, |
|
|
|
|
|
).subscribe(() => this.navigateToLogin()); |
|
|
|
|
|
break; |
|
|
|
|
|
case 403: |
|
|
|
|
|
this.createErrorComponent({ |
|
|
|
|
|
title: DEFAULT_ERROR_MESSAGES.defaultError403.title, |
|
|
|
|
|
details: DEFAULT_ERROR_MESSAGES.defaultError403.details, |
|
|
|
|
|
}); |
|
|
}); |
|
|
break; |
|
|
} |
|
|
case 404: |
|
|
} else { |
|
|
this.showError( |
|
|
switch ((err as HttpErrorResponse).status) { |
|
|
DEFAULT_ERROR_MESSAGES.defaultError404.details, |
|
|
case 401: |
|
|
DEFAULT_ERROR_MESSAGES.defaultError404.title, |
|
|
this.canCreateCustomError(401) |
|
|
); |
|
|
? this.show401Page() |
|
|
break; |
|
|
: this.showError( |
|
|
case 500: |
|
|
{ |
|
|
this.createErrorComponent({ |
|
|
key: 'AbpAccount::DefaultErrorMessage401', |
|
|
title: DEFAULT_ERROR_MESSAGES.defaultError500.title, |
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.title, |
|
|
details: DEFAULT_ERROR_MESSAGES.defaultError500.details, |
|
|
}, |
|
|
}); |
|
|
{ |
|
|
break; |
|
|
key: 'AbpAccount::DefaultErrorMessage401Detail', |
|
|
case 0: |
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.details, |
|
|
if ((err as HttpErrorResponse).statusText === 'Unknown Error') { |
|
|
}, |
|
|
|
|
|
).subscribe(() => this.navigateToLogin()); |
|
|
|
|
|
break; |
|
|
|
|
|
case 403: |
|
|
|
|
|
this.createErrorComponent({ |
|
|
|
|
|
title: { |
|
|
|
|
|
key: 'AbpAccount::DefaultErrorMessage403', |
|
|
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError403.title, |
|
|
|
|
|
}, |
|
|
|
|
|
details: { |
|
|
|
|
|
key: 'AbpAccount::DefaultErrorMessage403Detail', |
|
|
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError403.details, |
|
|
|
|
|
}, |
|
|
|
|
|
status: 403, |
|
|
|
|
|
}); |
|
|
|
|
|
break; |
|
|
|
|
|
case 404: |
|
|
|
|
|
this.canCreateCustomError(404) |
|
|
|
|
|
? this.show404Page() |
|
|
|
|
|
: this.showError( |
|
|
|
|
|
{ |
|
|
|
|
|
key: 'AbpAccount::DefaultErrorMessage404', |
|
|
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.title, |
|
|
|
|
|
}, |
|
|
|
|
|
{ |
|
|
|
|
|
key: 'AbpAccount::DefaultErrorMessage404Detail', |
|
|
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.details, |
|
|
|
|
|
}, |
|
|
|
|
|
); |
|
|
|
|
|
break; |
|
|
|
|
|
case 500: |
|
|
this.createErrorComponent({ |
|
|
this.createErrorComponent({ |
|
|
title: DEFAULT_ERROR_MESSAGES.defaultErrorUnknown.title, |
|
|
title: { |
|
|
details: DEFAULT_ERROR_MESSAGES.defaultErrorUnknown.details, |
|
|
key: 'AbpAccount::500Message', |
|
|
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError500.title, |
|
|
|
|
|
}, |
|
|
|
|
|
details: { |
|
|
|
|
|
key: 'AbpAccount::InternalServerErrorMessage', |
|
|
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError500.details, |
|
|
|
|
|
}, |
|
|
|
|
|
status: 500, |
|
|
}); |
|
|
}); |
|
|
} |
|
|
break; |
|
|
break; |
|
|
case 0: |
|
|
default: |
|
|
if ((err as HttpErrorResponse).statusText === 'Unknown Error') { |
|
|
this.showError(DEFAULT_ERROR_MESSAGES.defaultError.details, DEFAULT_ERROR_MESSAGES.defaultError.title); |
|
|
this.createErrorComponent({ |
|
|
break; |
|
|
title: { |
|
|
|
|
|
key: 'AbpAccount::DefaultErrorMessage', |
|
|
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, |
|
|
|
|
|
}, |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
this.showError(DEFAULT_ERROR_MESSAGES.defaultError.details, DEFAULT_ERROR_MESSAGES.defaultError.title); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
} else if (res instanceof RouterError && snq(() => res.event.error.indexOf('Cannot match') > -1, false)) { |
|
|
|
|
|
this.show404Page(); |
|
|
|
|
|
} else if (res instanceof RouterDataResolved && this.componentRef) { |
|
|
|
|
|
this.componentRef.destroy(); |
|
|
|
|
|
this.componentRef = null; |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private showError(message?: string, title?: string, body?: any): Observable<Toaster.Status> { |
|
|
private show401Page() { |
|
|
|
|
|
this.createErrorComponent({ |
|
|
|
|
|
title: { |
|
|
|
|
|
key: 'AbpAccount::401Message', |
|
|
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.title, |
|
|
|
|
|
}, |
|
|
|
|
|
status: 401, |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private show404Page() { |
|
|
|
|
|
this.createErrorComponent({ |
|
|
|
|
|
title: { |
|
|
|
|
|
key: 'AbpAccount::404Message', |
|
|
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.title, |
|
|
|
|
|
}, |
|
|
|
|
|
status: 404, |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private showError( |
|
|
|
|
|
message?: Config.LocalizationParam, |
|
|
|
|
|
title?: Config.LocalizationParam, |
|
|
|
|
|
body?: any, |
|
|
|
|
|
): Observable<Toaster.Status> { |
|
|
if (body) { |
|
|
if (body) { |
|
|
if (body.details) { |
|
|
if (body.details) { |
|
|
message = body.details; |
|
|
message = body.details; |
|
|
@ -123,35 +187,50 @@ export class ErrorHandler { |
|
|
|
|
|
|
|
|
return this.confirmationService.error(message, title, { |
|
|
return this.confirmationService.error(message, title, { |
|
|
hideCancelBtn: true, |
|
|
hideCancelBtn: true, |
|
|
yesCopy: 'OK', |
|
|
yesText: 'AbpAccount::Close', |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private navigateToLogin() { |
|
|
private navigateToLogin() { |
|
|
this.ngZone.run(() => { |
|
|
console.warn(this.store.selectSnapshot(RouterState.url)); |
|
|
this.router.navigate(['/account/login'], { |
|
|
this.store.dispatch( |
|
|
state: { redirectUrl: this.router.url }, |
|
|
new Navigate(['/account/login'], null, { state: { redirectUrl: this.store.selectSnapshot(RouterState.url) } }), |
|
|
}); |
|
|
); |
|
|
}); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
createErrorComponent(instance: Partial<ErrorComponent>) { |
|
|
createErrorComponent(instance: Partial<ErrorComponent>) { |
|
|
const renderer = this.rendererFactory.createRenderer(null, null); |
|
|
const renderer = this.rendererFactory.createRenderer(null, null); |
|
|
const host = renderer.selectRootElement(document.body, true); |
|
|
const host = renderer.selectRootElement(document.body, true); |
|
|
|
|
|
|
|
|
const componentRef = this.cfRes.resolveComponentFactory(ErrorComponent).create(this.injector); |
|
|
this.componentRef = this.cfRes.resolveComponentFactory(ErrorComponent).create(this.injector); |
|
|
|
|
|
|
|
|
for (const key in componentRef.instance) { |
|
|
for (const key in this.componentRef.instance) { |
|
|
if (componentRef.instance.hasOwnProperty(key)) { |
|
|
if (this.componentRef.instance.hasOwnProperty(key)) { |
|
|
componentRef.instance[key] = instance[key]; |
|
|
this.componentRef.instance[key] = instance[key]; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.appRef.attachView(componentRef.hostView); |
|
|
if (this.canCreateCustomError(instance.status as ErrorScreenErrorCodes)) { |
|
|
renderer.appendChild(host, (componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0]); |
|
|
this.componentRef.instance.cfRes = this.cfRes; |
|
|
|
|
|
this.componentRef.instance.customComponent = this.httpErrorConfig.errorScreen.component; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.appRef.attachView(this.componentRef.hostView); |
|
|
|
|
|
renderer.appendChild(host, (this.componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0]); |
|
|
|
|
|
|
|
|
|
|
|
const destroy$ = new Subject<void>(); |
|
|
|
|
|
this.componentRef.instance.destroy$ = destroy$; |
|
|
|
|
|
destroy$.subscribe(() => { |
|
|
|
|
|
this.componentRef.destroy(); |
|
|
|
|
|
this.componentRef = null; |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
componentRef.instance.renderer = renderer; |
|
|
canCreateCustomError(status: ErrorScreenErrorCodes): boolean { |
|
|
componentRef.instance.elementRef = componentRef.location; |
|
|
return snq( |
|
|
componentRef.instance.host = host; |
|
|
() => |
|
|
|
|
|
this.httpErrorConfig.errorScreen.component && |
|
|
|
|
|
this.httpErrorConfig.errorScreen.forWhichErrors.indexOf(status) > -1, |
|
|
|
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|