Base solution for your next web application

Activities of "tomrapdebruyne"

Hi,

When selecting the Thai language it uses the Buddhist calendar by default. We require the gregorian calendar as the difference in data causes issues when processing data.

How would we best go about using the gregorian calendar with the Thai language? What other languages would be prone to this issue and would it be possible to set the gregorian calendar as default for all languages?

Kind regards

Product version: 9.2.0 Product type: Angular Product framework type: .NET Core ABP Framework version 4.2.2

Prerequisites

Please answer the following questions before submitting an issue. YOU MAY DELETE THE PREREQUISITES SECTION.

  • What is your product version? v.9.3
  • What is your product type (Angular or MVC)? Angular
  • What is product framework type (.net framework or .net core)? .Net Core

If issue related with ABP Framework

  • What is ABP Framework version? Abp = 5.13

Our issue

Via an Azure FunctionApp we are calling the API interface to fetch some data. As you can see in the picture, it sometimes takes a lot of time to simply get teh openid-configuration which is always the same files. Today, this is blocking us as the functionApp calling the API has to wait (too) long to be to continue

Some questions:

  • Is there a way to debug/analyse, enable a verbose mode on the IdentityServer which is integrate in the framework?
  • Is it possible to deploy the openid configuration more or less as a static file as it seems to be generated everything time?

The Azure funtionApp is usign the application settings:

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

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

Is there already some news when the Metronic v5.3 will be integrated in the platform. Is it possible to offer the v5.3 download via the ASPNetZero download page?

How can we use the layout builder tool as it requires a Metronic layout tool?

Within the metronic theme: How can we switch from the default layout/style to the demo 3 layout/style?

Showing 1 to 9 of 9 entries