|
|
|
@ -1,68 +1,40 @@ |
|
|
|
import { |
|
|
|
AuthService, |
|
|
|
HttpErrorReporterService, |
|
|
|
LocalizationParam, |
|
|
|
RouterEvents, |
|
|
|
SessionStateService, |
|
|
|
} from '@abp/ng.core'; |
|
|
|
import { AuthService, HttpErrorReporterService } from '@abp/ng.core'; |
|
|
|
import { HttpErrorResponse } from '@angular/common/http'; |
|
|
|
import { |
|
|
|
ComponentFactoryResolver, |
|
|
|
ComponentRef, |
|
|
|
inject, |
|
|
|
Injectable, |
|
|
|
Injector, |
|
|
|
RendererFactory2, |
|
|
|
} from '@angular/core'; |
|
|
|
import { NavigationError, ResolveEnd } from '@angular/router'; |
|
|
|
import { Observable, of, Subject, throwError } from 'rxjs'; |
|
|
|
import { inject, Injectable, Injector } from '@angular/core'; |
|
|
|
import { Observable, of, throwError } from 'rxjs'; |
|
|
|
import { catchError, filter, switchMap } from 'rxjs/operators'; |
|
|
|
import { HttpErrorWrapperComponent } from '../components/http-error-wrapper/http-error-wrapper.component'; |
|
|
|
import { ErrorScreenErrorCodes, HttpErrorConfig } from '../models/common'; |
|
|
|
import { CustomHttpErrorHandlerService, HttpErrorConfig } from '../models/common'; |
|
|
|
import { Confirmation } from '../models/confirmation'; |
|
|
|
import { ConfirmationService } from '../services/confirmation.service'; |
|
|
|
import { HTTP_ERROR_HANDLER } from '../tokens/http-error.token'; |
|
|
|
import { CUSTOM_ERROR_HANDLERS, HTTP_ERROR_HANDLER } from '../tokens/http-error.token'; |
|
|
|
import { CreateErrorComponentService } from '../services/create-error-component.service'; |
|
|
|
import { CanCreateCustomErrorService } from '../services/can-create-custom-error.service'; |
|
|
|
import { DEFAULT_ERROR_LOCALIZATIONS, DEFAULT_ERROR_MESSAGES } from '../constants/error'; |
|
|
|
import { RouterErrorHandlerService } from '../services/router-error-handler.service'; |
|
|
|
|
|
|
|
@Injectable({ providedIn: 'root' }) |
|
|
|
export class ErrorHandler { |
|
|
|
protected httpErrorHandler = this.injector.get(HTTP_ERROR_HANDLER, (_, err: HttpErrorResponse) => |
|
|
|
throwError(err), |
|
|
|
); |
|
|
|
|
|
|
|
protected httpErrorReporter: HttpErrorReporterService; |
|
|
|
protected routerEvents: RouterEvents; |
|
|
|
protected confirmationService: ConfirmationService; |
|
|
|
protected cfRes: ComponentFactoryResolver; |
|
|
|
protected rendererFactory: RendererFactory2; |
|
|
|
protected httpErrorConfig: HttpErrorConfig; |
|
|
|
protected sessionStateService: SessionStateService; |
|
|
|
private authService: AuthService; |
|
|
|
|
|
|
|
|
|
|
|
private createErrorComponentService = inject(CreateErrorComponentService); |
|
|
|
private canCreateCustomErrorService = inject(CanCreateCustomErrorService); |
|
|
|
private customErrorHandlers = inject(CUSTOM_ERROR_HANDLERS); |
|
|
|
private routerErrorHandlerService = inject(RouterErrorHandlerService); |
|
|
|
|
|
|
|
constructor(protected injector: Injector) { |
|
|
|
this.httpErrorReporter = injector.get(HttpErrorReporterService); |
|
|
|
this.routerEvents = injector.get(RouterEvents); |
|
|
|
this.confirmationService = injector.get(ConfirmationService); |
|
|
|
this.cfRes = injector.get(ComponentFactoryResolver); |
|
|
|
this.rendererFactory = injector.get(RendererFactory2); |
|
|
|
this.httpErrorConfig = injector.get('HTTP_ERROR_CONFIG'); |
|
|
|
this.authService = this.injector.get(AuthService); |
|
|
|
this.sessionStateService = this.injector.get(SessionStateService); |
|
|
|
|
|
|
|
this.listenToRestError(); |
|
|
|
this.listenToRouterError(); |
|
|
|
} |
|
|
|
|
|
|
|
protected listenToRouterError() { |
|
|
|
this.routerEvents |
|
|
|
.getNavigationEvents('Error') |
|
|
|
.pipe(filter(this.filterRouteErrors)) |
|
|
|
.subscribe(() => this.show404Page()); |
|
|
|
this.routerErrorHandlerService.listen(); |
|
|
|
} |
|
|
|
|
|
|
|
protected listenToRestError() { |
|
|
|
@ -84,177 +56,45 @@ export class ErrorHandler { |
|
|
|
); |
|
|
|
}; |
|
|
|
|
|
|
|
private handleError(err: any) { |
|
|
|
const body = err?.error?.error || { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, |
|
|
|
}; |
|
|
|
|
|
|
|
if (err instanceof HttpErrorResponse && err.headers.get('Abp-Tenant-Resolve-Error')) { |
|
|
|
this.sessionStateService.setTenant(null) |
|
|
|
this.authService.logout().subscribe(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const isAbpErrorFormat = err.headers.get('_AbpErrorFormat'); |
|
|
|
if (err instanceof HttpErrorResponse && isAbpErrorFormat) { |
|
|
|
const confirmation$ = this.showErrorWithRequestBody(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, |
|
|
|
}, |
|
|
|
{ |
|
|
|
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, |
|
|
|
}, |
|
|
|
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; |
|
|
|
} |
|
|
|
} |
|
|
|
protected sortHttpErrorHandlers( |
|
|
|
a: CustomHttpErrorHandlerService, |
|
|
|
b: CustomHttpErrorHandlerService, |
|
|
|
) { |
|
|
|
return (b.priority || 0) - (a.priority || 0); |
|
|
|
} |
|
|
|
|
|
|
|
protected show401Page() { |
|
|
|
this.createErrorComponent({ |
|
|
|
title: { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError401.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError401.title, |
|
|
|
}, |
|
|
|
status: 401, |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
protected show404Page() { |
|
|
|
this.createErrorComponent({ |
|
|
|
title: { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError404.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError404.title, |
|
|
|
}, |
|
|
|
status: 404, |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
protected showErrorWithRequestBody(body: any) { |
|
|
|
let message: LocalizationParam; |
|
|
|
let title: LocalizationParam; |
|
|
|
|
|
|
|
if (body.details) { |
|
|
|
message = body.details; |
|
|
|
title = body.message; |
|
|
|
} else if (body.message) { |
|
|
|
title = { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, |
|
|
|
}; |
|
|
|
message = body.message; |
|
|
|
} else { |
|
|
|
message = body.message || { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, |
|
|
|
}; |
|
|
|
title = ''; |
|
|
|
private handleError(err: unknown) { |
|
|
|
if (this.customErrorHandlers && this.customErrorHandlers.length) { |
|
|
|
const canHandleService = this.customErrorHandlers |
|
|
|
.sort(this.sortHttpErrorHandlers) |
|
|
|
.find(service => service.canHandle(err)); |
|
|
|
if (canHandleService) { |
|
|
|
canHandleService.execute(); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return this.showError(message, title); |
|
|
|
this.showError().subscribe(); |
|
|
|
} |
|
|
|
|
|
|
|
protected showError( |
|
|
|
message: LocalizationParam, |
|
|
|
title: LocalizationParam, |
|
|
|
): Observable<Confirmation.Status> { |
|
|
|
protected showError(): Observable<Confirmation.Status> { |
|
|
|
const title = { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.title, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.title, |
|
|
|
}; |
|
|
|
const message = { |
|
|
|
key: DEFAULT_ERROR_LOCALIZATIONS.defaultError.details, |
|
|
|
defaultValue: DEFAULT_ERROR_MESSAGES.defaultError.details, |
|
|
|
}; |
|
|
|
return this.confirmationService.error(message, title, { |
|
|
|
hideCancelBtn: true, |
|
|
|
yesText: 'AbpAccount::Close', |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
private navigateToLogin() { |
|
|
|
this.authService.navigateToLogin(); |
|
|
|
} |
|
|
|
|
|
|
|
createErrorComponent(instance: Partial<HttpErrorWrapperComponent>) { |
|
|
|
this.createErrorComponentService.execute(instance); |
|
|
|
} |
|
|
|
|
|
|
|
canCreateCustomError(status: ErrorScreenErrorCodes): boolean { |
|
|
|
return this.canCreateCustomErrorService.execute(status); |
|
|
|
} |
|
|
|
|
|
|
|
protected filterRestErrors = ({ status }: HttpErrorResponse): boolean => { |
|
|
|
if (typeof status !== 'number') return false; |
|
|
|
|
|
|
|
@ -263,12 +103,4 @@ export class ErrorHandler { |
|
|
|
this.httpErrorConfig.skipHandledErrorCodes.findIndex(code => code === status) < 0 |
|
|
|
); |
|
|
|
}; |
|
|
|
|
|
|
|
protected filterRouteErrors = (navigationError: NavigationError): boolean => { |
|
|
|
return ( |
|
|
|
navigationError.error?.message?.indexOf('Cannot match') > -1 && |
|
|
|
!!this.httpErrorConfig.skipHandledErrorCodes && |
|
|
|
this.httpErrorConfig.skipHandledErrorCodes.findIndex(code => code === 404) < 0 |
|
|
|
); |
|
|
|
}; |
|
|
|
} |
|
|
|
|