Browse Source

Add docs for `AppUrlOptions` and `IAppUrlProvider`

pull/25505/head
maliming 2 months ago
parent
commit
85cb620d60
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 4
      docs/en/docs-nav.json
  2. 2
      docs/en/framework/architecture/multi-tenancy/index.md
  3. 164
      docs/en/framework/infrastructure/app-urls.md
  4. 1
      docs/en/framework/infrastructure/emailing.md
  5. 2
      docs/en/guides/ms-multi-tenant-domain-resolving.md
  6. 2
      docs/en/modules/account.md

4
docs/en/docs-nav.json

@ -586,6 +586,10 @@
}
]
},
{
"text": "Application URLs",
"path": "framework/infrastructure/app-urls.md"
},
{
"text": "Background Jobs",
"items": [

2
docs/en/framework/architecture/multi-tenancy/index.md

@ -357,6 +357,8 @@ context.Services
```
The configuration above resolves the current tenant from the incoming request (inbound). To make the **outbound** URLs your application generates — such as the password reset link inside an Account email — point to the tenant's subdomain as well, configure `AppUrlOptions`. See [Application URLs](../../infrastructure/app-urls.md#multi-tenant-aware-urls).
##### Custom Tenant Resolvers
You can add implement your custom tenant resolver and configure the `AbpTenantResolveOptions` in your module's `ConfigureServices` method as like below:

164
docs/en/framework/infrastructure/app-urls.md

@ -0,0 +1,164 @@
```json
//[doc-seo]
{
"Description": "Configure cross-application URLs in ABP with AppUrlOptions and IAppUrlProvider, including multi-tenant subdomain templates and redirect URL validation."
}
```
# Application URLs
ABP provides the `AppUrlOptions` options class and the `IAppUrlProvider` service to centrally configure and resolve URLs that point to **other applications** in your solution (for example, an MVC/Razor Pages UI, an Auth Server, an HTTP API host, etc.). They are typically used when code in one application needs to build a link that targets another — like the Account module putting a **password reset link** into an email.
* Defines `AppUrlOptions` to register the **root URL** and named relative URLs of each application.
* Provides `IAppUrlProvider` to **resolve** those URLs at runtime, with optional **tenant-aware** placeholder substitution.
* Supports **subdomain-style templates** (e.g. `https://{0}.example.com`) that produce per-tenant URLs without extra code.
* Maintains a `RedirectAllowedUrls` list used by `IAppUrlProvider.IsRedirectAllowedUrlAsync` to validate redirect targets.
> `AppUrlOptions` is defined in the `Volo.Abp.UI.Navigation` package, which comes pre-installed with the [application startup template](../../solution-templates/layered-web-application).
## Configuring Application URLs
`AppUrlOptions` exposes a dictionary of **applications**, each with a `RootUrl` and a set of named `Urls`.
**Example: Set the root URL and a named URL for the MVC application**
```csharp
Configure<AppUrlOptions>(options =>
{
options.Applications["MVC"].RootUrl = "https://my-app.com";
options.Applications["MVC"].Urls["MyPage"] = "my-page";
});
```
* `"MVC"` is the **application key**. Some modules (such as Account) register their URLs under a known key — `"MVC"` is the default for the **server-side UI**. You can use any key you want for your own applications.
* `RootUrl` is the **base URL** of that application.
* `Urls[urlName]` is a **relative path** appended to `RootUrl`. The final URL is built as `RootUrl.EnsureEndsWith('/') + Urls[urlName]`, so the relative path should **not** start with a `/`. When `RootUrl` is `null`, the value of `Urls[urlName]` is returned as-is.
The Account module, for example, **pre-registers** its URLs in its application module:
**Example: How the Account module registers the password reset URL**
```csharp
Configure<AppUrlOptions>(options =>
{
options.Applications["MVC"].Urls[AccountUrlNames.PasswordReset] = "Account/ResetPassword";
});
```
> So configuring `Applications["MVC"].RootUrl` in your own module is usually enough to make password reset and similar Account email links point to the right host.
### Defaults in the application startup template
The ABP **application startup template** wires `Applications["MVC"].RootUrl` to the `App:SelfUrl` setting and seeds `RedirectAllowedUrls` from `App:RedirectAllowedUrls`:
```csharp
Configure<AppUrlOptions>(options =>
{
options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"];
options.RedirectAllowedUrls.AddRange(
configuration["App:RedirectAllowedUrls"]?.Split(',') ?? Array.Empty<string>());
});
```
> This is why Account email links point to your **host URL** out of the box: they reuse `App:SelfUrl`. If that default isn't what you want — for example, in a subdomain-based **multi-tenant** setup — override `Applications["MVC"].RootUrl` with the template you need (see [Multi-Tenant Aware URLs](#multi-tenant-aware-urls)).
## Using `IAppUrlProvider`
[Inject](../fundamentals/dependency-injection.md) the `IAppUrlProvider` service into any class that needs to build a cross-application URL.
**Example: Resolve a root URL and a named URL of the MVC application**
```csharp
public class MyNotificationSender : ITransientDependency
{
private readonly IAppUrlProvider _appUrlProvider;
public MyNotificationSender(IAppUrlProvider appUrlProvider)
{
_appUrlProvider = appUrlProvider;
}
public async Task SendAsync()
{
var rootUrl = await _appUrlProvider.GetUrlAsync("MVC");
var pageUrl = await _appUrlProvider.GetUrlAsync("MVC", "MyPage");
}
}
```
* `GetUrlAsync(appName)` returns the configured `RootUrl` for the given application.
* `GetUrlAsync(appName, urlName)` returns the **combined URL** described above.
* `GetUrlAsync(...)` throws an `AbpException` when the resolved URL is `null` or empty (e.g. both `RootUrl` and `Urls[urlName]` are unset). Use `GetUrlOrNullAsync(...)` if you'd rather get `null` and decide what to do yourself.
* `NormalizeUrlAsync(url)` applies tenant placeholder substitution to a URL string that you already have. Useful when the URL doesn't come from `AppUrlOptions`.
## Multi-Tenant Aware URLs
If your solution uses **subdomain-based** multi-tenancy (see the [Domain/Subdomain Tenant Resolver](../architecture/multi-tenancy/index.md#domainsubdomain-tenant-resolver)), you'll usually want the **outbound URLs** you generate (email links, redirects) to also be tenant-aware — otherwise the link in a password reset email won't point to the tenant's subdomain.
`AppUrlOptions` supports the following **placeholders** in any URL value. They are substituted by `IAppUrlProvider` based on the **current tenant**:
| Placeholder | Replaced with |
| --- | --- |
| `{0}` | Current tenant **name** |
| `{%{{{ {{tenantName}} }}}%}` | Current tenant **name** |
| `{%{{{ {{tenantId}} }}}%}` | Current tenant **id** |
The `{0}` placeholder uses the **same convention** as `AddDomainTenantResolver("{0}.example.com")`, so a typical subdomain-tenant setup looks like this:
**Example: Tenant-aware Account email links via a subdomain template**
```csharp
Configure<AbpTenantResolveOptions>(options =>
{
options.AddDomainTenantResolver("{0}.example.com");
});
Configure<AppUrlOptions>(options =>
{
options.Applications["MVC"].RootUrl = "https://{0}.example.com";
});
```
With this configuration, password reset emails sent to a tenant whose name is `acme` will contain a link starting with `https://acme.example.com/`, matching the tenant's subdomain.
### Host (no tenant) Fallback
When there is **no current tenant** (host-side request), the placeholder **and the dot following it** are removed together:
| Template | Tenant `acme` | Host (no tenant) |
| --- | --- | --- |
| `https://{0}.example.com` | `https://acme.example.com` | `https://example.com` |
| `https://{%{{{ {{tenantId}} }}}%}.example.com` | `https://3a21....example.com` | `https://example.com` |
A single subdomain-style template like the ones above therefore works for **both** tenant and host scenarios without extra configuration.
> If your subdomain is based on the tenant **id** rather than the name, use `https://{%{{{ {{tenantId}} }}}%}.example.com`. The resolver's `{0}` placeholder accepts both name and id when finding a tenant, but `AppUrlOptions` substitutes `{0}` with the tenant **name**; if those two don't match, switch to the explicit `{%{{{ {{tenantId}} }}}%}` form on the `AppUrlOptions` side.
## Redirect Allowed URLs
`AppUrlOptions.RedirectAllowedUrls` is a list of URL entries used by `IAppUrlProvider.IsRedirectAllowedUrlAsync(url)` to decide whether a redirect target is allowed. A URL is allowed when it satisfies **either** of:
* **Prefix match**: the URL string **starts with** a configured entry (case-insensitive).
* **Subdomain match**: the URL and the entry have the **same scheme** and **port**, and the URL's host **ends with** `.{entry-host}`.
**Example: Register allowed redirect URLs (including a wildcard)**
```csharp
Configure<AppUrlOptions>(options =>
{
options.RedirectAllowedUrls.Add("https://my-app.com");
options.RedirectAllowedUrls.Add("https://admin.my-app.com");
options.RedirectAllowedUrls.Add("https://*.my-app.com");
});
```
* A **plain entry** like `https://my-app.com` allows any URL that starts with that prefix, plus any subdomain of `my-app.com`.
* A **wildcard entry** like `https://*.my-app.com` allows any subdomain of `my-app.com`; the `*.` is stripped before the subdomain check.
* Entries also go through **tenant placeholder substitution**, so `https://{0}.my-app.com` is resolved to the current tenant's URL first (e.g. `https://acme.my-app.com`) and then compared. Use the wildcard form when you need to allow *any* tenant subdomain regardless of the current tenant.
## See Also
* [Multi-Tenancy](../architecture/multi-tenancy/index.md)
* [Account Module](../../modules/account.md)
* [Emailing](emailing.md)

1
docs/en/framework/infrastructure/emailing.md

@ -265,3 +265,4 @@ So, don't confuse if you don't receive emails on DEBUG mode. Emails will be sent
## See Also
* [MailKit integration for sending emails](./mail-kit.md)
* [Application URLs](./app-urls.md) — for building cross-application links inside email content (e.g. password reset links).

2
docs/en/guides/ms-multi-tenant-domain-resolving.md

@ -68,6 +68,8 @@ This configuration will allow subdomain tenant resolving. Ex, if you have a tena
**For angular application**, you don't need to add anything since we will be overriding the angular environment via kubernetes values file.
> The configuration above resolves the current tenant from the incoming request. To make outbound URLs generated through `IAppUrlProvider` (Account email links, redirects, etc.) also tenant-aware, configure `AppUrlOptions` with the same `{0}` template. See [Application URLs](../framework/infrastructure/app-urls.md#multi-tenant-aware-urls).
## Configuring AuthServer
When the tenant try to login from an application (Ex `https://volosoft.angular.mystore.dev`) it will be redirected to AuthServer (`https://volosoft.authserver.mystore.dev`) and you will be seeing a **HTTP 400 error** related to `invalid redirect_uri`. If you check the authserver application logs (under Logs/logs.txt file or console logs), you will notice that the `https://volosoft.angular.mystore.dev` is not a valid redirect_uri since it is not been seeded by the OpenIddictDataSeeder. Only the **host** applications, gateways and microservice URLs are seeded (like `https://angular.mystore.dev`).

2
docs/en/modules/account.md

@ -43,6 +43,8 @@ Social/external login buttons becomes visible if you setup it. See the *Social/E
![account-module-forgot-password](../images/account-module-forgot-password.png)
> The host part of the password reset link is built from `AppUrlOptions.Applications["MVC"].RootUrl`. Configure it if the default `App:SelfUrl` isn't what you want users to see in emails — for example, when you use subdomain-based multi-tenancy and want the link to point to the tenant's subdomain. See [Application URLs](../framework/infrastructure/app-urls.md).
### Account Management
`/Account/Manage` page is used to change password and personal information of the user.

Loading…
Cancel
Save