Hello guys, I am trying to override default behavior when error occur on API side and that is: throw error in dialog (modal view - please check first image). I would like to replace it with toast notification (second image). Is there any way to do it ?
Release: .Net Core + Angular ASPNETZero: v5.5
Thanks in advance, Peter
6 Answer(s)
-
1
If you want to do this for all error messages, you can create a similar javascript file for the https://github.com/aspnetboilerplate/aspnetboilerplate/blob/dev/src/Abp.Web.Resources/Abp/Framework/scripts/libs/abp.sweet-alert.js and iclude it into angular.json instead of abp.sweet-alert.js.
If you want to do this only for server-side error messages, you need to write your own http interceptor similar to https://github.com/aspnetboilerplate/abp-ng2-module/blob/master/src/abpHttpInterceptor.ts and use it instead of AbpHttpInterceptor in your app.
-
0
Hello sir, thank you for response. I started implementing second solution, that is to create my own HTTP interceptor, and issue ocurred:
Response errors are thrown twice for some reason, please check provided images and code, maybe you will have more information for me...
On first image we can see that there is one error (type 500):
On second image you can see that there are two toast erros shown on UI:
This is my customHttpInterceptor (used instead of abpHttpInterceptor)
import { Injectable } from '@angular/core'; import { Observable, Subject, of } from 'rxjs'; import { HttpClient, HttpInterceptor, HttpHandler, HttpRequest, HttpEvent, HttpResponse, HttpErrorResponse, HttpHeaders } from '@angular/common/http'; import { MessageService } from 'abp-ng2-module/dist/src/message/message.service'; import { LogService } from 'abp-ng2-module/dist/src/log/log.service'; import { TokenService } from 'abp-ng2-module/dist/src/auth/token.service'; import { UtilsService } from 'abp-ng2-module/dist/src/utils/utils.service'; import { NotifyService } from 'abp-ng2-module/dist/src/notify/notify.service'; import { SignalRService } from '@app/shared/common/signalr/signalRService'; export interface IValidationErrorInfo { message: string; members: string[]; } export interface IErrorInfo { code: number; message: string; details: string; validationErrors: IValidationErrorInfo[]; } export interface IAjaxResponse { success: boolean; result?: any; targetUrl?: string; error?: IErrorInfo; unAuthorizedRequest: boolean; __abp: boolean; } @Injectable() // tslint:disable-next-line:class-name export class customHttpConfiguration { constructor( private _messageService: MessageService, private _logService: LogService, private _notify: NotifyService) { } defaultError = <IErrorInfo>{ message: 'An error has occurred!', details: 'Error details were not sent by server.' }; defaultError401 = <IErrorInfo>{ message: 'You are not authenticated!', details: 'You should be authenticated (sign in) in order to perform this operation.' }; defaultError403 = <IErrorInfo>{ message: 'You are not authorized!', details: 'You are not allowed to perform this operation.' }; defaultError404 = <IErrorInfo>{ message: 'Resource not found!', details: 'The resource requested could not be found on the server.' }; logError(error: IErrorInfo): void { this._logService.error(error); } showError(error: IErrorInfo): any { if (error.details) { return this._messageService.error(error.details, error.message || this.defaultError.message); } else { return this._messageService.error(error.message || this.defaultError.message); } } toastError(error: IErrorInfo): void { if (error.details) { return this._notify.error(error.details, error.message || this.defaultError.message); } else { return this._notify.error(error.message || this.defaultError.message); } } handleTargetUrl(targetUrl: string): void { if (!targetUrl) { location.href = '/'; } else { location.href = targetUrl; } } handleUnAuthorizedRequest(messagePromise: any, targetUrl?: string) { const self = this; if (messagePromise) { messagePromise.done(() => { this.handleTargetUrl(targetUrl || '/'); }); } else { self.handleTargetUrl(targetUrl || '/'); } } handleNonAbpErrorResponse(response: HttpResponse<any>) { const self = this; switch (response.status) { case 401: self.handleUnAuthorizedRequest( self.showError(self.defaultError401), '/' ); break; case 403: self.showError(self.defaultError403); break; case 404: self.showError(self.defaultError404); break; default: self.showError(self.defaultError); break; } } handleAbpResponse(response: HttpResponse<any>, ajaxResponse: IAjaxResponse): HttpResponse<any> { let newResponse: HttpResponse<any>; if (ajaxResponse.success) { newResponse = response.clone({ body: ajaxResponse.result }); if (ajaxResponse.targetUrl) { this.handleTargetUrl(ajaxResponse.targetUrl); } } else { newResponse = response.clone({ body: ajaxResponse.result }); if (!ajaxResponse.error) { ajaxResponse.error = this.defaultError; } if (response.status === 500) { // this.logError(ajaxResponse.error); this.toastError(ajaxResponse.error); } else { this.logError(ajaxResponse.error); this.showError(ajaxResponse.error); } if (response.status === 401) { this.handleUnAuthorizedRequest(null, ajaxResponse.targetUrl); } } return newResponse; } getAbpAjaxResponseOrNull(response: HttpResponse<any>): IAjaxResponse | null { if (!response || !response.headers) { return null; } let contentType = response.headers.get('Content-Type'); if (!contentType) { this._logService.warn('Content-Type is not sent!'); return null; } if (contentType.indexOf('application/json') < 0) { this._logService.warn('Content-Type is not application/json: ' + contentType); return null; } let responseObj = JSON.parse(JSON.stringify(response.body)); if (!responseObj.__abp) { return null; } return responseObj as IAjaxResponse; } handleResponse(response: HttpResponse<any>): HttpResponse<any> { let ajaxResponse = this.getAbpAjaxResponseOrNull(response); if (ajaxResponse == null) { return response; } return this.handleAbpResponse(response, ajaxResponse); } blobToText(blob: any): Observable<string> { return new Observable<string>((observer: any) => { if (!blob) { observer.next(''); observer.complete(); } else { let reader = new FileReader(); reader.onload = function () { observer.next(this.result); observer.complete(); }; reader.readAsText(blob); } }); } } @Injectable() // tslint:disable-next-line:class-name export class customHttpInterceptor implements HttpInterceptor { protected configuration: customHttpConfiguration; private _tokenService: TokenService = new TokenService(); private _utilsService: UtilsService = new UtilsService(); private _logService: LogService = new LogService(); constructor(configuration: customHttpConfiguration) { this.configuration = configuration; } intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { let interceptObservable = new Subject<HttpEvent<any>>(); let modifiedRequest = this.normalizeRequestHeaders(request); next.handle(modifiedRequest) .subscribe((event: HttpEvent<any>) => { this.handleSuccessResponse(event, interceptObservable); }, (error: any) => { return this.handleErrorResponse(error, interceptObservable); }); return interceptObservable; } protected normalizeRequestHeaders(request: HttpRequest<any>): HttpRequest<any> { let modifiedHeaders = new HttpHeaders(); modifiedHeaders = request.headers.set('Pragma', 'no-cache') .set('Cache-Control', 'no-cache') .set('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT'); modifiedHeaders = this.addXRequestedWithHeader(modifiedHeaders); modifiedHeaders = this.addAuthorizationHeaders(modifiedHeaders); modifiedHeaders = this.addAspNetCoreCultureHeader(modifiedHeaders); modifiedHeaders = this.addAcceptLanguageHeader(modifiedHeaders); modifiedHeaders = this.addTenantIdHeader(modifiedHeaders); return request.clone({ headers: modifiedHeaders }); } protected addXRequestedWithHeader(headers: HttpHeaders): HttpHeaders { if (headers) { headers = headers.set('X-Requested-With', 'XMLHttpRequest'); } return headers; } protected addAspNetCoreCultureHeader(headers: HttpHeaders): HttpHeaders { let cookieLangValue = this._utilsService.getCookieValue('Abp.Localization.CultureName'); if (cookieLangValue && headers && !headers.has('.AspNetCore.Culture')) { headers = headers.set('.AspNetCore.Culture', cookieLangValue); } return headers; } protected addAcceptLanguageHeader(headers: HttpHeaders): HttpHeaders { let cookieLangValue = this._utilsService.getCookieValue('Abp.Localization.CultureName'); if (cookieLangValue && headers && !headers.has('Accept-Language')) { headers = headers.set('Accept-Language', cookieLangValue); } return headers; } protected addTenantIdHeader(headers: HttpHeaders): HttpHeaders { let cookieTenantIdValue = this._utilsService.getCookieValue('Abp.TenantId'); if (cookieTenantIdValue && headers && !headers.has('Abp.TenantId')) { headers = headers.set('Abp.TenantId', cookieTenantIdValue); } return headers; } protected addAuthorizationHeaders(headers: HttpHeaders): HttpHeaders { let authorizationHeaders = headers ? headers.getAll('Authorization') : null; if (!authorizationHeaders) { authorizationHeaders = []; } if (!this.itemExists(authorizationHeaders, (item: string) => item.indexOf('Bearer ') === 0)) { let token = this._tokenService.getToken(); if (headers && token) { headers = headers.set('Authorization', 'Bearer ' + token); } } return headers; } protected handleSuccessResponse(event: HttpEvent<any>, interceptObservable: Subject<HttpEvent<any>>): void { let self = this; if (event instanceof HttpResponse) { if (event.body instanceof Blob && event.body.type && event.body.type.indexOf('application/json') >= 0) { let clonedResponse = event.clone(); self.configuration.blobToText(event.body).subscribe(json => { const responseBody = json === 'null' ? {} : JSON.parse(json); let modifiedResponse = self.configuration.handleResponse(event.clone({ body: responseBody })); interceptObservable.next(modifiedResponse.clone({ body: new Blob([JSON.stringify(modifiedResponse.body)], { type: 'application/json' }) })); interceptObservable.complete(); }); } else { interceptObservable.next(event); interceptObservable.complete(); } } } protected handleErrorResponse(error: any, interceptObservable: Subject<HttpEvent<any>>): Observable<any> { let errorObservable = new Subject<any>(); if (!(error.error instanceof Blob)) { interceptObservable.error(error); interceptObservable.complete(); return of({}); } this.configuration.blobToText(error.error).subscribe(json => { const errorBody = (json === '' || json === 'null') ? {} : JSON.parse(json); const errorResponse = new HttpResponse({ headers: error.headers, status: error.status, body: errorBody }); let ajaxResponse = this.configuration.getAbpAjaxResponseOrNull(errorResponse); if (ajaxResponse != null) { this.configuration.handleAbpResponse(errorResponse, ajaxResponse); } else { this.configuration.handleNonAbpErrorResponse(errorResponse); } errorObservable.complete(); interceptObservable.error(error); interceptObservable.complete(); }); return errorObservable; } private itemExists<T>(items: T[], predicate: (item: T) => boolean): boolean { for (let i = 0; i < items.length; i++) { if (predicate(items[i])) { return true; } } return false; } }
This is my root module (providers part of it). Here I provide customHttpInterceptor and you can see that I added customHttpConfiguration as well
providers: [ ** { provide: HTTP_INTERCEPTORS, useClass: customHttpInterceptor, multi: true },** { provide: API_BASE_URL, useFactory: getRemoteServiceBaseUrl }, { provide: APP_INITIALIZER, useFactory: appInitializerFactory, deps: [Injector, PlatformLocation], multi: true }, { provide: LOCALE_ID, useFactory: getCurrentLanguage }, AppInsightsService, AppTranslationService, ** customHttpConfiguration** ],
Am I doing something wrong ?
-
0
Hi @tomrapdebruyne
Have you removed the usages of AbpHttpInterceptor ? You should only use your own version in this case.
-
0
Hello @ismcagdas, thanks for fast response...
Yes, it is fully removed from project (from root module imports and providers as well from service proxy module import and providers).
Still same issue - double erros for one request.
I did some testing and it seems that even with AbpHttpInterceptor logic I have same issue, it is just that sweet-alert is used (dialog error) and it handles it somehow correctly, but logging of error is done twice as well.
Do you know any possible cause or solution for this ?
-
0
@tomrapdebruyne
Are you able to expand the javascript errors and see if the both error logs have the same origin (source code) ?
-
0
@ismcagdas
I've managed to solve this problem, for some reason I had to give interceptor name: abpHttpInterceptor, not customHttpInterceptor,using that name (abpHttpInterceptor) solved my problem of having double error popups.
Thank you for your help.