diff --git a/aspnetcore/fundamentals/static-files.md b/aspnetcore/fundamentals/static-files.md index 6d0520649819..925052b03aeb 100644 --- a/aspnetcore/fundamentals/static-files.md +++ b/aspnetcore/fundamentals/static-files.md @@ -5,7 +5,7 @@ description: Learn how to serve and secure static files and configure static fil monikerRange: '>= aspnetcore-3.1' ms.author: riande ms.custom: mvc -ms.date: 7/4/2024 +ms.date: 7/25/2024 uid: fundamentals/static-files --- # Static files in ASP.NET Core @@ -24,7 +24,7 @@ Static files are stored within the project's [web root](xref:fundamentals/index# The method sets the content root to the current directory: -[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet&highlight=1)] +[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet&highlight=1,15)] 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: @@ -33,16 +33,28 @@ Static files are accessible via a path relative to the [web root](xref:fundament * `js` * `lib` -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:///images/`. For example, `https://localhost:5001/images/MyImage.jpg` +Consider an app with the `wwwroot/images/MyImage.jpg` file. The URI format to access a file in the `images` folder is `https:///images/`. For example, `https://localhost:5001/images/MyImage.jpg` ### MapStaticAssets -`MapStaticAssets` is a middleware that helps optimize the delivery of static assets in an app. For more information, see [Optimizing static web asset delivery +Creating performant web apps requires optimizing asset delivery to the browser. Possible optimizations include: + +* 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. +* 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. +* Set up proper [caching headers](https://developer.mozilla.org/docs/Web/HTTP/Headers/Cache-Control). +* Use [caching middleware](xref:performance/caching/middleware). +* Serve [compressed](/aspnet/core/performance/response-compression) versions of the assets when possible. +* Use a [CDN](/microsoft-365/enterprise/content-delivery-networks?view=o365-worldwide&preserve-view=true) to serve the assets closer to the user. +* Minimize the size of assets served to the browser. This optimization doesn't include minification. + +[`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. + +[`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 ](xref:aspnetcore-9#optimizing-static-web-asset-delivery). ### Serve files in web root -The default web app templates call the method in `Program.cs`, which enables static files to be served: +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: [!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet&highlight=15)] @@ -84,11 +96,11 @@ A object can be used to se [!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_rh&highlight=16-24)] -The preceding code makes static files publicly available in the local cache for one week (604800 seconds). +The preceding code makes static files publicly available in the local cache for one week. ## Static file authorization -The ASP.NET Core templates call before calling . Most apps follow this pattern. When the Static File Middleware is called before the authorization middleware: +The ASP.NET Core templates call [`MapStaticAssets`](/dotnet/api/microsoft.aspnetcore.builder.staticassetsendpointroutebuilderextensions.mapstaticassets) before calling . Most apps follow this pattern. When the Static File Middleware is called before the authorization middleware: * No authorization checks are performed on the static files. * 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: * Call `UseStaticFiles`, specifying a path, after calling `UseAuthorization`. * Set the [fallback authorization policy](xref:Microsoft.AspNetCore.Authorization.AuthorizationOptions.FallbackPolicy). -[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFileAuth/Program.cs?name=snippet_auth&highlight=18-23,38,45-50)] + +[!code-csharp[](~/fundamentals/static-files/samples/6.x/StaticFileAuth/Program.cs?name=snippet_auth&highlight=18-23,38,45-50)] 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. adds to the current instance, which enforces that the current user is authenticated. - 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. + 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. An alternative approach to serve files based on authorization is to: * Store them outside of `wwwroot` and any directory accessible to the Static File Middleware. * Serve them via an action method to which authorization is applied and return a object: + [!code-csharp[](~/fundamentals/static-files/samples/6.x/StaticFileAuth/Pages/BannerImage.cshtml.cs?name=snippet)] The preceding approach requires a page or endpoint per file. The following code returns files or uploads files for authenticated users: :::code language="csharp" source="~/fundamentals/static-files/samples/9.x/StaticFileAuth/Program.cs" id="snippet_1"::: +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). + 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. ## Directory browsing @@ -154,11 +170,11 @@ With `UseDefaultFiles`, requests to a folder in `wwwroot` search for: * `index.htm` * `index.html` -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. +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:/def/` serves `default.html` from `wwwroot/def`. The following code changes the default file name to `mydefault.html`: -[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_df2&highlight=16-19)] +[!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_df2&highlight=16-19)] ### UseFileServer for default documents @@ -182,9 +198,11 @@ Consider the following directory hierarchy: * `images` * `js` * `MyStaticFiles` + * `defaultFiles` + * `default.html` + * `image3.png` * `images` * `MyImage.jpg` - * `default.html` The following code enables the serving of static files, the default file, and directory browsing of `MyStaticFiles`: @@ -199,7 +217,9 @@ Using the preceding file hierarchy and code, URLs resolve as follows: | URI | Response | | ------- | ------| | `https:///StaticFiles/images/MyImage.jpg` | `MyStaticFiles/images/MyImage.jpg` | -| `https:///StaticFiles` | `MyStaticFiles/default.html` | +| `https:///StaticFiles` | directory listing | +| `https:///StaticFiles/defaultFiles` | `MyStaticFiles/defaultFiles/default.html` | +| `https:///StaticFiles/defaultFiles/image3.png` | `MyStaticFiles/defaultFiles//image3.png` | If no default-named file exists in the *MyStaticFiles* directory, `https:///StaticFiles` returns the directory listing with clickable links: @@ -209,7 +229,7 @@ If no default-named file exists in the *MyStaticFiles* directory, `https:// 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: +The 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: [!code-csharp[](~/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs?name=snippet_fec&highlight=19-33)] @@ -253,9 +273,9 @@ The following code updates the `WebRootFileProvider`, which enables the Image Ta ### Security considerations for static files > [!WARNING] -> `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 `/wwwroot`. Separate these files from MVC views, Razor Pages, configuration files, etc. +> `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 `/wwwroot`. Separate these files from MVC views, Razor Pages, configuration files, etc. -* 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. +* 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. * 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. diff --git a/aspnetcore/fundamentals/static-files/samples/6.x/StaticFileAuth/Program.cs b/aspnetcore/fundamentals/static-files/samples/6.x/StaticFileAuth/Program.cs index 6dc1e32d7e42..1ad457d86bba 100644 --- a/aspnetcore/fundamentals/static-files/samples/6.x/StaticFileAuth/Program.cs +++ b/aspnetcore/fundamentals/static-files/samples/6.x/StaticFileAuth/Program.cs @@ -1,7 +1,7 @@ #define DEFAULT // DEFAULT AUTH AUTH2 #if NEVER #elif DEFAULT -#region snippet +// using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using StaticFileAuth.Data; @@ -38,9 +38,9 @@ app.MapRazorPages(); app.Run(); -#endregion +// #elif AUTH -#region snippet_auth +// using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; @@ -95,9 +95,9 @@ app.MapRazorPages(); app.Run(); -#endregion +// #elif AUTH2 -#region snippet_auth2 +// using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; @@ -135,5 +135,5 @@ app.MapRazorPages(); app.Run(); -#endregion -#endif \ No newline at end of file +// +#endif diff --git a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFileAuth/Program.cs b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFileAuth/Program.cs index 078d4f88e781..ca0cf394cf50 100644 --- a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFileAuth/Program.cs +++ b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFileAuth/Program.cs @@ -131,7 +131,7 @@ async Task SaveFileWithCustomFileName(IFormFile file, string fileSaveName) if (File.Exists(filePath)) { - return TypedResults.PhysicalFile(filePath, fileDownloadName: $"{fileName}"); + return TypedResults.PhysicalFile(filePath, fileDownloadName: $"{fileName}"); } return TypedResults.NotFound("No file found with the supplied file name"); @@ -139,17 +139,21 @@ async Task SaveFileWithCustomFileName(IFormFile file, string fileSaveName) .WithName("GetFileByName") .RequireAuthorization("AuthenticatedUsers"); -// IFormFile uses memory buffer for uploading. For handling large file use streaming instead. -// https://learn.microsoft.com/aspnet/core/mvc/models/file-uploads#upload-large-files-with-streaming -app.MapPost("/files", async (IFormFile file, LinkGenerator linker, HttpContext context) => +app.MapPost("/files", + async (IFormFile file, LinkGenerator linker, HttpContext context) => { - // Don't rely on the file.FileName as it is only metadata that can be manipulated by the end-user - // Take a look at the `Utilities.IsFileValid` method that takes an IFormFile and validates its signature within the AllowedFileSignatures + // Don't rely on the file.FileName as it is only metadata that can be + // manipulated by the end-user. See the `Utilities.IsFileValid` method that + // takes an IFormFile and validates its signature within the + // AllowedFileSignatures - var fileSaveName = Guid.NewGuid().ToString("N") + Path.GetExtension(file.FileName); + var fileSaveName = Guid.NewGuid().ToString("N") + + Path.GetExtension(file.FileName); await SaveFileWithCustomFileName(file, fileSaveName); - context.Response.Headers.Append("Location", linker.GetPathByName(context, "GetFileByName", new { fileName = fileSaveName})); + context.Response.Headers.Append("Location", + linker.GetPathByName(context, "GetFileByName", + new { fileName = fileSaveName})); return TypedResults.Ok("File Uploaded Successfully!"); }) .RequireAuthorization("AdminsOnly"); diff --git a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFileAuth/StaticFilesAuth.csproj b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFileAuth/StaticFilesAuth.csproj index 2644e9e880c3..240b54d9cdff 100644 --- a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFileAuth/StaticFilesAuth.csproj +++ b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFileAuth/StaticFilesAuth.csproj @@ -8,9 +8,9 @@ - - - + + + diff --git a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/default.html b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/MyStaticFiles/defaultFiles/default.html similarity index 54% rename from aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/default.html rename to aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/MyStaticFiles/defaultFiles/default.html index 0e7651fcb76e..fe8894aa4252 100644 --- a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/default.html +++ b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/MyStaticFiles/defaultFiles/default.html @@ -1,4 +1,4 @@ - + @@ -6,6 +6,6 @@

This is the default page

- default.html in wwwroot. comment out @*@page*@ in Pages\Index.cshtml + default.html in wwwroot/def comment out @*@page*@ in Pages\Index.cshtml - \ No newline at end of file + diff --git a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/MyStaticFiles/defaultFiles/image3.png b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/MyStaticFiles/defaultFiles/image3.png new file mode 100644 index 000000000000..82eb991a4e4e Binary files /dev/null and b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/MyStaticFiles/defaultFiles/image3.png differ diff --git a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/Pages/Privacy.cshtml b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/Pages/Privacy.cshtml index 0baf7b951997..c9aa449da6a8 100644 --- a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/Pages/Privacy.cshtml +++ b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/Pages/Privacy.cshtml @@ -7,4 +7,17 @@

RP: Use this page to detail your site's privacy policy.

+

The following inmage doesn't display with app.UseStaticFiles(new StaticFileOptions

My image +

The following image requires:

+

+app.UseStaticFiles(new StaticFileOptions //
+{
+  FileProvider = new PhysicalFileProvider(
+      Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")),
+  RequestPath = "/StaticFiles"
+});
+
+ +A red rose diff --git a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs index bb4e9fac93b7..6bb507555e82 100644 --- a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs +++ b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/Program.cs @@ -1,4 +1,5 @@ -#define MULT2 // DEFAULT RR RH DB DF DF2 UFS UFS2 TREE FECTP NS MUL MULT2 +#define TREE // DEFAULT RR RH DB DF DF2 UFS UFS2 TREE FECTP NS MUL MULT2 +// Test1 #if NEVER #elif DEFAULT // @@ -16,12 +17,12 @@ } app.UseHttpsRedirection(); -app.UseStaticFiles(); +app.MapStaticAssets(); app.UseAuthorization(); -app.MapDefaultControllerRoute(); -app.MapRazorPages(); +app.MapDefaultControllerRoute().WithStaticAssets(); +app.MapRazorPages().WithStaticAssets(); app.Run(); // @@ -44,17 +45,18 @@ app.UseHttpsRedirection(); +app.UseStaticFiles(); //Serve files from wwwroot app.UseStaticFiles(new StaticFileOptions -{ - FileProvider = new PhysicalFileProvider( - Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")), - RequestPath = "/StaticFiles" -}); + { + FileProvider = new PhysicalFileProvider( + Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")), + RequestPath = "/StaticFiles" + }); app.UseAuthorization(); -app.MapDefaultControllerRoute(); -app.MapRazorPages(); +app.MapDefaultControllerRoute().WithStaticAssets(); +app.MapRazorPages().WithStaticAssets(); app.Run(); // @@ -75,20 +77,20 @@ app.UseHttpsRedirection(); -var cacheMaxAgeOneWeek = (60 * 60 * 24 * 7).ToString(); -app.UseStaticFiles(new StaticFileOptions -{ - OnPrepareResponse = ctx => - { - ctx.Context.Response.Headers.Append( - "Cache-Control", $"public, max-age={cacheMaxAgeOneWeek}"); - } -}); + var cacheMaxAgeOneWeek = (60 * 60 * 24 * 7).ToString(); + app.UseStaticFiles(new StaticFileOptions + { + OnPrepareResponse = ctx => + { + ctx.Context.Response.Headers.Append( + "Cache-Control", $"public, max-age={cacheMaxAgeOneWeek}"); + } + }); app.UseAuthorization(); -app.MapDefaultControllerRoute(); -app.MapRazorPages(); +app.MapDefaultControllerRoute().WithStaticAssets(); +app.MapRazorPages().WithStaticAssets(); app.Run(); // @@ -114,7 +116,7 @@ app.UseHttpsRedirection(); -app.UseStaticFiles(); +app.MapStaticAssets(); var fileProvider = new PhysicalFileProvider(Path.Combine(builder.Environment.WebRootPath, "images")); var requestPath = "/MyImages"; @@ -134,12 +136,13 @@ app.UseAuthorization(); -app.MapDefaultControllerRoute(); -app.MapRazorPages(); +app.MapDefaultControllerRoute().WithStaticAssets(); +app.MapRazorPages().WithStaticAssets(); app.Run(); // #elif DF // Default file +https://localhost:50921/def/ // var builder = WebApplication.CreateBuilder(args); @@ -161,12 +164,13 @@ app.UseStaticFiles(); app.UseAuthorization(); -app.MapDefaultControllerRoute(); -app.MapRazorPages(); +app.MapDefaultControllerRoute().WithStaticAssets(); +app.MapRazorPages().WithStaticAssets(); app.Run(); // #elif DF2 +// https://localhost:50921/def/ // var builder = WebApplication.CreateBuilder(args); @@ -192,8 +196,8 @@ app.UseAuthorization(); -app.MapDefaultControllerRoute(); -app.MapRazorPages(); +app.MapDefaultControllerRoute().WithStaticAssets(); +app.MapRazorPages().WithStaticAssets(); app.Run(); // @@ -218,8 +222,8 @@ app.UseAuthorization(); -app.MapDefaultControllerRoute(); -app.MapRazorPages(); +app.MapDefaultControllerRoute().WithStaticAssets(); +app.MapRazorPages().WithStaticAssets(); app.Run(); // @@ -248,8 +252,8 @@ app.UseAuthorization(); -app.MapDefaultControllerRoute(); -app.MapRazorPages(); +app.MapDefaultControllerRoute().WithStaticAssets(); +app.MapRazorPages().WithStaticAssets(); app.Run(); // @@ -286,8 +290,8 @@ app.UseAuthorization(); -app.MapDefaultControllerRoute(); -app.MapRazorPages(); +app.MapDefaultControllerRoute().WithStaticAssets(); +app.MapRazorPages().WithStaticAssets(); app.Run(); // @@ -329,8 +333,8 @@ app.UseAuthorization(); -app.MapDefaultControllerRoute(); -app.MapRazorPages(); +app.MapDefaultControllerRoute().WithStaticAssets(); +app.MapRazorPages().WithStaticAssets(); app.Run(); // @@ -351,16 +355,16 @@ app.UseHttpsRedirection(); -app.UseStaticFiles(new StaticFileOptions -{ - ServeUnknownFileTypes = true, - DefaultContentType = "image/png" -}); + app.UseStaticFiles(new StaticFileOptions + { + ServeUnknownFileTypes = true, + DefaultContentType = "image/png" + }); app.UseAuthorization(); -app.MapDefaultControllerRoute(); -app.MapRazorPages(); +app.MapDefaultControllerRoute().WithStaticAssets(); +app.MapRazorPages().WithStaticAssets(); app.Run(); // @@ -383,18 +387,18 @@ app.UseHttpsRedirection(); // -app.UseStaticFiles(); // Serve files from wwwroot -app.UseStaticFiles(new StaticFileOptions -{ - FileProvider = new PhysicalFileProvider( - Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")) -}); + app.UseStaticFiles(); + app.UseStaticFiles(new StaticFileOptions + { + FileProvider = new PhysicalFileProvider( + Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")) + }); // app.UseAuthorization(); -app.MapDefaultControllerRoute(); -app.MapRazorPages(); +app.MapDefaultControllerRoute().WithStaticAssets(); +app.MapRazorPages().WithStaticAssets(); app.Run(); #elif MULT2 @@ -426,14 +430,47 @@ // Update the default provider. app.Environment.WebRootFileProvider = compositeProvider; -app.UseStaticFiles(); +app.MapStaticAssets(); // app.UseAuthorization(); -app.MapDefaultControllerRoute(); -app.MapRazorPages(); +app.MapDefaultControllerRoute().WithStaticAssets(); +app.MapRazorPages().WithStaticAssets(); + +app.Run(); +#elif Test1 // no snippet to import, just for testing +using Microsoft.Extensions.FileProviders; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddRazorPages(); +builder.Services.AddControllersWithViews(); + +var app = builder.Build(); + +if (!app.Environment.IsDevelopment()) +{ + app.UseExceptionHandler("/Error"); + app.UseHsts(); +} + +app.UseHttpsRedirection(); + +app.UseStaticFiles(); // Serve files from wwwroot + +app.UseStaticFiles(new StaticFileOptions +{ + FileProvider = new PhysicalFileProvider( + Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")), + RequestPath = "/StaticFiles" +}); + +app.UseAuthorization(); + +app.MapDefaultControllerRoute().WithStaticAssets(); +app.MapRazorPages().WithStaticAssets(); app.Run(); #endif diff --git a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/StaticFilesSample.csproj b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/StaticFilesSample.csproj index 9e8317646abe..4e404415b4a3 100644 --- a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/StaticFilesSample.csproj +++ b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/StaticFilesSample.csproj @@ -1,7 +1,7 @@ - net6.0 + net9.0 enable enable diff --git a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/def/default.html b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/def/default.html new file mode 100644 index 000000000000..fe8894aa4252 --- /dev/null +++ b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/def/default.html @@ -0,0 +1,11 @@ + + + + + Default page + + +

This is the default page

+ default.html in wwwroot/def comment out @*@page*@ in Pages\Index.cshtml + + diff --git a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/index.html b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/def/index.html similarity index 54% rename from aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/index.html rename to aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/def/index.html index 538552f6c90c..6721b6699af8 100644 --- a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/index.html +++ b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/def/index.html @@ -1,10 +1,10 @@ - + Index -

This is the index page in wwwroot.

+

This is the index page in wwwroot/def

- \ No newline at end of file + diff --git a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/myIndex.html b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/def/myIndex.html similarity index 54% rename from aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/myIndex.html rename to aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/def/myIndex.html index f45bd38a2b11..7659dc28db95 100644 --- a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/myIndex.html +++ b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/def/myIndex.html @@ -1,10 +1,10 @@ - + myIndex -

This is the muyindex page in wwwroot.

+

This is the muyindex page in wwwroot/def

- \ No newline at end of file + diff --git a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/mydefault.html b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/def/mydefault.html similarity index 54% rename from aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/mydefault.html rename to aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/def/mydefault.html index 41db053a84f6..d5bef7f921a3 100644 --- a/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/mydefault.html +++ b/aspnetcore/fundamentals/static-files/samples/9.x/StaticFilesSample/wwwroot/def/mydefault.html @@ -1,10 +1,10 @@ - + myDefault -

This is the mydefault page in wwwroot.

+

This is the mydefault page in wwwroot/def

- \ No newline at end of file + diff --git a/aspnetcore/fundamentals/static-files/samples/9.x/WebRoot/Program.cs b/aspnetcore/fundamentals/static-files/samples/9.x/WebRoot/Program.cs index 59031d7ec791..32471a5c09cf 100644 --- a/aspnetcore/fundamentals/static-files/samples/9.x/WebRoot/Program.cs +++ b/aspnetcore/fundamentals/static-files/samples/9.x/WebRoot/Program.cs @@ -1,4 +1,4 @@ -#define FIRST // FIRST +#define SECOND // FIRST SECOND #if NEVER #elif FIRST // @@ -12,7 +12,7 @@ var app = builder.Build(); app.UseDefaultFiles(); -app.UseStaticFiles(); +app.MapStaticAssets(); app.Run(); // @@ -35,7 +35,7 @@ app.Environment.IsDevelopment().ToString()); app.UseDefaultFiles(); -app.UseStaticFiles(); +app.MapStaticAssets(); app.Run(); // diff --git a/aspnetcore/fundamentals/static-files/samples/9.x/WebRoot/WebRoot.csproj b/aspnetcore/fundamentals/static-files/samples/9.x/WebRoot/WebRoot.csproj index cae4bd2ba86e..99769afd6147 100644 --- a/aspnetcore/fundamentals/static-files/samples/9.x/WebRoot/WebRoot.csproj +++ b/aspnetcore/fundamentals/static-files/samples/9.x/WebRoot/WebRoot.csproj @@ -1,7 +1,7 @@ - net6.0 + net9.0 enable enable diff --git a/aspnetcore/release-notes/aspnetcore-9/includes/web_asset_delivery.md b/aspnetcore/release-notes/aspnetcore-9/includes/web_asset_delivery.md index dff1b1936961..69636d34ed7d 100644 --- a/aspnetcore/release-notes/aspnetcore-9/includes/web_asset_delivery.md +++ b/aspnetcore/release-notes/aspnetcore-9/includes/web_asset_delivery.md @@ -16,7 +16,7 @@ Creating performant web apps requires optimizing asset delivery to the browser. * Use a [CDN](/microsoft-365/enterprise/content-delivery-networks?view=o365-worldwide&preserve-view=true) to serve the assets closer to the user. * Minimize the size of assets served to the browser. This optimization doesn't include minification. -[`MapStaticAssets`](https://source.dot.net/#Microsoft.AspNetCore.StaticAssets/StaticAssetsEndpointRouteBuilderExtensions.cs,18) is a new 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. It's typically a drop-in replacement for [UseStaticFiles](/dotnet/api/microsoft.aspnetcore.builder.staticfileextensions.usestaticfiles): +[`MapStaticAssets`](/dotnet/api/microsoft.aspnetcore.builder.staticassetsendpointroutebuilderextensions.mapstaticassets) is a new 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. It's typically a drop-in replacement for [UseStaticFiles](/dotnet/api/microsoft.aspnetcore.builder.staticfileextensions.usestaticfiles): ```diff var builder = WebApplication.CreateBuilder(args); @@ -46,7 +46,7 @@ app.Run(); `MapStaticAssets` operates by combining build and publish-time processes to collect information about all the static resources in an app. This information is then utilized by the runtime library to efficiently serve these files to the browser. -`MapStaticAssets` can replace `UseStaticFiles` in most situations, however, it's optimized for serving the assets that the app has knowledge of at build and publish time. If the app serves assets from other locations, such as disk or embedded resources, `UseStaticFiles` should be used. +`MapStaticAssets` can replace [`UseStaticFiles`](/dotnet/api/microsoft.aspnetcore.builder.staticfileextensions.usestaticfiles) in most situations, however, it's optimized for serving the assets that the app has knowledge of at build and publish time. If the app serves assets from other locations, such as disk or embedded resources, `UseStaticFiles` should be used. `MapStaticAssets` provides the following benefits not found with `UseStaticFiles`: