Current status (ABP Studio CLI 9.2.x): There is no supported way to drop a custom ZIP into ~/.abp/templates or to point ABP Studio CLI to a custom template folder. That directory is a managed download cache and is overwritten by clear-download-cache and by template updates. Overriding the built-in template download behavior is not supported.
Recommended, supportable approaches to standardize your organization’s starting point:
Create an Organization Base Solution once, and reuse it
Package your reusable code into modules and NuGet/npm packages
ABP Suite/Studio automation (optional)
ABP CLI vs ABP Studio CLI regarding templates:
Answer to your specific attempts:
Minimal example of an internal module approach
[DependsOn(
typeof(AbpIdentityApplicationModule) // and other dependencies your base module needs
)]
public class MyCompanyBaseApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// Configure organization-wide conventions, object mappers, settings, etc.
}
}
Then in a newly created project:
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
Apply one of the following fixes (prefer the first):
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Volo.Docs.Admin.HttpApi.Client; // for assembly reference
public override void ConfigureServices(ServiceConfigurationContext context)
{
// ... your existing registrations
Configure<MvcOptions>(options =>
{
options.Conventions.Add(new ApiExplorerVisibilityConvention(type =>
type.Assembly == typeof(DocsAdminHttpApiClientModule).Assembly,
isVisible: false));
});
}
// helper convention
public sealed class ApiExplorerVisibilityConvention : Microsoft.AspNetCore.Mvc.ApplicationModels.IApplicationModelConvention
{
private readonly Func<Type, bool> _typePredicate;
private readonly bool _isVisible;
public ApiExplorerVisibilityConvention(Func<Type, bool> typePredicate, bool isVisible)
{
_typePredicate = typePredicate;
_isVisible = isVisible;
}
public void Apply(Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModel application)
{
foreach (var controller in application.Controllers)
{
if (_typePredicate(controller.ControllerType.AsType()))
{
controller.ApiExplorer.IsVisible = _isVisible;
}
}
}
}
Explanation: The Docs HttpApi Client assembly contains typed client proxies (used by your Web app to call the Docs service). They are not real MVC API controllers and shouldn’t participate in ApiExplorer/endpoint discovery. This convention hides them from ApiExplorer to prevent the “ApiExplorer enabled + conventional routing” conflict.
[ApiExplorerSettings(IgnoresApi = true)]
on the controller or action. However, for generated client proxies, Fix A is more robust.
Fix C: Ensure all discovered controllers use attribute routing (not recommended here) Converting affected controllers to attribute routing resolves the mismatch, but since the offending type is a client proxy (not meant to expose HTTP endpoints), hiding it from ApiExplorer is the correct approach.
About MapMetrics and UseConfiguredEndpoints The crash occurs when endpoints are finalized (app.UseConfiguredEndpoints), where MVC builds its action descriptors. The above fix prevents the proxy from being treated as an MVC action, so MapMetrics and endpoint configuration can proceed normally.
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
To use Azure AD/Managed Identity with ABP’s Azure Blob provider based on verified guidance:
If you need AccountName-style configuration, check your installed Volo.Abp.BlobStoring.Azure package’s API surface (e.g., via IntelliSense or source) and release notes. If it’s not present, you must use ConnectionString-based auth per current official docs, or implement a custom blob provider that uses DefaultAzureCredential.
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
Steps:
using Volo.Abp.Modularity;
using Volo.Abp.BlobStoring;
using Volo.Abp.BlobStoring.Azure;
[DependsOn(typeof(AbpBlobStoringAzureModule))]
public class MyProjectDomainModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.ConfigureDefault(container =>
{
container.UseAzure(azure =>
{
// Use AAD/MSI instead of connection string:
azure.AccountName = configuration["Blob:AccountName"]; // e.g. mystorageacct
azure.ContainerName = configuration["Blob:ContainerName"]; // e.g. app-blobs
azure.CreateContainerIfNotExists = true;
// When no ConnectionString is provided, the provider will use Azure SDK credentials.
// With DefaultAzureCredential in App Service, MSI will be used automatically.
// Optionally, you can set an explicit endpoint (useful for national clouds):
// azure.BlobServiceEndpoint = $"https://{azure.AccountName}.blob.core.windows.net";
});
});
});
}
}
App settings (no keys):
"Blob": {
"AccountName": "mystorageacct",
"ContainerName": "app-blobs"
}
Assign RBAC to the Managed Identity of your App Service
Ensure the Azure SDK uses Managed Identity at runtime
Verify locally with developer credentials (optional)
Using named containers (optional)
[BlobContainerName("profile-pictures")]
public class ProfilePictureContainer {}
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.Configure<ProfilePictureContainer>(container =>
{
container.UseAzure(azure =>
{
azure.AccountName = configuration["Blob:AccountName"];
azure.ContainerName = "profile-pictures";
azure.CreateContainerIfNotExists = true;
});
});
});
Notes:
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
Root cause
What to verify in your app
var book = await _bookRepository.GetAsync(id);
book.AuthorId = input.AuthorId; // set the FK scalar
// or set book.Author = await _authorRepository.GetAsync(input.AuthorId);
await _bookRepository.UpdateAsync(book, autoSave: true);
var claims = new List<Claim> { new Claim(AbpClaimTypes.UserId, userId.ToString()) };
using (_currentPrincipalAccessor.Change(claims))
{
await _bookRepository.UpdateAsync(book, autoSave: true);
}
ABP 8.3+ behavior (applies to 9.2.3)
Minimal working example
public class BookAppService : ApplicationService
{
private readonly IRepository<Book, long> _bookRepository;
public BookAppService(IRepository<Book, long> bookRepository)
{
_bookRepository = bookRepository;
}
public async Task UpdateAuthorAsync(long id, long? newAuthorId)
{
var book = await _bookRepository.GetAsync(id); // tracked
book.AuthorId = newAuthorId;
await _bookRepository.UpdateAsync(book, autoSave: true); // triggers ApplyAbpConcepts
// After save, LastModificationTime and LastModifierId should be updated (if CurrentUser.Id exists)
}
}
var book = await _bookRepository.GetAsync(id);
_ = ObjectMapper.Map(input, book); // ensure AuthorId ends up different
await _bookRepository.UpdateAsync(book, autoSave: true);
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
Root cause
Fix (recommended)
Reduce auth cookie size
Use tokens that don’t bloat headers
IIS-specific mitigations (if you still need more room)
Verify no unnecessary cookies are sent
Minimal ABP-side checklist
public class MyClaimsContributor : IAbpClaimsPrincipalContributor
{
public Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
{
var identity = context.ClaimsPrincipal.Identities.First();
// Remove or avoid adding large/duplicate claims here
return Task.CompletedTask;
}
}
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
Why this happens
Fixes (pick one or combine)
Reduce the amount of data stored in the cookie
Switch to reference tokens (prevent large cookies to the frontend)
Increase header limits on your front-end/reverse proxy if absolutely necessary
Ensure antiforgery/cookie settings don’t add extra cookies unnecessarily
Practical steps to reduce cookie size in ABP
IIS-specific mitigation (optional)
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
<div id="lpx-wrapper" class="lpx-layout lpx-layout--side">
<MainHeader />
<div class="lpx-container">
<SideMenu />
<div class="lpx-content-container">
<CustomSubmenu />
<div class="lpx-topbar-container">
<div class="lpx-topbar"><Breadcrumbs /></div>
</div>
<div class="lpx-content-wrapper">
<div class="lpx-content">
<CustomPageActions />
@Body
</div>
</div>
<footer><Footer /></footer>
</div>
</div>
<MobileNavbar />
</div>
No estás usando los componentes de layout del tema
El layout registrado no es el de LeptonX o se reemplazó completamente
Configure<LeptonXThemeBlazorOptions>(options =>
{
options.Layout = typeof(Volo.Abp.AspNetCore.Components.Web.LeptonXTheme.Components
.ApplicationLayout.SideMenu.SideMenuLayout);
// o tu CustomApplicationLayout, pero conservando estructura + clases LeptonX
});
Falta del bundle de estilos del tema (Blazor WASM/WebApp con Global Assets)
CSS aislado que sobreescribe variables/clases del tema
Mezcla de namespaces de TopMenu y SideMenu
¿Debo agregar un CSS al componente?
Checklist rápido:
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
What to request
How to submit the request
Interim workaround (minimal replacement)
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
A) Use the theme’s replaceable component hook to customize the layout header/toolbar area where the collapse icon lives
Example registration (in your ThemeModule or a shared module initialized at app bootstrap):
import { NgModule } from '@angular/core';
import { ReplaceableComponentsService } from '@abp/ng.core';
import { eThemeLeptonXComponents } from '@abp/ng.theme.lepton-x'; // adjust import to your version
import { AccessibleSidebarToggleComponent } from './accessible-sidebar-toggle.component';
@NgModule({
declarations: [AccessibleSidebarToggleComponent],
exports: [AccessibleSidebarToggleComponent]
})
export class ThemeCustomizationModule {
constructor(rc: ReplaceableComponentsService) {
rc.add({
key: eThemeLeptonXComponents.Navbar, // or the exact header/toolbar key used by your layout
component: AccessibleSidebarToggleComponent,
});
}
}
B) Implement the toggle component by binding to LeptonX/Theme Shared layout state (do not re-implement state)
Example component:
import { Component, HostListener, ChangeDetectionStrategy } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
// Layout service location may differ by version; it’s typically in @abp/ng.theme.shared or @abp/ng.theme.lepton-x
import { LayoutService } from '@abp/ng.theme.shared'; // adjust import path to your version
@Component({
selector: 'app-accessible-sidebar-toggle',
template: `
<button
type="button"
class="menu-collapse-icon"
[attr.aria-label]="(isCollapsed$ | async) ? 'Expand navigation' : 'Collapse navigation'"
[attr.aria-expanded]="!(isCollapsed$ | async)"
(click)="toggleSidebar()"
(keydown)="onKeydown($event)"
>
<i class="bi" [ngClass]="(isCollapsed$ | async) ? 'bi-filter-right' : 'bi-filter-left'"></i>
</button>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AccessibleSidebarToggleComponent {
// Expose collapsed state from the theme’s layout service
isCollapsed$: Observable<boolean> = this.layout.state$.pipe(map(s => !!s?.sidebarCollapsed));
constructor(private layout: LayoutService) {}
toggleSidebar(): void {
const cur = this.layout.getState()?.sidebarCollapsed ?? false;
this.layout.setState({ sidebarCollapsed: !cur });
}
onKeydown(e: KeyboardEvent): void {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
this.toggleSidebar();
}
}
// Optional: expand on focus to mirror hover
@HostListener('focusin')
onFocusIn(): void {
const cur = this.layout.getState()?.sidebarCollapsed ?? false;
if (cur) {
this.layout.setState({ sidebarCollapsed: false });
}
}
}
Notes:
C) Keep hover-trigger behavior
D) Where to place the toggle
E) Persisting user choice (optional)
// On app init
const saved = localStorage.getItem('sidebarCollapsed') === 'true';
layout.setState({ sidebarCollapsed: saved });
// When state changes
layout.state$.subscribe(s => localStorage.setItem('sidebarCollapsed', String(!!s?.sidebarCollapsed)));
F) Minimal template-only alternative
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.