/HRBack but some requests are still going to the site root (missing /HRBack), so IIS returns 404/400 and ABP shows “Error detail not sent by the server.”From your screenshot, the browser is calling an error page under:
/HRBack/Error?httpStatusCode=400
That typically happens when the API call URL is wrong (PathBase missing) or the server rejects the request and ABP redirects to the Error page in production.
In your Angular environment.prod.ts (or the environment you deploy), ensure all URLs include /HRBack where appropriate:
export const environment = {
production: true,
application: {
baseUrl: 'https://eform/HR/', // Angular site base
// If Angular itself is under /HR, keep it. Otherwise adjust.
},
oAuthConfig: {
issuer: 'https://eform/HRBack', // AuthServer/Authority (MUST match exactly)
clientId: 'TNAPLanner_App', // your angular client id
scope: 'offline_access ...', // keep your existing scopes
},
apis: {
default: {
url: 'https://eform/HRBack', // IMPORTANT: API root including /HRBack
rootNamespace: '...', // keep existing
},
},
};
After deploying, open DevTools → Network and verify that save (POST/PUT) requests go to:
https://eform/HRBack/api/...
and not:https://eform/api/.../HRBackOn HttpApi.Host side you already have it, but verify production config is deployed:
"App": {
"SelfUrl": "https://eform/HRBack",
"PathBase": "/HRBack"
},
"AuthServer": {
"Authority": "https://eform/HRBack",
"RequireHttpsMetadata": true
}
If GET works but POST/PUT/DELETE fail or return 400 under IIS, remove/disable WebDAV for that site (or server) because it can block verbs.
This is a common IIS deployment step and ABP’s IIS deployment doc covers IIS-side requirements.
.../Error?httpStatusCode=400/api/abp/application-configuration from the roothttps://abp.io/docs/latest/solution-templates/layered-web-application/deployment/deployment-iis
https://abp.io/docs/latest/framework/api-development/swagger
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.
https://eform/HRBack/swagger/v1/swagger.json in the browser), so the problem is not Swagger generation.GET https://eform/api/abp/application-configuration 404 (Not Found) (from abp.swagger.js)
That means Swagger UI is trying to call ABP’s application-configuration endpoint from the site root (/api/...) instead of from your virtual directory (/HRBack/api/...). So ABP’s swagger helper script doesn’t know your PathBase, and Swagger UI fails even though the swagger.json itself is OK.
In your *.HttpApi.Host OnApplicationInitialization, make sure UsePathBase("/HRBack") (or ABP’s equivalent configuration) is executed early (before UseStaticFiles, UseSwaggerUI, etc.).
Example:
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
var configuration = context.GetConfiguration();
var pathBase = configuration["App:PathBase"];
if (!pathBase.IsNullOrWhiteSpace())
{
app.UsePathBase(pathBase);
}
app.UseCorrelationId();
app.UseStaticFiles();
app.UseRouting();
app.UseSwagger();
app.UseAbpSwaggerUI(options =>
{
options.SwaggerEndpoint($"{pathBase}/swagger/v1/swagger.json", "API");
});
app.UseConfiguredEndpoints();
}
ABP injects /swagger/ui/abp.swagger.js and that script calls abp/appPath-dependent endpoints like api/abp/application-configuration.
If abp.appPath is not set correctly, it defaults to / and you get exactly your 404.
To force the correct base path for swagger UI under /HRBack, add this right before UseAbpSwaggerUI:
app.Use((ctx, next) =>
{
// Force ABP appPath for swagger UI under a virtual directory
if (ctx.Request.Path.StartsWithSegments("/swagger"))
{
ctx.Items["__abp_app_path"] = configuration["App:PathBase"]?.EnsureEndsWith('/');
}
return next();
});
If you don’t want middleware tricks, the practical workaround is:
UseSwaggerUI(...) without ABP additions, OR(Your specific symptom is produced by ABP’s injected script calling /api/abp/application-configuration.)
Open browser devtools Network tab and refresh /HRBack/swagger/index.html:
GET https://eform/HRBack/api/abp/application-configuration should return 200https://eform/api/abp/application-configurationIf you still see calls going to /api/... (without /HRBack), the PathBase is not being applied to swagger UI runtime variables.
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.
@PageLayout.Content.Title) only when the language switch is shown (i.e., when languageInfo.Languages.Count > 1). So, with a single enabled language, the title disappears exactly as you observed.What you should do (until it’s fixed upstream)
Themes/LeptonX/Layouts/Account/Default.cshtml@PageLayout.Content.Title out of the if (hasLanguageSwitch) block.This is the intended/standard way to patch theme layout issues without modifying NuGet package files directly.
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.
https://eform/HRBack), Swagger UI must request the swagger json with the same PathBase. If Swagger UI is configured with an absolute path like /swagger/v1/swagger.json, it will try to load it from the web site root, not from /HRBack, and you’ll see “Failed to load API definition”.App:PathBase as you already have:"App": {
"PathBase": "/HRBack",
"SelfUrl": "https://eform/HRBack"
}
abp.appPath (PathBase-aware) when setting the endpoint.In your *.HttpApi.Host module (typically OnApplicationInitialization), ensure it’s like this:
app.UseSwagger();
app.UseAbpSwaggerUI(options =>
{
options.SwaggerEndpoint($"{app.ApplicationServices.GetRequiredService<IConfiguration>()["App:PathBase"]}/swagger/v1/swagger.json", "API");
});
Better / cleaner (doesn’t rely on config string concatenation) is to use ABP’s abp.appPath on the UI side, but in Swashbuckle options you typically hardcode. The key point is: the endpoint must start with /HRBack (your PathBase), not /swagger/....
So for your case it must be:
/HRBack/swagger/v1/swagger.json
Your server log shows GET https://eform/HRBack/swagger/v1/swagger.json returns 200. Swagger UI’s error often appears when the UI is actually trying a different URL (commonly /swagger/v1/swagger.json without /HRBack) or when the browser blocks it due to mixed origin / redirect. Ensuring the SwaggerEndpoint includes the PathBase resolves the common IIS-virtual-directory case.
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.
Upgrade incrementally (recommended for 20+ solutions): 8.3.4 → latest 9.x → 10.0.1.
Upgrade packages using ABP Studio (GUI) or ABP CLI (scriptable):
dotnet tool update -g Volo.Abp.Studio.Cli
abp update
This updates ABP-related **NuGet** (and if applicable **NPM**) packages in the solution.
After upgrading packages, build and run, then follow the migration guide steps for any breaking changes you hit (for 10.0, start here):
For many solutions, automate it:
abp update in a loop for each repository/solution root (CI script or local script), then run dotnet build / tests.Links used (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.
If each React app shows its own username/password form and calls POST /connect/token with grant_type=password (ROPC), you won’t get SSO.
Reason: /connect/token returns tokens but does not create/consume a shared browser login session (no central Auth Server cookie), so App2 has nothing to “reuse” from App1.
To have SSO, both SPAs must authenticate via the authorization endpoint (Authorization Code + PKCE) against the same Auth Server, so the Auth Server can maintain a login session cookie and silently authenticate the second app.
If you insist on keeping a “dedicated form” UX, the only way to still get SSO-like behavior is to still redirect/popup to the Auth Server (so the cookie-based session exists), even if you visually keep the user in the SPA (popup). Pure ROPC from two separate forms will always require credentials again.
(Reference for ABP using OpenIddict as the auth server component is here; and an example article shows authorization_code as the recommended grant compared to password):
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, it’s still possible if “login in place” actually means you authenticate against the same ABP Auth Server using OpenID Connect (typically by opening the Auth Server login UI in a redirect or a popup). That still produces SSO because the Auth Server session cookie is what enables the second app to log in without prompting again.
If by “login in place” you mean each React app has its own local username/password form and calls its own API to authenticate (no shared Auth Server / OIDC flow), then no, you won’t get SSO automatically—because there is no shared central login session.
authority/issuer).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.
Register both React apps as OIDC clients in the Auth Server (two clientIds):
App1_Client with redirect URI(s) for App1 (e.g. https://app1.example.com/auth-callback)App2_Client with redirect URI(s) for App2 (e.g. https://app2.example.com/auth-callback)In each React app, configure OIDC Authorization Code Flow (PKCE) pointing to the same authority/issuer:
authority/issuer: https://auth.example.comclient_id: its own client id (App1_Client / App2_Client)redirect_uri: app-specificopenid profile email + your API scope(s)When the user navigates from App1 to App2:
Ensure both APIs validate tokens from the same authority/issuer (standard ABP setup).
auth.example.com) and the browser accepts it.SameSite settings and HTTPSThis 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.
Create the shared entities/tables without IMultiTenant (no TenantId column). These are naturally readable by everyone because they are not tenant-filtered.
For tables that need both tenant-specific rows and global rows, implement IMultiTenant and store shared rows with TenantId = null.
When querying from the tenant side, explicitly include both:
TenantId == CurrentTenantIdTenantId == null (global/shared)Example (repository or EF Core query):
var tenantId = CurrentTenant.Id;
var items = await _repo.GetQueryableAsync();
var result = await items
.Where(x => x.TenantId == tenantId || x.TenantId == null)
.ToListAsync();
Notes:
- ABP’s automatic multi-tenancy filter for `IMultiTenant` typically returns only the current tenant’s rows. So for this “tenant + global” scenario, you should **control the predicate yourself** (as above), or use a separate read model/service dedicated to this query pattern.
This requirement violates strict multi-tenancy isolation (ABP team explicitly calls out that if tenants must see other tenants’ data, it’s not a pure multi-tenant model).
Practical approach in ABP is to model “sharing” explicitly, e.g.:
TenantId or TenantId = null)EntityTenantShare(EntityId, TenantId) that defines which tenants can see itConceptual example:
var tenantId = CurrentTenant.Id;
var query = from e in await _entityRepo.GetQueryableAsync()
join s in await _shareRepo.GetQueryableAsync() on e.Id equals s.EntityId into shares
from share in shares.DefaultIfEmpty()
where e.TenantId == tenantId
|| (e.TenantId == null && share.TenantId == tenantId)
select e;
If you find many tables needing this, consider whether you actually want multi-tenancy in ABP terms, or a “single-tenant app with a Company/Group filter” using ABP Data Filtering (custom filters).
When you need “not all tables”
IMultiTenant are tenant-filtered. Others are shared.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.
[Mapper]
public partial class CoreBookToBookDtoMapper : MapperBase<Book, BookDto>
{
public override partial BookDto Map(Book source);
public override partial void Map(Book source, BookDto destination);
}
dotnet build does. That’s why runtime/build is fine, but the IDE shows errors.Fix (practical, ABP-safe)
dotnet cleandotnet buildbin/ and obj/ folders for the project(s) containing the mappers, then rebuild.public override partial ... (don’t convert them to non-partial, and don’t remove override), because ABP’s MapperBase<TSource,TDestination> is designed for this pattern.If you follow the steps above, the IDE should stop reporting CS8795 while keeping the correct ABP + Mapperly integration.
Sources (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.