Base solution for your next web application

Activities of "henriksorensen"

We've run into an issue with handling an issue on the angular side when our angular application has a relative path like /application/, for example, http://localhost/application/

The default ASP.NET Zero handler redirects the user according to the following code (zero-template-http-configuration.service.ts):

import { Injectable } from '@angular/core'; import { AbpHttpConfigurationService, LogService, MessageService } from 'abp-ng2-module'; import { Router } from '@angular/router';

@Injectable({ providedIn: 'root', }) export class ZeroTemplateHttpConfigurationService extends AbpHttpConfigurationService { constructor(messageService: MessageService, logService: LogService, private _route: Router) { super(messageService, logService); }

// Override handleUnAuthorizedRequest so it doesn't refresh the page during failed login attempts.
handleUnAuthorizedRequest(messagePromise: any, targetUrl?: string) {
    if (this._route.url && (
        this._route.url.startsWith('/account/login') || this._route.url.startsWith('/account/session-locked')
    )) {
        return;
    }

    const self = this;

    if (messagePromise) {
        messagePromise.done(() => {
            this.handleTargetUrl(targetUrl || '/');
        });
    } else {
        self.handleTargetUrl(targetUrl || '/');
    }
}

}

Actual result

The redirect occurs to the domain name and does not take into account the relative path.

Expected result After the redirect, the user is redirected to the correct root URL

So, could you please provide us: 1. How to pass targetUrl argument to this method? In our case it is NULL

2. Or how can we solve the problem with an application whose relative path is different from the domain name = '/'?

Error message with the inability to log out

We encountered an error when the user's session has expired and the user is trying to log out of the site. It leads to an error message with the inability to log out.

Pre-conditions: User session is expired (AccessTokenExpirationMinutes parameter is used to set session timeout)

Debug steps:

  1. User clicks Logout button

  2. From angular project, the GET request is sent to back-end Logout endpoint - /api/TokenAuth/LogOut Original ASP NET Zero code: logout(reload?: boolean, returnUrl?: string): void { let customHeaders = { [abp.multiTenancy.tenantIdCookieName]: abp.multiTenancy.getTenantIdCookie(), Authorization: 'Bearer ' + abp.auth.getToken(), };

     XmlHttpRequestHelper.ajax(
         'GET',
         AppConsts.remoteServiceBaseUrl + '/api/TokenAuth/LogOut',
         customHeaders,
         null,
         () => {
             abp.auth.clearToken();
             abp.auth.clearRefreshToken();
             new LocalStorageService().removeItem(AppConsts.authorization.encrptedAuthTokenName, () => {
                 if (reload !== false) {
                     if (returnUrl) {
                         location.href = returnUrl;
                     } else {
                         location.href = '';
                     }
                 }
             });
         }
     );
    

    }

  3. This endpoint looks like: Original ASP NET Zero code:

     [HttpGet]
     [AbpMvcAuthorize]
     public async Task LogOut()
     {
         if (AbpSession.UserId != null)
         {
             var tokenValidityKeyInClaims = User.Claims.First(c => c.Type == AppConsts.TokenValidityKey);
             await RemoveTokenAsync(tokenValidityKeyInClaims.Value);
    
             var refreshTokenValidityKeyInClaims =
                 User.Claims.FirstOrDefault(c => c.Type == AppConsts.RefreshTokenValidityKey);
             if (refreshTokenValidityKeyInClaims != null)
             {
                 await RemoveTokenAsync(refreshTokenValidityKeyInClaims.Value);
             }
    
             if (AllowOneConcurrentLoginPerUser())
             {
                 await _securityStampHandler.RemoveSecurityStampCacheItem(
                     AbpSession.TenantId,
                     AbpSession.GetUserId()
                 );
             }
         }
     }
    
  4. The main problem is with the AbpMvcAuthorize attribute [AbpMvcAuthorize] Since user is already not authenticated, then it leads to 404 response from server(suggest, one more error in ASP NET Zero, it should be 401).

  5. On front-end part this response is handled as:

export class XmlHttpRequestHelper { static ajax(type: string, url: string, customHeaders: any, data: any, success: any) { let xhr = new XMLHttpRequest();

    xhr.onreadystatechange = () => {
        if (xhr.readyState === XMLHttpRequest.DONE) {
            if (xhr.status === 200) {
                let result = JSON.parse(xhr.responseText);
                success(result);
            } else if (xhr.status !== 0) {
                alert(abp.localization.localize('InternalServerError', 'AbpWeb'));
            }
        }
    };

    url += (url.indexOf('?') >= 0 ? '&' : '?') + 'd=' + new Date().getTime();
    xhr.open(type, url, true);

    for (let property in customHeaders) {
        if (customHeaders.hasOwnProperty(property)) {
            xhr.setRequestHeader(property, customHeaders[property]);
        }
    }

    xhr.setRequestHeader('Content-type', 'application/json');
    if (data) {
        xhr.send(data);
    } else {
        xhr.send();
    }
}

}

Alert message is shown:

alert(abp.localization.localize('InternalServerError', 'AbpWeb'));

Expected result Alert Internal Server Error message

Actual result No errors, user is logged out, login page is displayed

Question

Do you agree that the expected result is not as desired? Will you fix this in a later release? (we are using 11.4)

Sometimes, during healthchecking, we get error about connection to database. It happens 2-3 times in 2500 healthcheck requests. Could you help us to find possible reason of this error?

Log: TASWebclient;2022-10-30 23:34:50,139;ERROR;Microsoft.Extensions.Diagnostics.HealthChecks.DefaultHealthCheckService;[.NET ThreadPool Worker];(null);(null);Health check Database Connection with user check with status Unhealthy completed after 4.4974ms with message 'WebClientDbContext could not connect to database.';System.NullReferenceException: Object reference not set to an instance of an object. at Microsoft.AspNetCore.Http.DefaultHttpRequest.get_Scheme() at Microsoft.AspNetCore.Http.Extensions.UriHelper.GetDisplayUrl(HttpRequest request) at Abp.EntityHistory.EntityHistoryHelper.CreateEntityChangeSet(ICollection1 entityEntries) at Abp.Zero.EntityFrameworkCore.AbpZeroCommonDbContext3.SaveChangesAsync(CancellationToken cancellationToken) at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChangesAsync() at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.CompleteUowAsync() at Abp.Domain.Uow.UnitOfWorkBase.CompleteAsync() at TAS.WebClient.HealthChecks.WebClientDbContextUsersHealthCheck.CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken) in D:\BuildAgents\DevOpsAgent39_Work\4\s\TASWebclient\aspnet-core\src\TAS.WebClient.Application\HealthChecks\WebClientDbContextUsersHealthCheck.cs:line 58 ;

WebClientDbContextUsersHealthCheck.cs :

using System;
using System.Threading;
using System.Threading.Tasks;
using Abp.Domain.Uow;
using Abp.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using TAS.WebClient.EntityFrameworkCore;

namespace TAS.WebClient.HealthChecks
{
    public class WebClientDbContextUsersHealthCheck : IHealthCheck
    {
        private readonly IDbContextProvider<WebClientDbContext> _dbContextProvider;
        private readonly IUnitOfWorkManager _unitOfWorkManager;

        public WebClientDbContextUsersHealthCheck(
            IDbContextProvider<WebClientDbContext> dbContextProvider,
            IUnitOfWorkManager unitOfWorkManager
            )
        {
            _dbContextProvider = dbContextProvider;
            _unitOfWorkManager = unitOfWorkManager;
        }

        public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = new CancellationToken())
        {
            try
            {
                using (var uow = _unitOfWorkManager.Begin())
                {
                    // Switching to host is necessary for single tenant mode.
                    using (_unitOfWorkManager.Current.SetTenantId(null))
                    {
                        var dbContext = await _dbContextProvider.GetDbContextAsync();
                        if (!await dbContext.Database.CanConnectAsync(cancellationToken))
                        {
                            return HealthCheckResult.Unhealthy(
                                "WebClientDbContext could not connect to database"
                            );
                        }

                        var user = await dbContext.Users.AnyAsync(cancellationToken);
                        await uow.CompleteAsync();

                        if (user)
                        {
                            return HealthCheckResult.Healthy("WebClientDbContext connected to database and checked whether user added");
                        }

                        return HealthCheckResult.Unhealthy("WebClientDbContext connected to database but there is no user.");

                    }
                }
            }
            catch (Exception e)
            {
                return HealthCheckResult.Unhealthy("WebClientDbContext could not connect to database.", e);
            }
        }
    }
}

AbpZeroHealthCheck.cs:

using Microsoft.Extensions.DependencyInjection;
using TAS.WebClient.HealthChecks;

namespace TAS.WebClient.Web.HealthCheck
{
    public static class AbpZeroHealthCheck
    {
        public static IHealthChecksBuilder AddAbpZeroHealthCheck(this IServiceCollection services)
        {
            var builder = services.AddHealthChecks();
            builder.AddCheck<WebClientDbContextHealthCheck>("Database Connection");
            builder.AddCheck<WebClientDbContextUsersHealthCheck>("Database Connection with user check");
            builder.AddCheck<CacheHealthCheck>("Cache");
            builder.AddCheck<ExternalApiHealthCheck>("ExternalApi Connection");
            builder.AddCheck<DocumentApiHealthCheck>("DocumentApi Connection");

            // add your custom health checks here
            // builder.AddCheck<MyCustomHealthCheck>("my health check");

            return builder;
        }
    }
}

Startup.cs:

if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
            {
                services.AddAbpZeroHealthCheck();

                var healthCheckUISection = _appConfiguration.GetSection("HealthChecks")?.GetSection("HealthChecksUI");

                if (bool.Parse(healthCheckUISection["HealthChecksUIEnabled"]))
                {
                    services.Configure<HealthChecksUISettings>(settings =>
                    {
                        healthCheckUISection.Bind(settings, c => c.BindNonPublicProperties = true);
                    });
                    services.AddHealthChecksUI()
                        .AddInMemoryStorage();
                }
            }

...



app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<AbpCommonHub>("/signalr");
                endpoints.MapHub<ChatHub>("/signalr-chat");

                endpoints.MapControllerRoute("defaultWithArea", "{area}/{controller=Home}/{action=Index}/{id?}");
                endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");

                if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
                {
                    endpoints.MapHealthChecks("/health", new HealthCheckOptions()
                    {
                        Predicate = _ => true,
                        ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
                    });
                }

                app.ApplicationServices.GetRequiredService<IAbpAspNetCoreConfiguration>().EndpointConfiguration.ConfigureAllEndpoints(endpoints);
            });

            if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksEnabled"]))
            {
                if (bool.Parse(_appConfiguration["HealthChecks:HealthChecksUI:HealthChecksUIEnabled"]))
                {
                    app.UseHealthChecksUI();
                }
            }

appsettings.json:

  "HealthChecks": {
    "HealthChecksEnabled": true,
    "HealthChecksUI": {
      "HealthChecksUIEnabled": true,
      "HealthChecks": [
        {
          "Name": "TAS.WebClient.Web.Host",
          "Uri": "https://localhost:44301/health"
        }
      ],
      "EvaluationTimeOnSeconds": 10,
      "MinimumSecondsBetweenFailureNotifications": 60
    }
  },
  

Product Version 9.1 Angular .Net Core ABP version 51.5

<br> And I am unable to configure the grant type / Response type in the OpenId configuring section. I'm able to configure ClientId, Client Secret etc. but not grant type. I want use Granttype = code. It looks like Angular is using grant type = "hybrid" by default. Please confirm?

I'm able to configure/code the same in the .Web.Startup.AuthConfigurer class in the MVC version of Asp.Net Zero (my demo solution). Like this

authenticationBuilder.AddOpenIdConnect(options => { options.ClientId = configuration["Authentication:OpenId:ClientId"]; options.Authority = configuration["Authentication:OpenId:Authority"]; options.SignedOutRedirectUri = configuration["App:WebSiteRootAddress"] + "Account/Logout"; options.ResponseType = OpenIdConnectResponseType.Code; // Grantstype = code

But I can't find a similar way for the angular solution? Please assist

Best regards Tom

My Configuration:

AllowSocialLoginSettingsPerTenant = true and "OpenId": { "IsEnabled": "true", "Authority": "https://localhost:44313", "LoginUrl": "https://localhost:44313/Account/Login?", "ClientId": "my-webclient", "ClientSecret": "XYZ", "ValidateIssuer": "false", "ClaimsMapping": [ { "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", "key": "http://schemas.microsoft.com/identity/claims/objectidentifier" } ] },

IdentityServer4 3.1 running on "https://localhost: 44313" with "my-webclient" like this "AllowedGrantTypes": [ "authorization_code" ], "RequirePkce": true,

Hi

I am calling a external rest service which returns a 404 error if query is not found. I would like to handle this error (and other errors) and not the default defaultError404 AbpHttpConfiguration.

this.ServiceProxy.apiV1Post(input)
                    .finally(() => { this.saving = false; })
                    .subscribe(() => {
                        this.notify.info(this.l('SavedSuccessfully'));
                        this.close();
                        this.modalSave.emit(null);
                    });

How to display custom error and prevent defaultError404 being called?

We call the exteranl service via serviceproxy generated by nswag.
It is a angular 5 +.net core project

I seen the "abpHandleError" setting but that seems to relate to a jquery project. Correct me if i am wrong.

Thanks, Henrik

Hi

We have to access and update tables in two databases within same transaction scope. We have the same setup as described here, <a class="postlink" href="https://github.com/aspnetboilerplate/aspnetboilerplate/issues/2325">https://github.com/aspnetboilerplate/as ... ssues/2325</a>. Also we have changed database transaction strategy to DbContextEfTransactionStrategy as described in <a class="postlink" href="https://aspnetboilerplate.com/Pages/Documents/EntityFramework-Integration#transaction-management">https://aspnetboilerplate.com/Pages/Doc ... management</a>.

The metod public DbContext CreateDbContext<TDbContext>(string connectionString, IDbContextResolver dbContextResolver) is always called the default connectionstring and therefore the second dbcontext is never inserted into this.ActiveTransactions[connectionString].

Abp Version is 2.2.2

Regards,

Hi,

I am trying to add a https (port 443) binding in IIS for the Angular site but I get an error saying "An Error Has Occurred"

I have checked the following:

  1. The backend is running and I am able to see the swagger ui
  2. The frontend appsettings.json is pointing to the correct port for the backend.
  3. The CorsOrigins are pointing to the https address

If I switch the Angular site back to http (port 80) I don't get the error.

I have tried to search the forums for similar problems but with no luck.

Can you provide a quick guide to enabling HTTPS for the Angular site?

Best regards Henrik

Showing 1 to 7 of 7 entries