Skip to content

Commit 68989b2

Browse files
Move to GitStaticAssets/4 (#33050)
* Move to GitStaticAssets * Move to GitStaticAssets * Move to GitStaticAssets * Move to GitStaticAssets * Move to GitStaticAssets * Move to GitStaticAssets * Move to GitStaticAssets * Move to GitStaticAssets * Move to GitStaticAssets * work * work * work * work * work * work
1 parent e608708 commit 68989b2

File tree

16 files changed

+193
-108
lines changed

16 files changed

+193
-108
lines changed

aspnetcore/fundamentals/static-files.md

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description: Learn how to serve and secure static files and configure static fil
55
monikerRange: '>= aspnetcore-3.1'
66
ms.author: riande
77
ms.custom: mvc
8-
ms.date: 7/4/2024
8+
ms.date: 7/25/2024
99
uid: fundamentals/static-files
1010
---
1111
# Static files in ASP.NET Core
@@ -24,7 +24,7 @@ Static files are stored within the project's [web root](xref:fundamentals/index#
2424

2525
The <xref:Microsoft.AspNetCore.Builder.WebApplication.CreateBuilder%2A> method sets the content root to the current directory:
2626

27-
[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet&highlight=1)]
27+
[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet&highlight=1,15)]
2828

2929
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:
3030

@@ -33,16 +33,28 @@ Static files are accessible via a path relative to the [web root](xref:fundament
3333
* `js`
3434
* `lib`
3535

36-
Consider creating the *wwwroot/images* folder and adding 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`
36+
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`
3737

3838
### MapStaticAssets
3939

40-
`MapStaticAssets` is a middleware that helps optimize the delivery of static assets in an app. For more information, see [Optimizing static web asset delivery
40+
Creating performant web apps requires optimizing asset delivery to the browser. Possible optimizations include:
41+
42+
* 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.
43+
* 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.
44+
* Set up proper [caching headers](https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control).
45+
* Use [caching middleware](xref:performance/caching/middleware).
46+
* Serve [compressed](/aspnet/core/performance/response-compression) versions of the assets when possible.
47+
* Use a [CDN](/microsoft-365/enterprise/content-delivery-networks?view=o365-worldwide&preserve-view=true) to serve the assets closer to the user.
48+
* Minimize the size of assets served to the browser. This optimization doesn't include minification.
49+
50+
[`MapStaticAssets`](/dotnet/api/microsoft.aspnetcore.builder.staticassetsendpointroutebuilderextensions.mapstaticassets) is a middleware that helps optimize the delivery of static assets in an app. It's designed to work with all UI frameworks, including Blazor, Razor Pages, and MVC.
51+
52+
[`UseStaticFiles`](/dotnet/api/microsoft.aspnetcore.builder.staticfileextensions.usestaticfiles) also serves static files, but it doesn't provide the same level of optimization as `MapStaticAssets`. For a comparison of `UseStaticFiles` and `MapStaticAssets`, see [Optimizing static web asset delivery
4153
](xref:aspnetcore-9#optimizing-static-web-asset-delivery).
4254

4355
### Serve files in web root
4456

45-
The default web app templates call the <xref:Microsoft.AspNetCore.Builder.StaticFileExtensions.UseStaticFiles%2A> method in `Program.cs`, which enables static files to be served:
57+
The default web app templates call the [`MapStaticAssets`](/dotnet/api/microsoft.aspnetcore.builder.staticassetsendpointroutebuilderextensions.mapstaticassets) method in `Program.cs`, which enables static files to be served:
4658

4759
[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet&highlight=15)]
4860

@@ -84,11 +96,11 @@ A <xref:Microsoft.AspNetCore.Builder.StaticFileOptions> object can be used to se
8496

8597
[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_rh&highlight=16-24)]
8698

87-
The preceding code makes static files publicly available in the local cache for one week (604800 seconds).
99+
The preceding code makes static files publicly available in the local cache for one week.
88100

89101
## Static file authorization
90102

91-
The ASP.NET Core templates call <xref:Microsoft.AspNetCore.Builder.StaticFileExtensions.UseStaticFiles%2A> before calling <xref:Microsoft.AspNetCore.Builder.AuthorizationAppBuilderExtensions.UseAuthorization%2A>. Most apps follow this pattern. When the Static File Middleware is called before the authorization middleware:
103+
The ASP.NET Core templates call [`MapStaticAssets`](/dotnet/api/microsoft.aspnetcore.builder.staticassetsendpointroutebuilderextensions.mapstaticassets) before calling <xref:Microsoft.AspNetCore.Builder.AuthorizationAppBuilderExtensions.UseAuthorization%2A>. Most apps follow this pattern. When the Static File Middleware is called before the authorization middleware:
92104

93105
* No authorization checks are performed on the static files.
94106
* Static files served by the Static File Middleware, such as those under `wwwroot`, are publicly accessible.
@@ -99,25 +111,29 @@ To serve static files based on authorization:
99111
* Call `UseStaticFiles`, specifying a path, after calling `UseAuthorization`.
100112
* Set the [fallback authorization policy](xref:Microsoft.AspNetCore.Authorization.AuthorizationOptions.FallbackPolicy).
101113

102-
[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFileAuth/Program.cs?name=snippet_auth&highlight=18-23,38,45-50)]
114+
<!-- ~/fundamentals/static-files/samples/8.x/StaticFileAuth is a different app that ~/fundamentals/static-files/samples/6.x/StaticFileAuth -->
115+
[!code-csharp[](~/fundamentals/static-files/samples/6.x/StaticFileAuth/Program.cs?name=snippet_auth&highlight=18-23,38,45-50)]
103116

104117
In the preceding code, the fallback authorization policy requires ***all*** users to be authenticated. Endpoints such as controllers, Razor Pages, etc that specify their own authorization requirements don't use the fallback authorization policy. For example, Razor Pages, controllers, or action methods with `[AllowAnonymous]` or `[Authorize(PolicyName="MyPolicy")]` use the applied authorization attribute rather than the fallback authorization policy.
105118

106119
<xref:Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireAuthenticatedUser%2A> adds <xref:Microsoft.AspNetCore.Authorization.Infrastructure.DenyAnonymousAuthorizationRequirement> to the current instance, which enforces that the current user is authenticated.
107120

108-
Static assets under `wwwroot` are publicly accessible because the default Static File Middleware (`app.UseStaticFiles();`) is called before `UseAuthentication`. Static assets in the ***MyStaticFiles*** folder require authentication. The [sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/fundamentals/static-files/samples/9.x) demonstrates this.
121+
Static assets under `wwwroot` are publicly accessible because the default Static File Middleware (`app.UseStaticFiles();`) is called before `UseAuthentication`. Static assets in the ***MyStaticFiles*** folder require authentication. The [sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/fundamentals/static-files/samples/8.x) demonstrates this.
109122

110123
An alternative approach to serve files based on authorization is to:
111124

112125
* Store them outside of `wwwroot` and any directory accessible to the Static File Middleware.
113126
* Serve them via an action method to which authorization is applied and return a <xref:Microsoft.AspNetCore.Mvc.FileResult> object:
114127

128+
115129
[!code-csharp[](~/fundamentals/static-files/samples/6.x/StaticFileAuth/Pages/BannerImage.cshtml.cs?name=snippet)]
116130

117131
The preceding approach requires a page or endpoint per file. The following code returns files or uploads files for authenticated users:
118132

119133
:::code language="csharp" source="~/fundamentals/static-files/samples/9.x/StaticFileAuth/Program.cs" id="snippet_1":::
120134

135+
IFormFile in the preceding sample uses memory buffer for uploading. For handling large file use streaming. See [Upload large files with streaming](/mvc/models/file-uploads#upload-large-files-with-streaming).
136+
121137
See the [StaticFileAuth](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/fundamentals/static-files/samples/9.x/StaticFileAuth) GitHub folder for the complete sample.
122138

123139
## Directory browsing
@@ -154,11 +170,11 @@ With `UseDefaultFiles`, requests to a folder in `wwwroot` search for:
154170
* `index.htm`
155171
* `index.html`
156172

157-
The first file found from the list is served as though the request included the file's name. The browser URL continues to reflect the URI requested.
173+
The first file found from the list is served as though the request included the file's name. The browser URL continues to reflect the URI requested. For example, in the [sample app](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs), a request to `https://localhost:<port>/def/` serves `default.html` from `wwwroot/def`.
158174

159175
The following code changes the default file name to `mydefault.html`:
160176

161-
[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_df2&highlight=16-19)]
177+
[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_df2&highlight=16-19)]
162178

163179
### UseFileServer for default documents
164180

@@ -182,9 +198,11 @@ Consider the following directory hierarchy:
182198
* `images`
183199
* `js`
184200
* `MyStaticFiles`
201+
* `defaultFiles`
202+
* `default.html`
203+
* `image3.png`
185204
* `images`
186205
* `MyImage.jpg`
187-
* `default.html`
188206

189207
The following code enables the serving of static files, the default file, and directory browsing of `MyStaticFiles`:
190208

@@ -199,7 +217,9 @@ Using the preceding file hierarchy and code, URLs resolve as follows:
199217
| URI | Response |
200218
| ------- | ------|
201219
| `https://<hostname>/StaticFiles/images/MyImage.jpg` | `MyStaticFiles/images/MyImage.jpg` |
202-
| `https://<hostname>/StaticFiles` | `MyStaticFiles/default.html` |
220+
| `https://<hostname>/StaticFiles` | directory listing |
221+
| `https://<hostname>/StaticFiles/defaultFiles` | `MyStaticFiles/defaultFiles/default.html` |
222+
| `https://<hostname>/StaticFiles/defaultFiles/image3.png` | `MyStaticFiles/defaultFiles//image3.png` |
203223

204224
If no default-named file exists in the *MyStaticFiles* directory, `https://<hostname>/StaticFiles` returns the directory listing with clickable links:
205225

@@ -209,7 +229,7 @@ If no default-named file exists in the *MyStaticFiles* directory, `https://<host
209229

210230
## FileExtensionContentTypeProvider
211231

212-
The <xref:Microsoft.AspNetCore.StaticFiles.FileExtensionContentTypeProvider> class contains a `Mappings` property that serves as a mapping of file extensions to MIME content types. In the following sample, several file extensions are mapped to known MIME types. The *.rtf* extension is replaced, and *.mp4* is removed:
232+
The <xref:Microsoft.AspNetCore.StaticFiles.FileExtensionContentTypeProvider> class contains a [Mappings](/dotnet/api/microsoft.aspnetcore.staticfiles.fileextensioncontenttypeprovider.mappings) property that serves as a mapping of file extensions to MIME content types. In the following sample, several file extensions are mapped to known MIME types. The *.rtf* extension is replaced, and *.mp4* is removed:
213233

214234
<!-- test via /mapTest/image1.image and mapTest/test.htm3 /mapTest/TextFile.rtf -->
215235
[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_fec&highlight=19-33)]
@@ -256,9 +276,9 @@ The following code updates the `WebRootFileProvider`, which enables the Image Ta
256276
### Security considerations for static files
257277

258278
> [!WARNING]
259-
> `UseDirectoryBrowser` and `UseStaticFiles` can leak secrets. Disabling directory browsing in production is highly recommended. Carefully review which directories are enabled via `UseStaticFiles` or `UseDirectoryBrowser`. The entire directory and its sub-directories become publicly accessible. Store files suitable for serving to the public in a dedicated directory, such as `<content_root>/wwwroot`. Separate these files from MVC views, Razor Pages, configuration files, etc.
279+
> `UseDirectoryBrowser` and `UseStaticFiles` <!-- but not MapStaticAssets --> can leak secrets. Disabling directory browsing in production is highly recommended. Carefully review which directories are enabled via `UseStaticFiles` or `UseDirectoryBrowser`. The entire directory and its sub-directories become publicly accessible. Store files suitable for serving to the public in a dedicated directory, such as `<content_root>/wwwroot`. Separate these files from MVC views, Razor Pages, configuration files, etc.
260280
261-
* The URLs for content exposed with `UseDirectoryBrowser` and `UseStaticFiles` are subject to the case sensitivity and character restrictions of the underlying file system. For example, Windows is case insensitive, but macOS and Linux aren't.
281+
* The URLs for content exposed with `UseDirectoryBrowser`, `UseStaticFiles`, and `MapStaticAssets` are subject to the case sensitivity and character restrictions of the underlying file system. For example, Windows is case insensitive, but macOS and Linux aren't.
262282

263283
* ASP.NET Core apps hosted in IIS use the [ASP.NET Core Module](xref:host-and-deploy/aspnet-core-module) to forward all requests to the app, including static file requests. The IIS static file handler isn't used and has no chance to handle requests.
264284

aspnetcore/fundamentals/static-files/samples/6.x/StaticFileAuth/Program.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#define DEFAULT // DEFAULT AUTH AUTH2
22
#if NEVER
33
#elif DEFAULT
4-
#region snippet
4+
// <snippet>
55
using Microsoft.AspNetCore.Identity;
66
using Microsoft.EntityFrameworkCore;
77
using StaticFileAuth.Data;
@@ -38,9 +38,9 @@
3838
app.MapRazorPages();
3939

4040
app.Run();
41-
#endregion
41+
// </snippet>
4242
#elif AUTH
43-
#region snippet_auth
43+
// <snippet_auth>
4444
using Microsoft.AspNetCore.Authorization;
4545
using Microsoft.AspNetCore.Identity;
4646
using Microsoft.EntityFrameworkCore;
@@ -95,9 +95,9 @@
9595
app.MapRazorPages();
9696

9797
app.Run();
98-
#endregion
98+
// </snippet_auth>
9999
#elif AUTH2
100-
#region snippet_auth2
100+
// <snippet_auth2>
101101
using Microsoft.AspNetCore.Authorization;
102102
using Microsoft.AspNetCore.Identity;
103103
using Microsoft.EntityFrameworkCore;
@@ -135,5 +135,5 @@
135135
app.MapRazorPages();
136136

137137
app.Run();
138-
#endregion
139-
#endif
138+
// </snippet_auth2>
139+
#endif

aspnetcore/fundamentals/static-files/samples/9.x/StaticFileAuth/Program.cs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,25 +131,29 @@ async Task SaveFileWithCustomFileName(IFormFile file, string fileSaveName)
131131

132132
if (File.Exists(filePath))
133133
{
134-
return TypedResults.PhysicalFile(filePath, fileDownloadName: $"{fileName}");
134+
return TypedResults.PhysicalFile(filePath, fileDownloadName: $"{fileName}");
135135
}
136136

137137
return TypedResults.NotFound("No file found with the supplied file name");
138138
})
139139
.WithName("GetFileByName")
140140
.RequireAuthorization("AuthenticatedUsers");
141141

142-
// IFormFile uses memory buffer for uploading. For handling large file use streaming instead.
143-
// https://learn.microsoft.com/aspnet/core/mvc/models/file-uploads#upload-large-files-with-streaming
144-
app.MapPost("/files", async (IFormFile file, LinkGenerator linker, HttpContext context) =>
142+
app.MapPost("/files",
143+
async (IFormFile file, LinkGenerator linker, HttpContext context) =>
145144
{
146-
// Don't rely on the file.FileName as it is only metadata that can be manipulated by the end-user
147-
// Take a look at the `Utilities.IsFileValid` method that takes an IFormFile and validates its signature within the AllowedFileSignatures
145+
// Don't rely on the file.FileName as it is only metadata that can be
146+
// manipulated by the end-user. See the `Utilities.IsFileValid` method that
147+
// takes an IFormFile and validates its signature within the
148+
// AllowedFileSignatures
148149

149-
var fileSaveName = Guid.NewGuid().ToString("N") + Path.GetExtension(file.FileName);
150+
var fileSaveName = Guid.NewGuid().ToString("N")
151+
+ Path.GetExtension(file.FileName);
150152
await SaveFileWithCustomFileName(file, fileSaveName);
151153

152-
context.Response.Headers.Append("Location", linker.GetPathByName(context, "GetFileByName", new { fileName = fileSaveName}));
154+
context.Response.Headers.Append("Location",
155+
linker.GetPathByName(context, "GetFileByName",
156+
new { fileName = fileSaveName}));
153157
return TypedResults.Ok("File Uploaded Successfully!");
154158
})
155159
.RequireAuthorization("AdminsOnly");

aspnetcore/fundamentals/static-files/samples/9.x/StaticFileAuth/StaticFilesAuth.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0-preview.2.23153.2" />
12-
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0-preview.2.23153.2" />
13-
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
11+
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.7" />
12+
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.7" />
13+
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
1414
</ItemGroup>
1515

1616
<ItemGroup>
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
<!DOCTYPE html>
1+
<!DOCTYPE html>
22
<html>
33
<head>
44
<meta charset="utf-8" />
55
<title>Default page</title>
66
</head>
77
<body>
88
<h1>This is the default page </h1>
9-
<b><i>default.html</i></b> in wwwroot. comment out @*@page*@ in Pages\Index.cshtml
9+
<b><i>default.html</i></b> in wwwroot/def comment out @*@page*@ in Pages\Index.cshtml
1010
</body>
11-
</html>
11+
</html>

aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/Pages/Privacy.cshtml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,17 @@
77

88
<p>RP: Use this page to detail your site's privacy policy.</p>
99

10+
<p>The following inmage doesn't display with app.UseStaticFiles(new StaticFileOptions </p>
1011
<img src="~/images/MyImage.jpg" class="img" alt="My image" asp-append-version="true" />
12+
<p> The following image requires:</p>
13+
<pre><code>
14+
app.UseStaticFiles(new StaticFileOptions //
15+
{
16+
FileProvider = new PhysicalFileProvider(
17+
Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")),
18+
RequestPath = "/StaticFiles"
19+
});
20+
</code></pre>
21+
22+
<img src="~/StaticFiles/images/red-rose.jpg" class="img" alt="A red rose"
23+
asp-append-version="true" />

0 commit comments

Comments
 (0)