Expand lead-in coverage on app base path (#29271)

pull/29316/head
Luke Latham 2023-05-19 09:59:02 -04:00 committed by GitHub
parent f1b2a84dba
commit ec254a0d21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 92 additions and 12 deletions

View File

@ -74,7 +74,9 @@ For more information on *solutions*, see <xref:blazor/tooling#visual-studio-solu
## App base path
The *app base path* is the app's root URL path. Consider the following ASP.NET Core app and Blazor sub-app:
The *app base path* is the app's root URL path. Successful routing in Blazor apps requires framework configuration for any root URL path that isn't at the default app base path `/`.
Consider the following ASP.NET Core app and Blazor sub-app:
* The ASP.NET Core app is named `MyApp`:
* The app physically resides at `d:/MyApp`.
@ -85,22 +87,99 @@ The *app base path* is the app's root URL path. Consider the following ASP.NET C
Without specifying additional configuration for `CoolApp`, the sub-app in this scenario has no knowledge of where it resides on the server. For example, the app can't construct correct relative URLs to its resources without knowing that it resides at the relative URL path `/CoolApp/`. This scenario also applies in various hosting and reverse proxy scenarios when an app isn't hosted at a root URL path.
To provide configuration for the Blazor app's base path of `https://www.contoso.com/CoolApp/`, set the relative root path.
### Background
By configuring the relative URL path for an app, a component that isn't in the root directory can construct URLs relative to the app's root path. Components at different levels of the directory structure can build links to other resources at locations throughout the app. The app base path is also used to intercept selected hyperlinks where the `href` target of the link is within the app base path URI space. The Blazor router handles the internal navigation.
An anchor tag's destination ([`href`](https://developer.mozilla.org/docs/Web/HTML/Element/a)) can be composed with two endpoints:
* Absolute locations that include a scheme (defaults to the page's scheme if omitted), host, port, and path or just a forward slash (`/`) followed by the path.
Examples: `https://example.com/a/b/c` or `/a/b/c`
* Relative locations that contain just a path and do not start with a forward slash (`/`). These are resolved relative to the current document URL or the `<base>` tag's value, if specified.
Example: `a/b/c`
The presence of a trailing slash (`/`) in a configured app base path is significant to compute the base path for URLs of the app. For example, `https://example.com/a` has a base path of `https://example.com/`, while `https://example.com/a/` with a trailing slash has a base path of `https://example.com/a`.
There are three sources of links that pertain to Blazor in ASP.NET Core apps:
* URLs manually written in the `_Host.cshtml` file (Blazor Server), which if you are rendering inside different documents should always be absolute.
* URLs in Razor components (`.razor`) are typically relative, but are essentially also manually written.
* URLs in scripts, such as the Blazor scripts (`blazor.webassembly.js` and `blazor.server.js`), which are relative to the document.
If you're rendering a Blazor app from different documents (for example, `/Admin/B/C/` and `/Admin/D/E/`), you must take the app base path into account, or the base path is different when the app renders in each document and the resources are fetched from the wrong URLs.
There are two approaches to deal with the challenge of resolving relative links correctly:
* Map the resources dynamically using the document they were rendered on as the root.
* Set a consistent base path for the document and map the resources under that base path.
The first option is more complicated and isn't the most typical approach, as it makes navigation different for each document. Consider the following example for rendering a page `/Something/Else`:
* Rendered under `/Admin/B/C/`, the page is rendered with a path of `/Admin/B/C/Something/Else`.
* Rendered under `/Admin/D/E/`, the page is rendered ***at the same path*** of `/Admin/B/C/Something/Else`.
Under the first approach, routing offers <xref:Microsoft.AspNetCore.Routing.IDynamicEndpointMetadata> and <xref:Microsoft.AspNetCore.Routing.MatcherPolicy>, which in combination can be the basis for implementing a completely dynamic solution that determines at runtime about how requests are routed.
For the second option, which is the usual approach taken, the app sets the base path in the document and maps the server endpoints to paths under the base. The following guidance adopts this approach.
### Blazor Server
Map the SignalR hub of a Blazor Server app by passing the path to <xref:Microsoft.AspNetCore.Builder.ComponentEndpointRouteBuilderExtensions.MapBlazorHub%2A>, which is the most typical approach:
```csharp
endpoints.MapBlazorHub("base/path");
```
The benefit of using <xref:Microsoft.AspNetCore.Builder.ComponentEndpointRouteBuilderExtensions.MapBlazorHub%2A> is that you can map patterns, such as `"{tenant}"` and not just concrete paths.
You can also map the SignalR hub when the app is in a virtual folder inside a forked pipeline using a snippet similar to the following:
```csharp
app.Map("/base/path/", subapp => {
subapp.UsePathBase("/base/path/");
subapp.UseRouting();
subapp.UseEndpoints(endpoints => endpoints.MapBlazorHub());
});
```
Configure the `<base>` tag, per the guidance in the [Configure the app base path](#configure-the-app-base-path) section.
### Hosted Blazor WebAssembly
If the app is a hosted Blazor WebAssembly app:
* In the in the **:::no-loc text="Server":::** project (`Program.cs`):
* Adjust the path of <xref:Microsoft.AspNetCore.Builder.ComponentsWebAssemblyApplicationBuilderExtensions.UseBlazorFrameworkFiles%2A> (for example, `app.UseBlazorFrameworkFiles("/base/path");`).
* Configure calls to <xref:Microsoft.AspNetCore.Builder.StaticFileExtensions.UseStaticFiles%2A> (for example, `app.UseStaticFiles("/base/path");`).
* In the **:::no-loc text="Client":::** project:
* Configure [`<StaticWebAssetBasePath>`](xref:blazor/fundamentals/static-files#static-web-asset-base-path) in the project file to match the path for serving static web assets (for example, `<StaticWebAssetBasePath>base/path</StaticWebAssetBasePath>
`).
* Configure the `<base>` tag, per the guidance in the [Configure the app base path](#configure-the-app-base-path) section.
For an example of hosting multiple Blazor WebAssembly apps in a hosted Blazor WebAssembly solution, see <xref:blazor/host-and-deploy/multiple-hosted-webassembly>, where approaches are explained for domain/port hosting and subpath hosting of multiple Blazor WebAssembly client apps.
### Standalone Blazor WebAssembly
In a standalone Blazor WebAssembly app, only the `<base>` tag is configured, per the guidance in the [Configure the app base path](#configure-the-app-base-path) section.
### Configure the app base path
To provide configuration for the Blazor app's base path of `https://www.contoso.com/CoolApp/`, set the app base path, which is also called the relative root path.
By configuring the app base path, a component that isn't in the root directory can construct URLs relative to the app's root path. Components at different levels of the directory structure can build links to other resources at locations throughout the app. The app base path is also used to intercept selected hyperlinks where the `href` target of the link is within the app base path URI space. The Blazor router handles the internal navigation.
In many hosting scenarios, the relative URL path to the app is the root of the app. In these default cases, the app's relative URL base path is the following:
* Blazor WebAssembly: `/` configured as `<base href="/" />`.
* Blazor WebAssembly (standalone or hosted): `/` configured as `<base href="/" />`.
* Blazor Server: `~/` configured as `<base href="~/" />`.
For the location of `<head>` content in Blazor apps, see <xref:blazor/project-structure#location-of-head-content>.
In other hosting scenarios, such as GitHub Pages and IIS sub-apps, the app base path must be set to the server's relative URL path of the app.
> [!NOTE]
> In some hosting scenarios, such as GitHub Pages and IIS sub-apps, the app base path must be set to the server's relative URL path of the app.
* Standalone Blazor WebAssembly:
`wwwroot/index.html`:
* Standalone Blazor WebAssembly (`wwwroot/index.html`):
```html
<base href="/CoolApp/">
@ -108,9 +187,7 @@ In other hosting scenarios, such as GitHub Pages and IIS sub-apps, the app base
**The trailing slash is required.**
* Hosted Blazor WebAssembly:
In the **:::no-loc text="Client":::** project, `wwwroot/index.html`:
* Hosted Blazor WebAssembly (**:::no-loc text="Client":::** project, `wwwroot/index.html`):
```html
<base href="/CoolApp/">
@ -182,7 +259,10 @@ Do ***not*** prefix [Navigation Manager](xref:blazor/fundamentals/routing#uri-an
* <span aria-hidden="true">✔️</span> Correct: `Navigation.NavigateTo("other");`
* <span aria-hidden="true">✔️</span> Correct: `Navigation.NavigateTo("./other");`
In typical configurations for Azure/IIS hosting, additional configuration usually isn't required. In some non-IIS hosting and reverse proxy hosting scenarios, additional Static File Middleware configuration might be required to serve static files correctly (for example, `app.UseStaticFiles("/CoolApp");`). The required configuration might require further configuration to serve the Blazor script (`_framework/blazor.server.js` or `_framework/blazor.webassembly.js`). For more information, see <xref:blazor/fundamentals/static-files>.
In typical configurations for Azure/IIS hosting, additional configuration usually isn't required. In some non-IIS hosting and reverse proxy hosting scenarios, additional Static File Middleware configuration might be required:
* To serve static files correctly (for example, `app.UseStaticFiles("/CoolApp");`).
* To serve the Blazor script (`_framework/blazor.server.js` or `_framework/blazor.webassembly.js`). For more information, see <xref:blazor/fundamentals/static-files>.
For a Blazor WebAssembly app with a non-root relative URL path (for example, `<base href="/CoolApp/">`), the app fails to find its resources *when run locally*. To overcome this problem during local development and testing, you can supply a *path base* argument that matches the `href` value of the `<base>` tag at runtime. **Don't include a trailing slash.** To pass the path base argument when running the app locally, execute the `dotnet run` command from the app's directory with the `--pathbase` option: