Base solution for your next web application

Activities of "tomrapdebruyne"

@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.

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 ?

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 ?

Showing 1 to 3 of 3 entries