@maliming
Here are logs from both HttpApi hosts. Level was set to 'Debug':
.MinimumLevel.Override("Microsoft", LogEventLevel.Debug)
Please be aware, that this time response times are less - though, they are still too big:
https://1drv.ms/u/s!AhWdpZddvifTtw10XrTEduNTBt8-?e=RKpfLs https://1drv.ms/u/s!AhWdpZddvifTtwzipyhO1VtNwgbF?e=3oxwaQ
P.S. Could you please add possibility to attach log files to a message, not only images?
We make calls from Angular app to several HTTP hosts which reside:
Gathering data from all the hosts is too slow, please have a look:
Log file analysis shows that is it not a problem of DB query which takes usually several dozens of ms, it is a server response creation itself, so looks like using alternative server cache (e.g. Redis) would not help (meanwhile we use default ABP caching):
[14:46:50 INF] Executed action method AbxEps.CentralTools.Controllers.FreeCodeValues.FreeCodeValueController.GetCumulationTypesAsync (AbxEps.CentralTools.HttpApi), returned result Microsoft.AspNetCore.Mvc.ObjectResult in 2167.4438ms.
[14:46:51 INF] Executed endpoint 'AbxEps.CentralTools.Controllers.FreeCodeValues.FreeCodeValueController.GetCumulationsAsync (AbxEps.CentralTools.HttpApi)' info: Microsoft.EntityFrameworkCore.Infrastructure[10403] Entity Framework Core 5.0.5 initialized 'CoreDbContext' using provider 'Oracle.EntityFrameworkCore' with options: SensitiveDataLoggingEnabled QuerySplittingBehavior=SplitQuery info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (43ms) [Parameters=[:filter_AbxTenantId_0='17' (Nullable = true), :filter_DomainId_1='MD' (Size = 4), :filter_CodeName_2='C_FND_CUM_TYP' (Size = 50), :filter_LanguageCode_3='E' (Size = 4), :defaultLanguageCode_4='E' (Size = 4)], CommandType='Text', CommandTimeout='0'] SELECT c.C_TENANT, c.C_DOMAIN, c.C_FLD, c.C_LANGUAGE, c.C_STR, c.S_COMMENT, c.D_INS, c.C_USR_INS, c.S_TXT, c.U_CA_IMAGE, c.D_UPD, c.C_USR_UPD, c.C_NEOSKEY, c.C_NUM, c.S_TXT_INT FROM CT_CA_CD_POS c WHERE ((((((c.C_TENANT = :filter_AbxTenantId_0) AND (c.C_DOMAIN = :filter_DomainId_1))) AND (c.C_FLD = :filter_CodeName_2))) AND (((c.C_LANGUAGE = :filter_LanguageCode_3) OR (c.C_LANGUAGE = :defaultLanguageCode_4))))
I also see some gaps between numbers which form total response time above and can't figure out what those ~15 seconds are spent for:
[14:46:54 INF] Request finished HTTP/2 GET https://localhost:44328/api/ct/central-tool/free-codes/values/cumulations/en - - - 200 302 application/json;+charset=utf-8 17065.3807ms
Used Oracle DB resides on Azure cloud, but it may not contribute much to the total time either.
How to overcome this issue with TTFB? We use standard ABP layered architecture, nothing fancy.
> [15:44:46 ERR] ---------- RemoteServiceErrorInfo ----------
> {
> "code": null,
> "message": "An internal error occurred during your request!",
> "details": null,
> "data": {
> "ActivatorChain": "AbxEps.CentralTools.Controllers.AbxUserProfile.AbxUserProfileController"
> },
> "validationErrors": null
> }
> [15:44:46 ERR] An exception was thrown while activating AbxEps.CentralTools.Controllers.AbxUserProfile.AbxUserProfileController.
> Autofac.Core.DependencyResolutionException: An exception was thrown while activating AbxEps.CentralTools.Controllers.AbxUserProfile.AbxUserProfileController.
> ---> Autofac.Core.DependencyResolutionException: None of the constructors found with 'Volo.Abp.Autofac.AbpAutofacConstructorFinder' on type 'AbxEps.CentralTools.Controllers.AbxUserProfile.AbxUserProfileController' can be invoked with the available services and parameters:
> Cannot resolve parameter 'AbxEps.CentralTools.AbxUserProfile.IAbxUserProfileService abxUserProfileService' of constructor 'Void .ctor(AbxEps.CentralTools.AbxUserProfile.IAbxUserProfileService)'.
> at Autofac.Core.Activators.Reflection.ReflectionActivator.GetAllBindings(ConstructorBinder[] availableConstructors, IComponentContext context, IEnumerable`1 parameters)
> at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
> at Autofac.Core.Activators.Reflection.ReflectionActivator.<ConfigurePipeline>b__11_0(ResolveRequestContext ctxt, Action`1 next)
> at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
> at Autofac.Core.Resolving.Middleware.DisposalTrackingMiddleware.Execute(ResolveRequestContext context, Action`1 next)
> at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
> at Autofac.Builder.RegistrationBuilder`3.<>c__DisplayClass41_0.<PropertiesAutowired>b__0(ResolveRequestContext ctxt, Action`1 next)
> at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
> at Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action`1 next)
> --- End of inner exception stack trace ---
> at Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action`1 next)
> at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
> at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
> at Autofac.Core.Resolving.Middleware.SharingMiddleware.Execute(ResolveRequestContext context, Action`1 next)
> at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
> at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
> at Autofac.Core.Resolving.Middleware.CircularDependencyDetectorMiddleware.Execute(ResolveRequestContext context, Action`1 next)
> at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
> at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, ResolveRequest request)
> at Autofac.Core.Resolving.ResolveOperation.ExecuteOperation(ResolveRequest request)
> at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
> at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters)
> at Microsoft.AspNetCore.Mvc.Controllers.ServiceBasedControllerActivator.Create(ControllerContext actionContext)
> at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)
> at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
> at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
> --- End of stack trace from previous location ---
> at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
This happens in a very common situation - we have another controllers which work fine. And this one seems to look the same, but for some reason it causes the given exception:
Let's say I have a lot of localizations and edit something at page 6 (UserName):
After editing is done, the grid is reloading to page 1, which is not very user-friendly: I'd prefer to stay at the same page and see the record I've just edited. Or sort the records by modification date in descending order - this way my record will be at the top of the grid and I won't need to look for it.
@maliming
Actually i've managed to achieve this, but using slighly different approach: i've overriden a couple of classes, where filtered existing permissions, but using licence rules:
[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(IPermissionAppService))]
[Authorize]
public class CentralToolsPermissionAppService : PermissionAppService
public class CentralToolsRolePermissionValueProvider : RolePermissionValueProvider
This way I get the filtered roles in the menu and in the Role Permission management. I am not sure we would use Client Permission Management for clients, but then probably I would need to override that provider too.
Possibly I am missing something??
Another approach - to use client-side changes: to override RoutesService
and PermissionGuard
in Angular app - so they would react on licence rules: so menu items will be filtered out and service access will be restricted based on these rules...
Are existing permissions are cached somehow by ABP? BTW, we do not use Redis so far and still not planning too... So if it's about caching - we will use something simple built-in...
You can create some permissions
We have a list of module IDs (each module can be considered as a separate page in navigation menu) and we would prefer not to mix that with "permissions", since it's another conception. And we would like to use it in parallel with requiredPolicy
as shown in screenshot:
ModuleId
needs to be checked in Licence Provider and decide whether the given user has access to the given module ID (page): if not - the page is not displayed in the navigation menu neither user can access data for it.
Can you try the latest? 4.3.2
I've tried 4.3.2 and now Task<MultiplePermissionGrantResult> CheckAsync(PermissionValuesCheckContext context)
is not triggered at all for a logged user: is it correct behavior? When it needs to be triggered??
The angular permission check happens locally, the server will check all the permissions of the current user or user's role etc and return to angular. The menu just checks whether the permission is granted
Yes, I understand - but how to extend "permissions" functionality and use some custom things like our moduleId
in our licence provider?
@maliming thank you, i've tried this, but did not succeed:
Issue #1:
when user is logged in, for some reason context.Permissions
is empty here, so I am getting exception (while a user is not logged in - the context.Permissions
contains all proper data):
public override async Task<MultiplePermissionGrantResult> CheckAsync(PermissionValuesCheckContext context)
{
var moduleId = _abxRequestContext.AbxModuleId;
var permissionNames = context.Permissions.Select(x => x.Name).Distinct().ToArray(); // context.Permissions is empty!
Issue #2:
I need to get access to custom data for my navigation menu items: meanwhile we use custom field - moduleId
- to pass the accessed page information to server (moduleId is written to our custom IAbxRequestContext
object via our middleware and and eventually is logged to DB table and used in other places). This field was also supposed to be used to identify the page to access via licence permission. But of course PermissionValuesCheckContext
does not contain this data from Angular routing item. Neither IAbxRequestContext
object data is available yet. So how to pass data from navigation menu to CheckAsync
method and decide on what to display and what - not to display?
Issue #3:
is there some ABP cache mechanism to be consumed for caching licence data? Otherwise we would need to re-read this data from DB via appservice each time CheckAsync
is called.
p.s. since we seem to have a noticeable timeshift and not able to correspond in real-time - please provide me with as much detailed answer as possible so i could analyze this approach and find out what's wrong now...
We would like to have permission mechanism on top of existing permission architecture.
For instance, current tenant's company have bound licences, each of licence determines which UI pages ("modules") are accessible for current user. Thus, is the logged-in user has no access to the given page - it has to be hidden from UI navigation route - even before checking the given app / role permissions.
How to implement this mechanism?
I've tried to override existing ABP PermissionChecker
class, doing my licence check there in IsGrantedAsync
, but such approach does not look fully right: I am getting error 403 if there is no access, but instead I need to have the menu item completely hidden from my Angular app navigation route. I need backend mechanism which works for both backend and frontend part.
@albert
I checked it but I couldn't see any logic to disable these tabs. The marked tabs are bootstrap dropdown toggles. Maybe this is not working in your browser. Can you try it on a different browser and send me the results?
I have not noticed, but someone in the team redefined the CSS - as a result, a tab was clicked, but its dropdown content was not displayed. Thank you for the reply!