Activities of "AI-Bot"

  • Your Swagger issue and your new Angular “save” issue have the same root cause: the app is hosted under /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.

Solution

1) Fix Angular environment to include the PathBase for API + OAuth issuer

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/...

2) Ensure backend knows it is running under /HRBack

On 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
}

3) If only POST/PUT fail on IIS: disable WebDAV module (classic IIS issue)

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.

What you should see after the fix

  • Angular “save” requests return 200/204 (or validation errors with JSON), not .../Error?httpStatusCode=400
  • Swagger UI no longer requests /api/abp/application-configuration from the root

https://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.

  • Your Swagger JSON is reachable (you can open https://eform/HRBack/swagger/v1/swagger.json in the browser), so the problem is not Swagger generation.
  • The browser console error in your screenshot is the key:

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.

Solution

1) Ensure your PathBase is applied BEFORE ABP middleware

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();
}

2) Fix the swagger UI “ABP script” base path (most important)

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:

  • Disable ABP’s swagger UI injected script and use plain UseSwaggerUI(...) without ABP additions, OR
  • Serve swagger at root instead of under an IIS Application.

(Your specific symptom is produced by ABP’s injected script calling /api/abp/application-configuration.)

3) Quick verification (what should happen after fix)

Open browser devtools Network tab and refresh /HRBack/swagger/index.html:

  • GET https://eform/HRBack/api/abp/application-configuration should return 200
  • No request should go to https://eform/api/abp/application-configuration

If you still see calls going to /api/... (without /HRBack), the PathBase is not being applied to swagger UI runtime variables.


  • 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.

  • This is a theme layout bug in the MVC Account layout of LeptonX (your screenshots match it). In ABP v10.0.2, the layout renders the login “header row” (including @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.
  • Your fix is correct: render the title unconditionally and only conditionally render the language dropdown.

What you should do (until it’s fixed upstream)

  1. Override the LeptonX account layout in your host project:
    • Create: Themes/LeptonX/Layouts/Account/Default.cshtml
    • Copy the original layout content into it (from the theme package source) and apply your change: move @PageLayout.Content.Title out of the if (hasLanguageSwitch) block.
  2. Keep the conditional only around the dropdown.

This is the intended/standard way to patch theme layout issues without modifying NuGet package files directly.

  • https://abp.io/docs/latest/framework/ui/mvc-razor-pages/basic-theme (reachable)
  • https://abp.io/docs/latest/ui/aspnetcore/customization-user-interface#overriding-a-razor-page-cshtml (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.

  • In ABP (v9), when you host the HttpApi.Host under an IIS Application / virtual directory (e.g. 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”.

Fix (recommended)

  1. Keep your App:PathBase as you already have:
"App": {
  "PathBase": "/HRBack",
  "SelfUrl": "https://eform/HRBack"
}
  1. Update your Swagger UI configuration to use 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
  1. Make sure your IIS application is configured as an Application (not just a folder) and uses the correct app pool (.NET CLR “No Managed Code” for ASP.NET Core hosting model).

Why your log looks “OK but UI fails”

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.


  • https://abp.io/docs/latest/framework/api-development/swagger
  • https://abp.io/community/articles/mb2w01fe

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.

    • Apply the related migration guides as you move between major/minor versions (especially 9.x → 10.0).
  • Upgrade packages using ABP Studio (GUI) or ABP CLI (scriptable):

    1. Install/upgrade the ABP CLI:
dotnet tool update -g Volo.Abp.Studio.Cli
  1. In each solution root folder, run:
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):

    • ABP 10.0 migration guide describes required code/config changes when upgrading from 9.x.
  • For many solutions, automate it:

    • Run abp update in a loop for each repository/solution root (CI script or local script), then run dotnet build / tests.

Links used (validated):

  • https://abp.io/docs/latest/CLI#update
  • https://abp.io/docs/latest/studio/installation#upgrading
  • https://abp.io/docs/10.0/release-info/migration-guides/abp-10-0
  • https://abp.io/docs/latest/release-info/migration-guides/abp-9-2
  • https://abp.io/docs/latest/release-info/migration-guides/abp-9-3

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):

  • https://abp.io/docs/latest/solution-templates/layered-web-application/authentication
  • https://abp.io/community/articles/aqjjwsdf

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.

What works for “in-place” UX while keeping SSO

  1. Use the same Auth Server for both apps (same authority/issuer).
  2. In App2, start OIDC login via:
    • popup flow (user stays on the same page visually), or
    • silent sign-in (check session) where possible.
  3. App2 will still go to the Auth Server under the hood; if the user is already logged in from App1, Auth Server will immediately issue tokens (SSO).

Sources

  • https://abp.io/docs/latest/others/why-abp-platform
  • https://abp.io/community/articles/adzzfnji

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—SSO between two React SPAs is possible as long as both apps are configured as separate OpenID Connect clients on the same ABP Auth Server (OpenIddict/IdentityServer).
  • You don’t “share the token” from App1 to App2. Instead, App2 redirects to the same Auth Server, and because the user already has an Auth Server login session (cookie), Auth Server will issue tokens for App2 without asking credentials again (that’s the SSO).

Step-by-step

  1. 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)
    • Add proper CORS origins for both apps.
  2. In each React app, configure OIDC Authorization Code Flow (PKCE) pointing to the same authority/issuer:

    • authority/issuer: https://auth.example.com
    • client_id: its own client id (App1_Client / App2_Client)
    • redirect_uri: app-specific
    • scopes: include openid profile email + your API scope(s)
  3. When the user navigates from App1 to App2:

    • App2 triggers login (OIDC redirect)
    • Auth Server sees the existing login session cookie
    • Auth Server returns authorization code → App2 exchanges for tokens
    • Result: user is logged in to App2 without re-entering credentials (SSO)
  4. Ensure both APIs validate tokens from the same authority/issuer (standard ABP setup).

Notes (common pitfalls)

  • Different domains are fine (SSO still works) if the Auth Server cookie is valid for its own domain (e.g. auth.example.com) and the browser accepts it.
  • If you open App2 in a new tab and it still asks for login, check:
    • third-party cookie restrictions / “block cross-site tracking” browser settings
    • Auth Server cookie SameSite settings and HTTPS

Sources

  • https://abp.io/docs/latest/others/why-abp-platform
  • https://abp.io/community/articles/adzzfnji

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.

  • Option A (Recommended): Model “common to all tenants” as Host data
    1. Create the shared entities/tables without IMultiTenant (no TenantId column). These are naturally readable by everyone because they are not tenant-filtered.

    2. For tables that need both tenant-specific rows and global rows, implement IMultiTenant and store shared rows with TenantId = null.

    3. When querying from the tenant side, explicitly include both:

      • rows with TenantId == CurrentTenantId
      • rows with TenantId == 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.
  • Option B (More complex): “Shared across some tenants (not all)”
    • 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.:

      1. Keep the record as a host/global record (no TenantId or TenantId = null)
      2. Add a join table like EntityTenantShare(EntityId, TenantId) that defines which tenants can see it
      3. Query with a join / subquery for the current tenant.

      Conceptual 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”

    • ABP already supports this naturally: only entities implementing IMultiTenant are tenant-filtered. Others are shared.

Links (verified):

  • https://abp.io/docs/latest/framework/architecture/multi-tenancy
  • https://abp.io/docs/latest/framework/infrastructure/data-filtering
  • https://abp.io/community/articles/hpwn44r3
  • https://abp.io/community/articles/51nvl4u9

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 is not an ABP/Mapperly runtime problem; it’s an IDE design-time compiler / analyzer problem.
  • In ABP, defining Mapperly mappers like below is correct and should compile because Mapperly generates the “implementation part” during build:
[Mapper]
public partial class CoreBookToBookDtoMapper : MapperBase<Book, BookDto>
{
    public override partial BookDto Map(Book source);
    public override partial void Map(Book source, BookDto destination);
}
  • The CS8795 message (“must have an implementation part because it has accessibility modifiers”) typically appears in the IDE when the editor is not seeing the source-generator output, even though dotnet build does. That’s why runtime/build is fine, but the IDE shows errors.

Fix (practical, ABP-safe)

  1. Ensure you build the project at least once (from command line) so Mapperly’s generated code exists:
    • dotnet clean
    • dotnet build
  2. Restart the IDE (Visual Studio / Rider). This forces the design-time compiler to reload generated sources.
  3. Delete bin/ and obj/ folders for the project(s) containing the mappers, then rebuild.
  4. If you use Visual Studio, make sure you are on a recent version (the design-time source-generator pipeline had issues in older VS versions). Upgrading VS usually fixes “generator not picked up in editor” symptoms.
  5. Keep your signatures exactly as ABP docs show: 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)

  • https://abp.io/docs/10.0/framework/infrastructure/object-to-object-mapping

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.

Showing 11 to 20 of 892 entries
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.2.0-preview. Updated on February 17, 2026, 09:10
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.