Skip to content

Commit 33f5a3e

Browse files
Merge pull request #34972 from dotnet/main
Merge to Live
2 parents 8a6e3e3 + 4b27114 commit 33f5a3e

File tree

8 files changed

+318
-335
lines changed

8 files changed

+318
-335
lines changed

aspnetcore/blazor/components/layouts.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ In an app created from a [Blazor project template](xref:blazor/project-structure
9898

9999
### Make the layout namespace available
100100

101-
Layout file locations and namespaces changed over time for the Blazor framework. Depending on the version of Blazor and type of Blazor app that you're building, you may need to indicate the layout's namespace when using it. When referencing a layout implementation and the layout isn't found without indicating the layout's namespace, take any of the following approaches:
101+
Layout file locations and namespaces changed over time for the Blazor framework. Depending on the version of Blazor and type of Blazor app that you're building, you may need to indicate the layout's namespace when using it. When referencing a layout implementation and the layout isn't found without indicating the layout's namespace, take any of the following approaches:
102102

103103
* Add an `@using` directive to the `_Imports.razor` file for the location of the layouts. In the following example, a folder of layouts with the name `Layout` is inside a `Components` folder, and the app's namespace is `BlazorSample`:
104104

@@ -206,6 +206,8 @@ Specifying a layout in `_Imports.razor` overrides a layout specified as the rout
206206

207207
> [!WARNING]
208208
> Do **not** add a Razor `@layout` directive to the root `_Imports.razor` file, which results in an infinite loop of layouts. To control the default app layout, specify the layout in the <xref:Microsoft.AspNetCore.Components.Routing.Router> component. For more information, see the following [Apply a default layout to an app](#apply-a-default-layout-to-an-app) section.
209+
>
210+
> The same condition results when using an `_Imports.razor` file to apply a layout to a folder of components with the `@layout` directive and the layout component itself is in the same folder or folder hierarchy of the `_Imports.razor` file. An infinite loop of applying the layout occurs because the `@layout` directive is also applied to the layout component. To avoid recursion problems, we recommend storing layout components in their own folder (for example, `Layouts`), away from where `_Imports.razor` files are applying them.
209211
210212
> [!NOTE]
211213
> The [`@layout`](xref:mvc/views/razor#layout) Razor directive only applies a layout to routable Razor components with an [`@page`](xref:mvc/views/razor#page) directive.

aspnetcore/blazor/components/quickgrid.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,40 @@ In the following example:
219219

220220
:::moniker-end
221221

222+
223+
:::moniker range=">= aspnetcore-10.0"
224+
225+
<!-- UPDATE 10.0 - API cross-link -->
226+
227+
:::moniker-end
228+
229+
### Close `QuickGrid` column options
230+
231+
Close the `QuickGrid` column options UI with the `CloseColumnOptionsAsync` method.
232+
233+
The following example closes the column options UI as soon as the title filter is applied:
234+
235+
```razor
236+
<QuickGrid @ref="movieGrid" Items="movies">
237+
<PropertyColumn Property="@(m => m.Title)" Title="Title">
238+
<ColumnOptions>
239+
<input type="search" @bind="titleFilter" placeholder="Filter by title"
240+
@bind:after="@(() => movieGrid.CloseColumnOptionsAsync())" />
241+
</ColumnOptions>
242+
</PropertyColumn>
243+
<PropertyColumn Property="@(m => m.Genre)" Title="Genre" />
244+
<PropertyColumn Property="@(m => m.ReleaseYear)" Title="Release Year" />
245+
</QuickGrid>
246+
247+
@code {
248+
private QuickGrid<Movie>? movieGrid;
249+
private string titleFilter = string.Empty;
250+
private IQueryable<Movie> movies = new List<Movie> { ... }.AsQueryable();
251+
private IQueryable<Movie> filteredMovies =>
252+
movies.Where(m => m.Title!.Contains(titleFilter));
253+
}
254+
```
255+
222256
## Entity Framework Core (EF Core) data source
223257

224258
Use the factory pattern to resolve an EF Core database context that provides data to a `QuickGrid` component. For more information on why the factory pattern is recommended, see <xref:blazor/blazor-ef-core>.

aspnetcore/blazor/fundamentals/routing.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1620,13 +1620,44 @@ For additional example code, see the [`ConfigurableNavigationLock` component in
16201620

16211621
Use a <xref:Microsoft.AspNetCore.Components.Routing.NavLink> component in place of HTML hyperlink elements (`<a>`) when creating navigation links. A <xref:Microsoft.AspNetCore.Components.Routing.NavLink> component behaves like an `<a>` element, except it toggles an `active` CSS class based on whether its `href` matches the current URL. The `active` class helps a user understand which page is the active page among the navigation links displayed. Optionally, assign a CSS class name to <xref:Microsoft.AspNetCore.Components.Routing.NavLink.ActiveClass?displayProperty=nameWithType> to apply a custom CSS class to the rendered link when the current route matches the `href`.
16221622

1623+
:::moniker range=">= aspnetcore-10.0"
1624+
16231625
There are two <xref:Microsoft.AspNetCore.Components.Routing.NavLinkMatch> options that you can assign to the `Match` attribute of the `<NavLink>` element:
16241626

1625-
* <xref:Microsoft.AspNetCore.Components.Routing.NavLinkMatch.All?displayProperty=nameWithType>: The <xref:Microsoft.AspNetCore.Components.Routing.NavLink> is active when it matches the entire current URL.
1627+
* <xref:Microsoft.AspNetCore.Components.Routing.NavLinkMatch.All?displayProperty=nameWithType>: The <xref:Microsoft.AspNetCore.Components.Routing.NavLink> is active when it matches the current URL, ignoring the query string and fragment. To include matching on the query string/fragment, use the `Microsoft.AspNetCore.Components.Routing.NavLink.DisableMatchAllIgnoresLeftUriPart` [`AppContext` switch](/dotnet/fundamentals/runtime-libraries/system-appcontext).
16261628
* <xref:Microsoft.AspNetCore.Components.Routing.NavLinkMatch.Prefix?displayProperty=nameWithType> (*default*): The <xref:Microsoft.AspNetCore.Components.Routing.NavLink> is active when it matches any prefix of the current URL.
16271629

1630+
:::moniker-end
1631+
1632+
:::moniker range="< aspnetcore-10.0"
1633+
1634+
There are two <xref:Microsoft.AspNetCore.Components.Routing.NavLinkMatch> options that you can assign to the `Match` attribute of the `<NavLink>` element:
1635+
1636+
* <xref:Microsoft.AspNetCore.Components.Routing.NavLinkMatch.All?displayProperty=nameWithType>: The <xref:Microsoft.AspNetCore.Components.Routing.NavLink> is active when it matches the entire current URL, including the query string and fragment.
1637+
* <xref:Microsoft.AspNetCore.Components.Routing.NavLinkMatch.Prefix?displayProperty=nameWithType> (*default*): The <xref:Microsoft.AspNetCore.Components.Routing.NavLink> is active when it matches any prefix of the current URL.
1638+
1639+
:::moniker-end
1640+
16281641
In the preceding example, the Home <xref:Microsoft.AspNetCore.Components.Routing.NavLink> `href=""` matches the home URL and only receives the `active` CSS class at the app's default base path (`/`). The second <xref:Microsoft.AspNetCore.Components.Routing.NavLink> receives the `active` class when the user visits any URL with a `component` prefix (for example, `/component` and `/component/another-segment`).
16291642

1643+
:::moniker range=">= aspnetcore-10.0"
1644+
1645+
<!-- UPDATE 10.0 - API cross-link -->
1646+
1647+
To adopt custom matching logic, subclass <xref:Microsoft.AspNetCore.Components.Routing.NavLink> and override its `ShouldMatch` method. Return `true` from the method when you want to apply the `active` CSS class:
1648+
1649+
```csharp
1650+
public class CustomNavLink : NavLink
1651+
{
1652+
protected override bool ShouldMatch(string currentUriAbsolute)
1653+
{
1654+
// Custom matching logic
1655+
}
1656+
}
1657+
```
1658+
1659+
:::moniker-end
1660+
16301661
Additional <xref:Microsoft.AspNetCore.Components.Routing.NavLink> component attributes are passed through to the rendered anchor tag. In the following example, the <xref:Microsoft.AspNetCore.Components.Routing.NavLink> component includes the `target` attribute:
16311662

16321663
```razor
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
---
2+
title: Map static files in ASP.NET Core
3+
author: rick-anderson
4+
description: Learn how to serve and secure mapped static files and configure static file hosting middleware behaviors in an ASP.NET Core web app.
5+
monikerRange: '>= aspnetcore-8.0'
6+
ms.author: riande
7+
ms.custom: mvc
8+
ms.date: 3/18/2025
9+
uid: fundamentals/map-static-files
10+
---
11+
12+
# Map static files in ASP.NET Core
13+
14+
:::moniker range=">= aspnetcore-9.0"
15+
16+
By [Rick Anderson](https://twitter.com/RickAndMSFT)
17+
18+
Static files, such as HTML, CSS, images, and JavaScript, are assets an ASP.NET Core app serves directly to clients by default.
19+
20+
For Blazor static files guidance, which adds to or supersedes the guidance in this article, see <xref:blazor/fundamentals/static-files>.
21+
22+
## Serve static files
23+
24+
Static files are stored within the project's [web root](xref:fundamentals/index#web-root) directory. The default directory is `{content root}/wwwroot`, but it can be changed with the <xref:Microsoft.AspNetCore.Hosting.HostingAbstractionsWebHostBuilderExtensions.UseWebRoot%2A> method. For more information, see [Content root](xref:fundamentals/index#content-root) and [Web root](xref:fundamentals/index#web-root).
25+
26+
The <xref:Microsoft.AspNetCore.Builder.WebApplication.CreateBuilder%2A> method sets the content root to the current directory:
27+
28+
[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet&highlight=1,15)]
29+
30+
Static files are accessible via a path relative to the [web root](xref:fundamentals/index#web-root). For example, the **Web Application** project templates contain several folders within the `wwwroot` folder:
31+
32+
* `wwwroot`
33+
* `css`
34+
* `js`
35+
* `lib`
36+
37+
Consider an app with the `wwwroot/images/MyImage.jpg` file. The URI format to access a file in the `images` folder is `https://<hostname>/images/<image_file_name>`. For example, `https://localhost:5001/images/MyImage.jpg`
38+
39+
### MapStaticAssets
40+
41+
Creating performant web apps requires optimizing asset delivery to the browser. Possible optimizations include:
42+
43+
* Serve a given asset once until the file changes or the browser clears its cache. Set the [ETag](https://developer.mozilla.org/docs/Web/HTTP/Headers/ETag) header.
44+
* Prevent the browser from using old or stale assets after an app is updated. Set the [Last-Modified](https://developer.mozilla.org/docs/Web/HTTP/Headers/Last-Modified) header.
45+
* Set up proper [caching headers](https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control).
46+
* Use [caching middleware](xref:performance/caching/middleware).
47+
* Serve [compressed](/aspnet/core/performance/response-compression) versions of the assets when possible.
48+
* Use a [CDN](/microsoft-365/enterprise/content-delivery-networks?view=o365-worldwide&preserve-view=true) to serve the assets closer to the user.
49+
* Minimize the size of assets served to the browser. This optimization doesn't include minification.
50+
51+
<xref:Microsoft.AspNetCore.Builder.StaticAssetsEndpointRouteBuilderExtensions.MapStaticAssets%2A>:
52+
* Integrates the information gathered about static web assets during the build and publish process with a runtime library that processes this information to optimize file serving to the browser.
53+
* Are routing endpoint conventions that optimize the delivery of static assets in an app. It's designed to work with all UI frameworks, including Blazor, Razor Pages, and MVC.
54+
55+
### `MapStaticAssets` versus `UseStaticFiles`
56+
57+
<xref:Microsoft.AspNetCore.Builder.StaticAssetsEndpointRouteBuilderExtensions.MapStaticAssets%2A> is available in ASP.NET Core in .NET 9.0 and later. <xref:Microsoft.AspNetCore.Builder.StaticFileExtensions.UseStaticFiles%2A> must be used in versions prior to .NET 9.0.
58+
59+
<xref:Microsoft.AspNetCore.Builder.StaticFileExtensions.UseStaticFiles%2A> serves static files, but it doesn't provide the same level of optimization as `MapStaticAssets`. `MapStaticAssets` is optimized for serving assets that the app has knowledge of at runtime. If the app serves assets from other locations, such as disk or embedded resources, `UseStaticFiles` should be used.
60+
61+
The following features are supported in `UseStaticFiles` but not in `MapStaticAssets`:
62+
63+
* [Serving files from disk or embedded resources, or other locations](xref:fundamentals/static-files#serve-files-from-multiple-locations)
64+
* [Serve files outside of web root](xref:fundamentals/static-files#serve-files-outside-of-web-root)
65+
* [Set HTTP response headers](xref:fundamentals/static-files#set-http-response-headers)
66+
* [Directory browsing](xref:fundamentals/static-files#directory-browsing)
67+
* [Serve default documents](xref:fundamentals/static-files#serve-default-documents)
68+
* [`FileExtensionContentTypeProvider`](xref:fundamentals/static-files#fileextensioncontenttypeprovider)
69+
* [Serve files from multiple locations](xref:fundamentals/static-files#serve-files-from-multiple-locations)
70+
* [Serving files from disk or embedded resources, or other locations](xref:fundamentals/static-files#serve-files-from-multiple-locations)
71+
* [Serve files outside of web root](xref:fundamentals/static-files#serve-files-outside-of-web-root)
72+
* [Set HTTP response headers](xref:fundamentals/static-files#set-http-response-headers)
73+
* [Directory browsing](xref:fundamentals/static-files#directory-browsing)
74+
* [Serve default documents](xref:fundamentals/static-files#serve-default-documents)
75+
* [`FileExtensionContentTypeProvider`](xref:fundamentals/static-files#fileextensioncontenttypeprovider)
76+
* [Serve files from multiple locations](xref:fundamentals/static-files#serve-files-from-multiple-locations)
77+
78+
### Serve files in web root
79+
80+
The default web app templates call the <xref:Microsoft.AspNetCore.Builder.StaticAssetsEndpointRouteBuilderExtensions.MapStaticAssets%2A> method in `Program.cs`, which enables static files to be served:
81+
82+
[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet&highlight=15)]
83+
84+
The parameterless `MapStaticAssets` method overload marks the files in [web root](xref:fundamentals/index#web-root) as servable. The following markup references `wwwroot/images/MyImage.jpg`:
85+
86+
```html
87+
<img src="~/images/MyImage.jpg" class="img" alt="My image" />
88+
```
89+
90+
In the preceding markup, the tilde character `~` points to the [web root](xref:fundamentals/index#web-root).
91+
92+
### Serve files outside of web root
93+
94+
Consider a directory hierarchy in which the static files to be served reside outside of the [web root](xref:fundamentals/index#web-root):
95+
96+
* `wwwroot`
97+
* `css`
98+
* `images`
99+
* `js`
100+
* `MyStaticFiles`
101+
* `images`
102+
* `red-rose.jpg`
103+
104+
A request can access the `red-rose.jpg` file by configuring the Static File Middleware as follows:
105+
106+
[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_rr&highlight=1,18-23)]
107+
108+
In the preceding code, the *MyStaticFiles* directory hierarchy is exposed publicly via the *StaticFiles* URI segment. A request to `https://<hostname>/StaticFiles/images/red-rose.jpg` serves the `red-rose.jpg` file.
109+
110+
The following markup references `MyStaticFiles/images/red-rose.jpg`:
111+
<!-- zz test via /Home2/MyStaticFilesRR -->
112+
[!code-html[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Views/Home2/MyStaticFilesRR.cshtml?range=1)]
113+
114+
To serve files from multiple locations, see [Serve files from multiple locations](xref:fundamentals/static-files#serve-files-from-multiple-locations).
115+
116+
### Set HTTP response headers
117+
118+
A <xref:Microsoft.AspNetCore.Builder.StaticFileOptions> object can be used to set HTTP response headers. In addition to configuring static file serving from the [web root](xref:fundamentals/index#web-root), the following code sets the [Cache-Control](https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control) header:
119+
120+
[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_rh&highlight=16-24)]
121+
122+
The preceding code makes static files publicly available in the local cache for one week.
123+
124+
## Static file authorization
125+
126+
The ASP.NET Core templates call <xref:Microsoft.AspNetCore.Builder.StaticAssetsEndpointRouteBuilderExtensions.MapStaticAssets%2A> before calling <xref:Microsoft.AspNetCore.Builder.AuthorizationAppBuilderExtensions.UseAuthorization%2A>. Most apps follow this pattern. When `MapStaticAssets` is called before the authorization middleware:
127+
128+
* No authorization checks are performed on the static files.
129+
* Static files served by the Static File Middleware, such as those under `wwwroot`, are publicly accessible.
130+
131+
To serve static files based on authorization, see [Static file authorization](xref:fundamentals/static-files#static-file-authorization).
132+
133+
## Serve files from multiple locations
134+
135+
Consider the following Razor page which displays the `/MyStaticFiles/image3.png` file:
136+
137+
[!code-cshtml[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Pages/Test.cshtml?highlight=5)]
138+
139+
`UseStaticFiles` and `UseFileServer` default to the file provider pointing at `wwwroot`. Additional instances of `UseStaticFiles` and `UseFileServer` can be provided with other file providers to serve files from other locations. The following example calls `UseStaticFiles` twice to serve files from both `wwwroot` and `MyStaticFiles`:
140+
141+
[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_mul)]
142+
143+
Using the preceding code:
144+
145+
* The `/MyStaticFiles/image3.png` file is displayed.
146+
* The [Image Tag Helpers](xref:mvc/views/tag-helpers/builtin-th/image-tag-helper) <xref:Microsoft.AspNetCore.Mvc.TagHelpers.ImageTagHelper.AppendVersion> is not applied because the Tag Helpers depend on <xref:Microsoft.AspNetCore.Hosting.IWebHostEnvironment.WebRootFileProvider>. `WebRootFileProvider` has not been updated to include the `MyStaticFiles` folder.
147+
148+
The following code updates the `WebRootFileProvider`, which enables the Image Tag Helper to provide a version:
149+
150+
[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_mult2)]
151+
152+
> [!NOTE]
153+
> The preceding approach applies to Razor Pages and MVC apps. For guidance that applies to Blazor Web Apps, see <xref:blazor/fundamentals/static-files#serve-files-from-multiple-locations>.
154+
155+
## Serve files outside wwwroot by updating IWebHostEnvironment.WebRootPath
156+
157+
When <xref:Microsoft.AspNetCore.Hosting.IWebHostEnvironment.WebRootPath%2A?displayProperty=nameWithType> is set to a folder other than `wwwroot`:
158+
159+
* In the development environment, static assets found in both `wwwroot` and the updated `IWebHostEnvironment.WebRootPath` are served from `wwwroot`.
160+
* In any environment other than development, duplicate static assets are served from the updated `IWebHostEnvironment.WebRootPath` folder.
161+
162+
Consider a web app created with the empty web template:
163+
164+
* Containing an `Index.html` file in `wwwroot` and `wwwroot-custom`.
165+
* With the following updated `Program.cs` file that sets `WebRootPath = "wwwroot-custom"`:
166+
167+
[!code-csharp[](~/fundamentals/static-files/samples/9.x/WebRoot/Program.cs?name=snippet1)]
168+
169+
In the preceding code, requests to `/`:
170+
171+
* In the development environment return `wwwroot/Index.html`
172+
* In any environment other than development return `wwwroot-custom/Index.html`
173+
174+
To ensure assets from `wwwroot-custom` are returned, use one of the following approaches:
175+
176+
* Delete duplicate named assets in `wwwroot`.
177+
* Set `"ASPNETCORE_ENVIRONMENT"` in `Properties/launchSettings.json` to any value other than `"Development"`.
178+
* Completely disable static web assets by setting `<StaticWebAssetsEnabled>false</StaticWebAssetsEnabled>` in the project file. ***WARNING, disabling static web assets disables [Razor Class Libraries](xref:razor-pages/ui-class)***.
179+
* Add the following XML to the project file:
180+
181+
```xml
182+
<ItemGroup>
183+
<Content Remove="wwwroot\**" />
184+
</ItemGroup>
185+
```
186+
187+
The following code updates `IWebHostEnvironment.WebRootPath` to a non development value, guaranteeing duplicate content is returned from `wwwroot-custom` rather than `wwwroot`:
188+
189+
[!code-csharp[](~/fundamentals/static-files/samples/9.x/WebRoot/Program.cs?name=snippet2&highlight=5)]
190+
191+
## Additional resources
192+
193+
* [View or download sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/fundamentals/static-files/samples) ([how to download](xref:index#how-to-download-a-sample))
194+
* [Middleware](xref:fundamentals/middleware/index)
195+
* [Introduction to ASP.NET Core](xref:index)
196+
* <xref:blazor/file-uploads>
197+
* <xref:blazor/file-downloads>
198+
199+
:::moniker-end

0 commit comments

Comments
 (0)