abp update
(run in the solution root)
Sources
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.
In ABP’s YARP-based WebGateway, you typically don’t need that app.MapWhen(...) block anymore.
MapWhen was used to short-circuit some “local” endpoints (like /api/abp/api-definition and /) so they are handled by the gateway app itself via UseConfiguredEndpoints() instead of being forwarded by Ocelot.What you must ensure after removing MapWhen:
/ (root) is not being proxied by a YARP route./{**catch-all} (or something too broad), it could start proxying the gateway’s home page and Swagger UI, which would break what the MapWhen previously protected./api/abp/api-definition behavior:
/api/abp/{**catch-all} to that service (typical AbpApi route)./api/abp/api-definition to be proxied (not handled locally). If your YARP config has the standard ABP route like /api/abp/{**catch-all}, you’re fine.Your new pipeline line app.UseEndpoints(endpoints => endpoints.MapReverseProxy()); is the essential part for YARP. (In minimal hosting, you may also see app.MapReverseProxy(); ABP templates commonly use the UseEndpoints form.)
If your gateway works and:
/swagger (or whatever path you host Swagger UI on) renders locally, and/api/abp/api-definition returns the expected response (likely coming from the proxied service),
then you didn’t miss anything from that MapWhen block—its job has effectively moved to “don’t proxy what should stay local” via the YARP route configuration.Links (validated):
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.
Pages/Account/ResetPassword.cshtml<a asp-page="./ForgotPassword" class="btn btn-link px-0">
@L["ForgotPassword"]
</a>
That’s all—since you’re only adding a link, the existing reset-password flow remains unchanged.
Configure<AppUrlOptions>(options =>
{
options.Applications["Angular"].RootUrl = configuration["App:AngularUrl"];
options.Applications["Angular"].Urls[AccountUrlNames.PasswordReset] = "account/reset-password";
options.Applications["Angular"].Urls[AccountUrlNames.EmailConfirmation] = "account/email-confirmation";
});
Links (checked):
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.
Store LDAP settings per tenant
ISettingManager / ISettingProvider), which already supports tenant-scoped values.Resolve LDAP configuration using ICurrentTenant at runtime
ISettingProvider (or ISettingManager) so the current tenant context automatically selects the correct values.Replace/extend the LDAP authentication implementation
[Dependency(ReplaceServices = true)]) and implement tenant-aware resolution logic.Keep the standard ABP login flow (Account module) and plug LDAP in as an authentication option.
Typical pattern:
Enable/disable per tenant:
ICurrentTenant):
// 1) Define tenant-scoped settings (server, baseDn, bindDn, bindPassword, etc.)
public static class TenantLdapSettings
{
private const string Prefix = "TenantLdap.";
public const string Server = Prefix + "Server";
public const string Port = Prefix + "Port";
public const string BaseDn = Prefix + "BaseDn";
public const string BindDn = Prefix + "BindDn";
public const string BindPassword = Prefix + "BindPassword";
public const string UseSsl = Prefix + "UseSsl";
}
public class TenantLdapSettingDefinitionProvider : SettingDefinitionProvider
{
public override void Define(ISettingDefinitionContext context)
{
context.Add(
new SettingDefinition(TenantLdapSettings.Server),
new SettingDefinition(TenantLdapSettings.Port, "389"),
new SettingDefinition(TenantLdapSettings.BaseDn),
new SettingDefinition(TenantLdapSettings.BindDn),
new SettingDefinition(TenantLdapSettings.BindPassword, isEncrypted: true),
new SettingDefinition(TenantLdapSettings.UseSsl, "false")
);
}
}
// 2) Implement a tenant-aware LDAP authentication service (pseudo-outline)
// Replace the service that the login flow uses to validate credentials.
[Dependency(ReplaceServices = true)]
public class TenantAwareLdapAuthenticator /* : SomeAbpLdapService */
{
private readonly ISettingProvider _settingProvider;
private readonly ICurrentTenant _currentTenant;
public TenantAwareLdapAuthenticator(ISettingProvider settingProvider, ICurrentTenant currentTenant)
{
_settingProvider = settingProvider;
_currentTenant = currentTenant;
}
public async Task<bool> ValidateAsync(string userNameOrEmail, string password)
{
// Read settings for CURRENT tenant (automatic due to ICurrentTenant)
var server = await _settingProvider.GetOrNullAsync(TenantLdapSettings.Server);
if (server.IsNullOrWhiteSpace())
{
return false; // tenant not configured => treat as disabled/fallback
}
// read other settings...
// connect/bind to LDAP and validate credentials...
return true;
}
}
This is the ABP-supported pattern: tenant-scoped settings + service replacement. The only part you must align in your solution is which exact service/class to replace in the ABP LDAP login integration you’re using (that depends on whether you use the built-in LDAP external provider as-is or a custom login path). The “dynamic provider per tenant” support answer shows the same technique in a verified way.
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.
This behavior (content visible only after login) is almost always caused by production-only authentication/authorization requirements being applied to the Public Website, not by CMS Kit Pro itself. CMS Kit menus/pages are designed to be publicly renderable; if they disappear for anonymous users, something in the pipeline is treating anonymous requests as unauthorized (and the UI ends up showing nothing / redirects).
Apply the following production checks/fixes (these are safe and common causes in multi-tenant + public website deployments):
ConfigureServices / OnApplicationInitialization) such as:
options.FallbackPolicy = options.DefaultPolicy;RequireAuthenticatedUser() applied globallyGlobalFeatureConfigurator (and the same code is deployed to production). Otherwise some UI pieces can disappear depending on feature flags.appsettings.Production.json values for auth-related endpoints and the public web’s self URL.Links (verified)
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.
The log you shared shows ABP Studio backend throwing a NullReferenceException inside the Studio connectivity heartbeat pipeline (IStudioHub/HeartbeatAsync → Volo.Abp.Studio.Connectivity.StudioBackendHub.NotifyHeartbeatAsync). That’s an ABP Studio bug/regression in newer Studio versions (not related to your ABP solution code), and it explains why it “sometimes hangs / crashes while loading projects” and “sometimes crashes when starting containers”.
Since this is a Studio internal exception (and you already confirmed 2.1.4 works while later versions don’t), the only definitive “fix” you can apply locally is:
What to send (so they can reproduce/fix quickly):
~/.abp/studio/ui/logs~/.abp/studio/backend/logs (or any backend/logs under ~/.abp/studio/).abpsln (ABP Studio solution definition) file for that microservice solution (if you can share it), and the approximate project count (you said ~36).Where to report (official channel):
(Links are verified reachable.)
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.
SessionStateService. ABP’s theme language switch uses SessionStateService to get/set the current language, so observing that state is the correct “language changed” signal.import { Injectable } from '@angular/core';
import { SessionStateService } from '@abp/ng.core';
import { distinctUntilChanged, map } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class LanguageChangeListenerService {
constructor(private sessionState: SessionStateService) {}
// Emits whenever language changes (e.g. user picks a language from theme UI)
language$ = this.sessionState.getLanguage$().pipe(
distinctUntilChanged()
);
// Example: call some action on change
init() {
this.language$.subscribe(lang => {
// do your action here
// e.g., refresh data, rebuild menus, change moment locale, etc.
console.log('Language changed:', lang);
});
}
}
Then call LanguageChangeListenerService.init() once at app startup (e.g., in AppComponent or an APP_INITIALIZER).
GET /api/abp/application-configuration?includeLocalizationResources=false is called:
HttpInterceptor and watch for that URL.import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable, tap } from 'rxjs';
@Injectable()
export class AppConfigCallInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
tap(() => {
if (req.url.includes('/api/abp/application-configuration')) {
// You can also check req.params for includeLocalizationResources
console.log('ABP application-configuration called:', req.urlWithParams);
}
})
);
}
}
Register it in your module providers:
import { HTTP_INTERCEPTORS } from '@angular/common/http';
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AppConfigCallInterceptor, multi: true },
]
What service is responsible for calling that API?
SessionStateService.getLanguage$() is the supported way to react to language changes, and an HttpInterceptor is the reliable way to detect calls to /api/abp/application-configuration.https://abp.io/docs/10.1/framework/ui/angular/theming
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.
FileDescriptor + blob content can be created before the main entity is saved. If the user closes the modal / refreshes, those uploaded blobs can remain orphaned unless you add a cleanup strategy.Implement the create/edit form so it does not call the server on file selection. Instead:
CreateAsync(...) and pass the file content as IRemoteStreamContent (or a byte[] if small), then inside the same application service method:
This matches ABP’s general “do it in the application service in a single use-case” style and avoids a “temporary upload” concept entirely.
If you keep the default immediate upload, use a “temporary upload” + scheduled cleanup pattern:
ABP’s Blob Storing API supports deleting blobs, so the cleanup job can safely remove old temporary items.
In the modal’s Cancel/Close handler (and on dispose/navigation), call an endpoint to delete the already-uploaded file descriptor/blob if it hasn’t been finalized. This reduces leftovers but won’t cover hard refresh/tab close/network loss—so you still typically want (B) as the safety net.
Use ABP Blob Storing’s container API to remove orphaned blobs when needed (e.g., in a background worker or explicit cancel endpoint):
public class MyFileCleanupService : ITransientDependency
{
private readonly IBlobContainer<MyFileContainer> _container;
public MyFileCleanupService(IBlobContainer<MyFileContainer> container)
{
_container = container;
}
public Task DeleteAsync(string blobName)
{
return _container.DeleteAsync(blobName);
}
}
You’d combine this with whatever “temporary” tracking you use (DB table/flag, naming convention, etc.) to find candidates to delete.
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.
Yes, you still need the YARP registration call (whatever your template/module uses, e.g. services.AddYarpReverseProxy() / AddReverseProxy() etc.).
Moving yarp.json content into appsettings.json only changes the configuration source; it does not register YARP services or map the reverse proxy endpoint. If you remove the YARP registration, the gateway won’t actually behave as a reverse proxy (and ABP’s YARP-based swagger aggregation logic won’t behave as expected either).
In the ABP YARP migration guide, the expected change is to change the config loader from Ocelot to YARP:
builder.Host
.AddAppSettingsSecretsJson()
- .AddOcelotJson()
+ .AddYarpJson()
.UseAutofac()
.UseSerilog();
That AddYarpJson() is about loading yarp.json (a separate file). If you merged the content into appsettings.json, you typically don’t need AddYarpJson() anymore, because appsettings.json is already loaded by default. But you still need the YARP service registration and MapReverseProxy().
Why your Swagger suddenly shows “all APIs”:
IProxyConfigProvider and calling options.SwaggerEndpoint(...) for each cluster (see the migration guide’s ConfigureSwaggerUI(proxyConfig, ...) sample). That means if your effective YARP config now contains more clusters than before (because of config merging from multiple sources, or you accidentally broadened routes/clusters), Swagger UI will list more services/APIs.AddAbpSwaggerGenWithOAuth to AddAbpSwaggerGenWithOidc generally affects authentication method in Swagger UI, not which endpoints are included. The “shows all APIs” symptom is much more consistent with the gateway’s swagger endpoints being generated for more clusters (or the proxy now allowing access to more swagger-json endpoints).Definitive setup guidance when ReverseProxy is inside appsettings.json:
ReverseProxy section in appsettings.json.AddYarpJson() (optional) if you deleted yarp.json and don’t want that custom provider anymore.endpoints.MapReverseProxy() (as shown in the migration guide).References (validated):
AddYarpJson(), IProxyConfigProvider-based Swagger endpoints, and MapReverseProxy(): https://abp.io/docs/latest/release-info/migration-guides/pro/migrating-to-yarpappsettings.json / ReverseProxy): https://abp.io/docs/latest/solution-templates/microservice/adding-new-api-gatewaysThis 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.
You didn’t miss anything: in ABP’s microservice gateway setup, Gateway Swagger UI is driven by the gateway’s own Swagger configuration, which (by design) typically points to the swagger-json endpoints that you route through YARP. So what the gateway swagger can “see” depends on what you proxy.
With YARP, ABP’s recommended approach is to define reverse proxy config under the ReverseProxy section (routes/clusters). This can live in appsettings.json (or any configuration source). ABP docs explicitly show configuring YARP via appsettings.json, so moving from yarp.json → appsettings.json is fine as long as the structure is the same.
What to do (definitive):
/swagger-json/Administration/swagger/v1/swagger.json/swagger-json/ProductService/swagger/v1/swagger.json
(each with a PathRemovePrefix transform)
If you add a too-broad route (for example routing /swagger/{**catch-all} or a very generic /{**catch-all}), the gateway swagger UI (or the browser) may be able to resolve more swagger docs than you expect.appsettings.json, appsettings.{Environment}.json, environment variables, configmaps, etc. can merge. If yarp.json is still being loaded somehow (custom code or host builder), you may effectively have more proxy routes/clusters than you think.HideAbpEndpoints() in your swagger setup.Putting YARP config in appsettings.json is supported; the key is keeping your ReverseProxy routes tightly scoped so the gateway swagger can only load the intended swagger documents.
Links (verified):
appsettings.json (ReverseProxy section): https://abp.io/docs/latest/solution-templates/microservice/adding-new-api-gatewaysHideAbpEndpoints): https://abp.io/docs/latest/API/Swagger-Integration#hide-abp-endpoints-on-swagger-uiThis 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.