作者: Rick Anderson

默认情况下,静态文件(如 HTML、CSS、图像和 JavaScript)是 ASP.NET Core 应用直接提供给客户端的资产。

提供静态文件

静态文件存储在项目的 Web 根 目录中。 默认目录为 {content root}/wwwroot ,但可通过 UseWebRoot 方法更改目录。 有关详细信息,请参阅 内容根目录 Web 根目录

采用 CreateBuilder 方法可将内容根目录设置为当前目录:

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();
app.UseAuthorization();
app.MapDefaultControllerRoute();
app.MapRazorPages();
app.Run();

可通过 Web 根目录的相关路径访问静态文件。 例如,Web 应用程序项目模板包含 wwwroot 文件夹中的多个文件夹:

  • wwwroot

    请考虑创建 wwwroot/images 文件夹,并添加 wwwroot/images/MyImage.jpg 文件。 用于访问 images 文件夹中的文件的 URI 格式为 https://<hostname>/images/<image_file_name>。 例如,https://localhost:5001/images/MyImage.jpg

    在 Web 根目录中提供文件

    默认 Web 应用模板在 Program.cs 中调用 UseStaticFiles 方法,这将允许提供静态文件:

    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();
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    无参数 UseStaticFiles 方法重载将 Web 根目录中的文件标记为可用。 以下标记引用 wwwroot/images/MyImage.jpg

    <img src="~/images/MyImage.jpg" class="img" alt="My image" />
    

    在上面的代码标记中,波形符 ~ 指向 Web 根目录

    提供 Web 根目录外的文件

    考虑一个目录层次结构,其中要提供的静态文件位于 Web 根目录之外:

  • wwwroot
  • images
  • MyStaticFiles
  • images
  • red-rose.jpg
  • 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(new StaticFileOptions
        FileProvider = new PhysicalFileProvider(
               Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")),
        RequestPath = "/StaticFiles"
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    在前面的代码中,MyStaticFiles 目录层次结构通过 StaticFiles URI 段公开 。 对 https://<hostname>/StaticFiles/images/red-rose.jpg 的请求将提供 red-rose.jpg 文件。

    以下标记引用 MyStaticFiles/images/red-rose.jpg

    <img src="~/StaticFiles/images/red-rose.jpg" class="img" alt="A red rose" />
    

    若要从多个位置提供文件,请参阅从多个位置提供文件

    设置 HTTP 响应标头

    StaticFileOptions 对象可用于设置 HTTP 响应标头。 除配置从 Web 根目录提供静态文件外,以下代码还设置 标头:

    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();
    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.Run();
    

    上述代码使静态文件在本地缓存中公开可用一周(604800 秒)。

    静态文件授权

    ASP.NET Core 模板在调用 UseAuthorization 之前调用 UseStaticFiles。 大多数应用都遵循此模式。 如果在授权中间件之前调用静态文件中间件:

  • 不会对静态文件执行任何授权检查。
  • 由静态文件中间件提供的静态文件(例如 wwwroot 下的文件)可公开访问。
  • 根据授权提供静态文件:

  • 将它们存储在 wwwroot 之外。
  • 调用 UseAuthorization 之后调用 UseStaticFiles,以指定路径。
  • 设置回退授权策略
  • using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.FileProviders;
    using StaticFileAuth.Data;
    var builder = WebApplication.CreateBuilder(args);
    var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
    builder.Services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(connectionString));
    builder.Services.AddDatabaseDeveloperPageExceptionFilter();
    builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    builder.Services.AddRazorPages();
    builder.Services.AddAuthorization(options =>
        options.FallbackPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();
    var app = builder.Build();
    if (app.Environment.IsDevelopment())
        app.UseMigrationsEndPoint();
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();
    app.UseStaticFiles(new StaticFileOptions
        FileProvider = new PhysicalFileProvider(
               Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")),
        RequestPath = "/StaticFiles"
    app.MapRazorPages();
    app.Run();
    

    在前面的代码中,回退授权策略要求所有用户进行身份验证。 用于指定其自己的授权要求的终结点(如控制器、Razor Pages 等)不使用回退授权策略。 例如,具有 [AllowAnonymous][Authorize(PolicyName="MyPolicy")] 的 Razor Pages、控制器或操作方法使用应用的授权属性,而不是回退授权策略。

    RequireAuthenticatedUserDenyAnonymousAuthorizationRequirement 添加到当前实例,这将强制对当前用户进行身份验证。

    wwwroot 下的静态资产是可公开访问的,因为在 UseAuthentication 之前会调用默认静态文件中间件 (app.UseStaticFiles();)。 MyStaticFiles 文件夹中的静态资产需要身份验证示例代码对此进行了演示。

    还有一种根据授权提供文件的方法是:

  • 将文件存储在 wwwroot 和静态文件中间件可访问的任何目录之外。
  • 通过应用授权的操作方法为其提供服务,并返回 FileResult 对象:
  • [Authorize]
    public class BannerImageModel : PageModel
        private readonly IWebHostEnvironment _env;
        public BannerImageModel(IWebHostEnvironment env) =>
            _env = env;
        public PhysicalFileResult OnGet()
            var filePath = Path.Combine(
                    _env.ContentRootPath, "MyStaticFiles", "images", "red-rose.jpg");
            return PhysicalFile(filePath, "image/jpeg");
    

    目录浏览允许在指定目录中列出目录。

    出于安全考虑,目录浏览默认处于禁用状态。 有关详细信息,请参阅静态文件的安全注意事项

    通过 AddDirectoryBrowserUseDirectoryBrowser 启用目录浏览:

    using Microsoft.AspNetCore.StaticFiles;
    using Microsoft.Extensions.FileProviders;
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddRazorPages();
    builder.Services.AddControllersWithViews();
    builder.Services.AddDirectoryBrowser();
    var app = builder.Build();
    if (!app.Environment.IsDevelopment())
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    var fileProvider = new PhysicalFileProvider(Path.Combine(builder.Environment.WebRootPath, "images"));
    var requestPath = "/MyImages";
    // Enable displaying browser links.
    app.UseStaticFiles(new StaticFileOptions
        FileProvider = fileProvider,
        RequestPath = requestPath
    app.UseDirectoryBrowser(new DirectoryBrowserOptions
        FileProvider = fileProvider,
        RequestPath = requestPath
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    上述代码允许使用 URL https://<hostname>/MyImages 浏览 wwwroot/images 文件夹的目录,并链接到每个文件和文件夹:

    AddDirectoryBrowser添加目录浏览中间件所需的服务,包括 HtmlEncoder。 这些服务可以通过其他调用(例如 AddRazorPages)添加,但我们建议调用 AddDirectoryBrowser 以确保将服务添加到所有应用中。

    提供默认文档

    设置默认页面为访问者提供网站的起点。 若要从 wwwroot 提供默认文件,而不要求请求 URL 包含文件名,请调用 UseDefaultFiles 方法:

    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.UseDefaultFiles();
    app.UseStaticFiles();
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    要提供默认文件,必须在 UseStaticFiles 前调用 UseDefaultFilesUseDefaultFiles 是不提供文件的 URL 重写工具。

    使用 UseDefaultFiles 请求对 wwwroot 中的文件夹搜索:

  • default.htm
  • default.html
  • index.htm
  • index.html
  • 如同请求包含了文件名一样,提供从列表中找到的第一个文件。 浏览器 URL 继续反映请求的 URI。

    以下代码将默认文件名更改为 mydefault.html

    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();
    var options = new DefaultFilesOptions();
    options.DefaultFileNames.Clear();
    options.DefaultFileNames.Add("mydefault.html");
    app.UseDefaultFiles(options);
    app.UseStaticFiles();
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    默认文档的 UseFileServer

    UseFileServer 结合了 UseStaticFilesUseDefaultFilesUseDirectoryBrowser(可选)的功能。

    调用 app.UseFileServer 以提供静态文件和默认文件。 未启用目录浏览:

    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.UseFileServer();
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    以下代码提供静态文件、默认文件和目录浏览:

    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddRazorPages();
    builder.Services.AddControllersWithViews();
    builder.Services.AddDirectoryBrowser();
    var app = builder.Build();
    if (!app.Environment.IsDevelopment())
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    app.UseHttpsRedirection();
    app.UseFileServer(enableDirectoryBrowsing: true);
    app.UseRouting();
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    考虑以下目录层次结构:

  • wwwroot
  • images
  • MyStaticFiles
  • images
  • MyImage.jpg
  • default.html
  • 以下代码提供静态文件、默认文件和 MyStaticFiles 的目录浏览:

    using Microsoft.Extensions.FileProviders;
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddRazorPages();
    builder.Services.AddControllersWithViews();
    builder.Services.AddDirectoryBrowser();
    var app = builder.Build();
    if (!app.Environment.IsDevelopment())
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseFileServer(new FileServerOptions
        FileProvider = new PhysicalFileProvider(
               Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")),
        RequestPath = "/StaticFiles",
        EnableDirectoryBrowsing = true
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    必须在 EnableDirectoryBrowsing 属性值为 true 时调用 AddDirectoryBrowser

    使用前面的文件层次结构和代码,URL 解析如下:

    如果 MyStaticFiles 目录中不存在默认命名文件,则 https://<hostname>/StaticFiles 返回包含可单击链接的目录列表:

    UseDefaultFilesUseDirectoryBrowser 执行从不带尾部 / 的目标 URI 到带尾部 / 的目标 URI 的客户端重定向。 例如,从 https://<hostname>/StaticFileshttps://<hostname>/StaticFiles/。 如果没有尾随斜杠 (/),StaticFiles 目录中的相对 URL 无效,除非使用 DefaultFilesOptionsRedirectToAppendTrailingSlash 选项。

    FileExtensionContentTypeProvider

    FileExtensionContentTypeProvider 类包含 Mappings 属性,用作文件扩展名到 MIME 内容类型的映射。 在以下示例中,多个文件扩展名映射到了已知的 MIME 类型。 替换了 .rtf 扩展名,删除了 .mp4 :

    using Microsoft.AspNetCore.StaticFiles;
    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();
    // Set up custom content types - associating file extension to MIME type
    var provider = new FileExtensionContentTypeProvider();
    // Add new mappings
    provider.Mappings[".myapp"] = "application/x-msdownload";
    provider.Mappings[".htm3"] = "text/html";
    provider.Mappings[".image"] = "image/png";
    // Replace an existing mapping
    provider.Mappings[".rtf"] = "application/x-msdownload";
    // Remove MP4 videos.
    provider.Mappings.Remove(".mp4");
    app.UseStaticFiles(new StaticFileOptions
        ContentTypeProvider = provider
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    请参阅 MIME 内容类型

    非标准内容类型

    静态文件中间件可理解近 400 种已知文件内容类型。 如果用户请求文件类型未知的文件,则静态文件中间件将请求传递给管道中的下一个中间件。 如果没有中间件处理请求,则返回“404 未找到”响应。 如果启用了目录浏览,则在目录列表中会显示该文件的链接。

    以下代码提供未知类型,并以图像形式呈现未知文件:

    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(new StaticFileOptions
        ServeUnknownFileTypes = true,
        DefaultContentType = "image/png"
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    使用前面的代码,请求的文件含未知内容类型时,以图像形式返回请求。

    启用 ServeUnknownFileTypes 会形成安全隐患。 它默认处于禁用状态,不建议使用。 FileExtensionContentTypeProvider 提供了更安全的替代方法来提供含非标准扩展名的文件。

    从多个位置提供文件

    请考虑以下显示 /MyStaticFiles/image3.png 文件的 Razor 页面:

    @page
    <p> Test /MyStaticFiles/image3.png</p>
    <img src="~/image3.png" class="img" asp-append-version="true" alt="Test">
    

    UseStaticFilesUseFileServer 默认为指向 wwwroot 的文件提供程序。 可使用其他文件提供程序提供 UseStaticFilesUseFileServer 的其他实例,从多个位置提供文件。 以下示例调用 UseStaticFiles 两次以提供来自 wwwrootMyStaticFiles 的文件:

    app.UseStaticFiles(); // Serve files from wwwroot
    app.UseStaticFiles(new StaticFileOptions
        FileProvider = new PhysicalFileProvider(
            Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles"))
    

    使用上述代码:

  • 将显示 /MyStaticFiles/image3.png 文件。
  • 由于标记帮助程序依赖于 WebRootFileProvider,因此未应用图像标记帮助程序AppendVersionWebRootFileProvider 尚未更新为包含 MyStaticFiles 文件夹。
  • 以下代码会更新 WebRootFileProvider,使图像标记帮助程序能够提供版本:

    var webRootProvider = new PhysicalFileProvider(builder.Environment.WebRootPath);
    var newPathProvider = new PhysicalFileProvider(
      Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles"));
    var compositeProvider = new CompositeFileProvider(webRootProvider,
                                                      newPathProvider);
    // Update the default provider.
    app.Environment.WebRootFileProvider = compositeProvider;
    app.UseStaticFiles();
    

    静态文件的安全注意事项

    UseDirectoryBrowserUseStaticFiles 可能会泄漏机密。 强烈建议在生产中禁用目录浏览。 请仔细查看 UseStaticFilesUseDirectoryBrowser 启用了哪些目录。 整个目录及其子目录均可公开访问。 将适合公开的文件存储在专用目录中,如 <content_root>/wwwroot。 将这些文件与 MVC 视图、Razor Pages 和配置文件等分开。

  • 使用 UseDirectoryBrowserUseStaticFiles 公开的内容的 URL 受大小写和基础文件系统字符限制的影响。 例如,Windows 不区分大小写,但 macOS 和 Linux 区分大小写。

  • 托管于 IIS 中的 ASP.NET Core 应用使用 ASP.NET Core 模块将所有请求转发到应用,包括静态文件请求。 不使用 IIS 静态文件处理程序,并且没有机会处理请求。

  • 在 IIS Manager 中完成以下步骤,删除服务器或网站级别的 IIS 静态文件处理程序:

  • 转到“模块”功能。
  • 在列表中选择 StaticFileModule。
  • 单击“操作”侧栏中的“删除” 。
  • 将代码文件(包括 .cs.cshtml)放在应用项目的 Web 根目录之外。 这样就在应用的客户端内容和基于服务器的代码间创建了逻辑分隔。 可以防止服务器端代码泄漏。
  • 通过更新 IWebHostEnvironment.WebRootPath 提供 wwwroot 外的文件

    IWebHostEnvironment.WebRootPath 设置为 wwwroot 以外的文件夹时:

  • 在开发环境中,在 wwwroot 和更新的 IWebHostEnvironment.WebRootPath 中发现的静态资产由 wwwroot 提供。
  • 在开发环境以外的任何环境中,重复的静态资产会从更新的 IWebHostEnvironment.WebRootPath 文件夹提供。
  • 请考虑使用空 Web 模板创建的 Web 应用:

  • wwwrootwwwroot-custom 中包含一个 Index.html 文件。

  • 使用以下设置了 WebRootPath = "wwwroot-custom" 的已更新 Program.cs 文件:

    var builder = WebApplication.CreateBuilder(new WebApplicationOptions
        Args = args,
        // Look for static files in "wwwroot-custom"
        WebRootPath = "wwwroot-custom"
    var app = builder.Build();
    app.UseDefaultFiles();
    app.UseStaticFiles();
    app.Run();
    

    在前面的代码中,请求 /

  • 在开发环境中,返回 wwwroot/Index.html
  • 在开发环境以外的任何环境中,返回 wwwroot-custom/Index.html
  • 若要确保返回 wwwroot-custom 中的资产,请使用以下方法之一:

  • wwwroot 中删除重复的命名资产。

  • Properties/launchSettings.json 中的 "ASPNETCORE_ENVIRONMENT" 设置为 "Development" 以外的任何值。

  • 通过在项目文件中设置 <StaticWebAssetsEnabled>false</StaticWebAssetsEnabled>,完全禁用静态 Web 资产。 警告,禁用静态 Web 资产会禁用 Razor 类库

  • 将以下 JSON 添加到项目文件:

    <ItemGroup>
        <Content Remove="wwwroot\**" />
    </ItemGroup>
    

    以下代码将 IWebHostEnvironment.WebRootPath 更新为非开发值,从而保证从 wwwroot-custom(而不是 wwwroot)返回重复内容:

    var builder = WebApplication.CreateBuilder(new WebApplicationOptions
        Args = args,
        // Examine Hosting environment: logging value
        EnvironmentName = Environments.Staging,
        WebRootPath = "wwwroot-custom"
    var app = builder.Build();
    app.Logger.LogInformation("ASPNETCORE_ENVIRONMENT: {env}",
          Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"));
    app.Logger.LogInformation("app.Environment.IsDevelopment(): {env}",
          app.Environment.IsDevelopment().ToString());
    app.UseDefaultFiles();
    app.UseStaticFiles();
    app.Run();
    
  • 查看或下载示例代码如何下载
  • ASP.NET Core 简介
  • 作者:Rick AndersonKirk Larkin

    默认情况下,静态文件(如 HTML、CSS、图像和 JavaScript)是 ASP.NET Core 应用直接提供给客户端的资产。

    提供静态文件

    静态文件存储在项目的 Web 根目录中。 默认目录为 {content root}/wwwroot,但可通过 UseWebRoot 方法更改目录。 有关详细信息,请参阅内容根目录Web 根目录

    采用 CreateBuilder 方法可将内容根目录设置为当前目录:

    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();
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    可通过 Web 根目录的相关路径访问静态文件。 例如,Web 应用程序项目模板包含 wwwroot 文件夹中的多个文件夹:

  • wwwroot

    请考虑创建 wwwroot/images 文件夹,并添加 wwwroot/images/MyImage.jpg 文件。 用于访问 images 文件夹中的文件的 URI 格式为 https://<hostname>/images/<image_file_name>。 例如,https://localhost:5001/images/MyImage.jpg

    在 Web 根目录中提供文件

    默认 Web 应用模板在 Program.cs 中调用 UseStaticFiles 方法,这将允许提供静态文件:

    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();
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    无参数 UseStaticFiles 方法重载将 Web 根目录中的文件标记为可用。 以下标记引用 wwwroot/images/MyImage.jpg

    <img src="~/images/MyImage.jpg" class="img" alt="My image" />
    

    在上面的代码标记中,波形符 ~ 指向 Web 根目录

    提供 Web 根目录外的文件

    考虑一个目录层次结构,其中要提供的静态文件位于 Web 根目录之外:

  • wwwroot
  • images
  • MyStaticFiles
  • images
  • red-rose.jpg
  • 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(new StaticFileOptions
        FileProvider = new PhysicalFileProvider(
               Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")),
        RequestPath = "/StaticFiles"
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    在前面的代码中,MyStaticFiles 目录层次结构通过 StaticFiles URI 段公开 。 对 https://<hostname>/StaticFiles/images/red-rose.jpg 的请求将提供 red-rose.jpg 文件。

    以下标记引用 MyStaticFiles/images/red-rose.jpg

    <img src="~/StaticFiles/images/red-rose.jpg" class="img" alt="A red rose" />
    

    若要从多个位置提供文件,请参阅从多个位置提供文件

    设置 HTTP 响应标头

    StaticFileOptions 对象可用于设置 HTTP 响应标头。 除配置从 Web 根目录提供静态文件外,以下代码还设置 标头:

    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();
    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.Run();
    

    上述代码使静态文件在本地缓存中公开可用一周(604800 秒)。

    静态文件授权

    ASP.NET Core 模板在调用 UseAuthorization 之前调用 UseStaticFiles。 大多数应用都遵循此模式。 如果在授权中间件之前调用静态文件中间件:

  • 不会对静态文件执行任何授权检查。
  • 由静态文件中间件提供的静态文件(例如 wwwroot 下的文件)可公开访问。
  • 根据授权提供静态文件:

  • 将它们存储在 wwwroot 之外。
  • 调用 UseAuthorization 之后调用 UseStaticFiles,以指定路径。
  • 设置回退授权策略
  • using Microsoft.AspNetCore.Authorization;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.FileProviders;
    using StaticFileAuth.Data;
    var builder = WebApplication.CreateBuilder(args);
    var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
    builder.Services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(connectionString));
    builder.Services.AddDatabaseDeveloperPageExceptionFilter();
    builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    builder.Services.AddRazorPages();
    builder.Services.AddAuthorization(options =>
        options.FallbackPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();
    var app = builder.Build();
    if (app.Environment.IsDevelopment())
        app.UseMigrationsEndPoint();
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();
    app.UseStaticFiles(new StaticFileOptions
        FileProvider = new PhysicalFileProvider(
               Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")),
        RequestPath = "/StaticFiles"
    app.MapRazorPages();
    app.Run();
    

    在前面的代码中,回退授权策略要求所有用户进行身份验证。 用于指定其自己的授权要求的终结点(如控制器、Razor Pages 等)不使用回退授权策略。 例如,具有 [AllowAnonymous][Authorize(PolicyName="MyPolicy")] 的 Razor Pages、控制器或操作方法使用应用的授权属性,而不是回退授权策略。

    RequireAuthenticatedUserDenyAnonymousAuthorizationRequirement 添加到当前实例,这将强制对当前用户进行身份验证。

    wwwroot 下的静态资产是可公开访问的,因为在 UseAuthentication 之前会调用默认静态文件中间件 (app.UseStaticFiles();)。 MyStaticFiles 文件夹中的静态资产需要身份验证示例代码对此进行了演示。

    还有一种根据授权提供文件的方法是:

  • 将文件存储在 wwwroot 和静态文件中间件可访问的任何目录之外。
  • 通过应用授权的操作方法为其提供服务,并返回 FileResult 对象:
  • [Authorize]
    public class BannerImageModel : PageModel
        private readonly IWebHostEnvironment _env;
        public BannerImageModel(IWebHostEnvironment env) =>
            _env = env;
        public PhysicalFileResult OnGet()
            var filePath = Path.Combine(
                    _env.ContentRootPath, "MyStaticFiles", "images", "red-rose.jpg");
            return PhysicalFile(filePath, "image/jpeg");
    

    目录浏览允许在指定目录中列出目录。

    出于安全考虑,目录浏览默认处于禁用状态。 有关详细信息,请参阅静态文件的安全注意事项

    通过 AddDirectoryBrowserUseDirectoryBrowser 启用目录浏览:

    using Microsoft.AspNetCore.StaticFiles;
    using Microsoft.Extensions.FileProviders;
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddRazorPages();
    builder.Services.AddControllersWithViews();
    builder.Services.AddDirectoryBrowser();
    var app = builder.Build();
    if (!app.Environment.IsDevelopment())
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    var fileProvider = new PhysicalFileProvider(Path.Combine(builder.Environment.WebRootPath, "images"));
    var requestPath = "/MyImages";
    // Enable displaying browser links.
    app.UseStaticFiles(new StaticFileOptions
        FileProvider = fileProvider,
        RequestPath = requestPath
    app.UseDirectoryBrowser(new DirectoryBrowserOptions
        FileProvider = fileProvider,
        RequestPath = requestPath
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    上述代码允许使用 URL https://<hostname>/MyImages 浏览 wwwroot/images 文件夹的目录,并链接到每个文件和文件夹:

    AddDirectoryBrowser添加目录浏览中间件所需的服务,包括 HtmlEncoder。 这些服务可以通过其他调用(例如 AddRazorPages)添加,但我们建议调用 AddDirectoryBrowser 以确保将服务添加到所有应用中。

    提供默认文档

    设置默认页面为访问者提供网站的起点。 若要从 wwwroot 提供默认文件,而不要求请求 URL 包含文件名,请调用 UseDefaultFiles 方法:

    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.UseDefaultFiles();
    app.UseStaticFiles();
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    要提供默认文件,必须在 UseStaticFiles 前调用 UseDefaultFilesUseDefaultFiles 是不提供文件的 URL 重写工具。

    使用 UseDefaultFiles 请求对 wwwroot 中的文件夹搜索:

  • default.htm
  • default.html
  • index.htm
  • index.html
  • 如同请求包含了文件名一样,提供从列表中找到的第一个文件。 浏览器 URL 继续反映请求的 URI。

    以下代码将默认文件名更改为 mydefault.html

    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();
    var options = new DefaultFilesOptions();
    options.DefaultFileNames.Clear();
    options.DefaultFileNames.Add("mydefault.html");
    app.UseDefaultFiles(options);
    app.UseStaticFiles();
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    默认文档的 UseFileServer

    UseFileServer 结合了 UseStaticFilesUseDefaultFilesUseDirectoryBrowser(可选)的功能。

    调用 app.UseFileServer 以提供静态文件和默认文件。 未启用目录浏览:

    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.UseFileServer();
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    以下代码提供静态文件、默认文件和目录浏览:

    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddRazorPages();
    builder.Services.AddControllersWithViews();
    builder.Services.AddDirectoryBrowser();
    var app = builder.Build();
    if (!app.Environment.IsDevelopment())
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    app.UseHttpsRedirection();
    app.UseFileServer(enableDirectoryBrowsing: true);
    app.UseRouting();
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    考虑以下目录层次结构:

  • wwwroot
  • images
  • MyStaticFiles
  • images
  • MyImage.jpg
  • default.html
  • 以下代码提供静态文件、默认文件和 MyStaticFiles 的目录浏览:

    using Microsoft.Extensions.FileProviders;
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddRazorPages();
    builder.Services.AddControllersWithViews();
    builder.Services.AddDirectoryBrowser();
    var app = builder.Build();
    if (!app.Environment.IsDevelopment())
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseFileServer(new FileServerOptions
        FileProvider = new PhysicalFileProvider(
               Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")),
        RequestPath = "/StaticFiles",
        EnableDirectoryBrowsing = true
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    必须在 EnableDirectoryBrowsing 属性值为 true 时调用 AddDirectoryBrowser

    使用前面的文件层次结构和代码,URL 解析如下:

    如果 MyStaticFiles 目录中不存在默认命名文件,则 https://<hostname>/StaticFiles 返回包含可单击链接的目录列表:

    UseDefaultFilesUseDirectoryBrowser 执行从不带尾部 / 的目标 URI 到带尾部 / 的目标 URI 的客户端重定向。 例如,从 https://<hostname>/StaticFileshttps://<hostname>/StaticFiles/。 如果没有尾随斜杠 (/),StaticFiles 目录中的相对 URL 无效,除非使用 DefaultFilesOptionsRedirectToAppendTrailingSlash 选项。

    FileExtensionContentTypeProvider

    FileExtensionContentTypeProvider 类包含 Mappings 属性,用作文件扩展名到 MIME 内容类型的映射。 在以下示例中,多个文件扩展名映射到了已知的 MIME 类型。 替换了 .rtf 扩展名,删除了 .mp4 :

    using Microsoft.AspNetCore.StaticFiles;
    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();
    // Set up custom content types - associating file extension to MIME type
    var provider = new FileExtensionContentTypeProvider();
    // Add new mappings
    provider.Mappings[".myapp"] = "application/x-msdownload";
    provider.Mappings[".htm3"] = "text/html";
    provider.Mappings[".image"] = "image/png";
    // Replace an existing mapping
    provider.Mappings[".rtf"] = "application/x-msdownload";
    // Remove MP4 videos.
    provider.Mappings.Remove(".mp4");
    app.UseStaticFiles(new StaticFileOptions
        ContentTypeProvider = provider
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    请参阅 MIME 内容类型

    非标准内容类型

    静态文件中间件可理解近 400 种已知文件内容类型。 如果用户请求文件类型未知的文件,则静态文件中间件将请求传递给管道中的下一个中间件。 如果没有中间件处理请求,则返回“404 未找到”响应。 如果启用了目录浏览,则在目录列表中会显示该文件的链接。

    以下代码提供未知类型,并以图像形式呈现未知文件:

    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(new StaticFileOptions
        ServeUnknownFileTypes = true,
        DefaultContentType = "image/png"
    app.UseAuthorization();
    app.MapDefaultControllerRoute();
    app.MapRazorPages();
    app.Run();
    

    使用前面的代码,请求的文件含未知内容类型时,以图像形式返回请求。

    启用 ServeUnknownFileTypes 会形成安全隐患。 它默认处于禁用状态,不建议使用。 FileExtensionContentTypeProvider 提供了更安全的替代方法来提供含非标准扩展名的文件。

    从多个位置提供文件

    请考虑以下显示 /MyStaticFiles/image3.png 文件的 Razor 页面:

    @page
    <p> Test /MyStaticFiles/image3.png</p>
    <img src="~/image3.png" class="img" asp-append-version="true" alt="Test">
    

    UseStaticFilesUseFileServer 默认为指向 wwwroot 的文件提供程序。 可使用其他文件提供程序提供 UseStaticFilesUseFileServer 的其他实例,从多个位置提供文件。 以下示例调用 UseStaticFiles 两次以提供来自 wwwrootMyStaticFiles 的文件:

    app.UseStaticFiles(); // Serve files from wwwroot
    app.UseStaticFiles(new StaticFileOptions
        FileProvider = new PhysicalFileProvider(
            Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles"))
    

    使用上述代码:

  • 将显示 /MyStaticFiles/image3.png 文件。
  • 由于标记帮助程序依赖于 WebRootFileProvider,因此未应用图像标记帮助程序AppendVersionWebRootFileProvider 尚未更新为包含 MyStaticFiles 文件夹。
  • 以下代码会更新 WebRootFileProvider,使图像标记帮助程序能够提供版本:

    var webRootProvider = new PhysicalFileProvider(builder.Environment.WebRootPath);
    var newPathProvider = new PhysicalFileProvider(
      Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles"));
    var compositeProvider = new CompositeFileProvider(webRootProvider,
                                                      newPathProvider);
    // Update the default provider.
    app.Environment.WebRootFileProvider = compositeProvider;
    app.UseStaticFiles();
    

    静态文件的安全注意事项

    UseDirectoryBrowserUseStaticFiles 可能会泄漏机密。 强烈建议在生产中禁用目录浏览。 请仔细查看 UseStaticFilesUseDirectoryBrowser 启用了哪些目录。 整个目录及其子目录均可公开访问。 将适合公开的文件存储在专用目录中,如 <content_root>/wwwroot。 将这些文件与 MVC 视图、Razor Pages 和配置文件等分开。

  • 使用 UseDirectoryBrowserUseStaticFiles 公开的内容的 URL 受大小写和基础文件系统字符限制的影响。 例如,Windows 不区分大小写,但 macOS 和 Linux 区分大小写。

  • 托管于 IIS 中的 ASP.NET Core 应用使用 ASP.NET Core 模块将所有请求转发到应用,包括静态文件请求。 不使用 IIS 静态文件处理程序,并且没有机会处理请求。

  • 在 IIS Manager 中完成以下步骤,删除服务器或网站级别的 IIS 静态文件处理程序:

  • 转到“模块”功能。
  • 在列表中选择 StaticFileModule。
  • 单击“操作”侧栏中的“删除” 。
  • 将代码文件(包括 .cs.cshtml)放在应用项目的 Web 根目录之外。 这样就在应用的客户端内容和基于服务器的代码间创建了逻辑分隔。 可以防止服务器端代码泄漏。
  • 通过更新 IWebHostEnvironment.WebRootPath 提供 wwwroot 外的文件

    IWebHostEnvironment.WebRootPath 设置为 wwwroot 以外的文件夹时:

  • 在开发环境中,在 wwwroot 和更新的 IWebHostEnvironment.WebRootPath 中发现的静态资产由 wwwroot 提供。
  • 在开发环境以外的任何环境中,重复的静态资产会从更新的 IWebHostEnvironment.WebRootPath 文件夹提供。
  • 请考虑使用空 Web 模板创建的 Web 应用:

  • wwwrootwwwroot-custom 中包含一个 Index.html 文件。

  • 使用以下设置了 WebRootPath = "wwwroot-custom" 的已更新 Program.cs 文件:

    var builder = WebApplication.CreateBuilder(new WebApplicationOptions
        Args = args,
        // Look for static files in "wwwroot-custom"
        WebRootPath = "wwwroot-custom"
    var app = builder.Build();
    app.UseDefaultFiles();
    app.UseStaticFiles();
    app.Run();
    

    在前面的代码中,请求 /

  • 在开发环境中,返回 wwwroot/Index.html
  • 在开发环境以外的任何环境中,返回 wwwroot-custom/Index.html
  • 若要确保返回 wwwroot-custom 中的资产,请使用以下方法之一:

  • wwwroot 中删除重复的命名资产。

  • Properties/launchSettings.json 中的 "ASPNETCORE_ENVIRONMENT" 设置为 "Development" 以外的任何值。

  • 通过在项目文件中设置 <StaticWebAssetsEnabled>false</StaticWebAssetsEnabled>,完全禁用静态 Web 资产。 警告,禁用静态 Web 资产会禁用 Razor 类库

  • 将以下 JSON 添加到项目文件:

    <ItemGroup>
        <Content Remove="wwwroot\**" />
    </ItemGroup>
    

    以下代码将 IWebHostEnvironment.WebRootPath 更新为非开发值,从而保证从 wwwroot-custom(而不是 wwwroot)返回重复内容:

    var builder = WebApplication.CreateBuilder(new WebApplicationOptions
        Args = args,
        // Examine Hosting environment: logging value
        EnvironmentName = Environments.Staging,
        WebRootPath = "wwwroot-custom"
    var app = builder.Build();
    app.Logger.LogInformation("ASPNETCORE_ENVIRONMENT: {env}",
          Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"));
    app.Logger.LogInformation("app.Environment.IsDevelopment(): {env}",
          app.Environment.IsDevelopment().ToString());
    app.UseDefaultFiles();
    app.UseStaticFiles();
    app.Run();
    
  • 查看或下载示例代码如何下载
  • ASP.NET Core 简介
  • 作者:Rick AndersonKirk Larkin

    默认情况下,静态文件(如 HTML、CSS、图像和 JavaScript)是 ASP.NET Core 应用直接提供给客户端的资产。

    查看或下载示例代码如何下载

    提供静态文件

    静态文件存储在项目的 Web 根目录中。 默认目录为 {content root}/wwwroot,但可通过 UseWebRoot 方法更改目录。 有关详细信息,请参阅内容根目录Web 根目录

    采用 CreateDefaultBuilder 方法可将内容根目录设置为当前目录:

    public class Program public static void Main(string[] args) CreateHostBuilder(args).Build().Run(); public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>();

    上述代码是使用 Web 应用程序模板创建的。

    可通过 Web 根目录的相关路径访问静态文件。 例如,Web 应用程序项目模板包含 wwwroot 文件夹中的多个文件夹:

  • wwwroot

    请考虑创建 wwwroot/images 文件夹,并添加 wwwroot/images/MyImage.jpg 文件。 用于访问 images 文件夹中的文件的 URI 格式为 https://<hostname>/images/<image_file_name>。 例如,https://localhost:5001/images/MyImage.jpg

    在 Web 根目录中提供文件

    默认 Web 应用模板在 Startup.Configure 中调用 UseStaticFiles 方法,这将允许提供静态文件:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
            endpoints.MapDefaultControllerRoute();
    

    无参数 UseStaticFiles 方法重载将 Web 根目录中的文件标记为可用。 以下标记引用 wwwroot/images/MyImage.jpg

    <img src="~/images/MyImage.jpg" class="img" alt="My image" />
    

    在上面的代码中,波形符 ~/ 指向 Web 根目录

    提供 Web 根目录外的文件

    考虑一个目录层次结构,其中要提供的静态文件位于 Web 根目录之外:

  • wwwroot
  • images
  • MyStaticFiles
  • images
  • red-rose.jpg
  • 按如下方式配置静态文件中间件后,请求可访问 red-rose.jpg 文件:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        app.UseHttpsRedirection();
        // using Microsoft.Extensions.FileProviders;
        // using System.IO;
        app.UseStaticFiles(new StaticFileOptions
            FileProvider = new PhysicalFileProvider(
                Path.Combine(env.ContentRootPath, "MyStaticFiles")),
            RequestPath = "/StaticFiles"
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
            endpoints.MapDefaultControllerRoute();
    

    在前面的代码中,MyStaticFiles 目录层次结构通过 StaticFiles URI 段公开 。 对 https://<hostname>/StaticFiles/images/red-rose.jpg 的请求将提供 red-rose.jpg 文件。

    以下标记引用 MyStaticFiles/images/red-rose.jpg

    <img src="~/StaticFiles/images/red-rose.jpg" class="img" alt="A red rose" />
    

    设置 HTTP 响应标头

    StaticFileOptions 对象可用于设置 HTTP 响应标头。 除配置从 Web 根目录提供静态文件外,以下代码还设置 Cache-Control 标头:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        app.UseHttpsRedirection();
        const string cacheMaxAge = "604800";
        app.UseStaticFiles(new StaticFileOptions
            OnPrepareResponse = ctx =>
                // using Microsoft.AspNetCore.Http;
                ctx.Context.Response.Headers.Append(
                     "Cache-Control", $"public, max-age={cacheMaxAge}");
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
            endpoints.MapDefaultControllerRoute();
    

    前面的代码将最长时间设置为 604800 秒(7 天)。

    静态文件授权

    ASP.NET Core 模板在调用 UseAuthorization 之前调用 UseStaticFiles。 大多数应用都遵循此模式。 如果在授权中间件之前调用静态文件中间件:

  • 不会对静态文件执行任何授权检查。
  • 由静态文件中间件提供的静态文件(例如 wwwroot 下的文件)可公开访问。
  • 根据授权提供静态文件:

  • 将它们存储在 wwwroot 之外。
  • 调用 UseAuthorization 之后调用 UseStaticFiles,以指定路径。
  • 设置回退授权策略
  • public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();
            app.UseDatabaseErrorPage();
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        app.UseHttpsRedirection();
        // wwwroot css, JavaScript, and images don't require authentication.
        app.UseStaticFiles();   
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseStaticFiles(new StaticFileOptions
            FileProvider = new PhysicalFileProvider(
                         Path.Combine(env.ContentRootPath, "MyStaticFiles")),
            RequestPath = "/StaticFiles"
        app.UseEndpoints(endpoints =>
            endpoints.MapRazorPages();
    
    public class Startup
        public Startup(IConfiguration configuration)
            Configuration = configuration;
        public IConfiguration Configuration { get; }
        public void ConfigureServices(IServiceCollection services)
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));
            services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores<ApplicationDbContext>();
            services.AddRazorPages();
            services.AddAuthorization(options =>
                options.FallbackPolicy = new AuthorizationPolicyBuilder()
                    .RequireAuthenticatedUser()
                    .Build();
        // Remaining code ommitted for brevity.
    

    在前面的代码中,回退授权策略要求所有用户进行身份验证。 用于指定其自己的授权要求的终结点(如控制器、Razor Pages 等)不使用回退授权策略。 例如,具有 [AllowAnonymous][Authorize(PolicyName="MyPolicy")] 的 Razor Pages、控制器或操作方法使用应用的授权属性,而不是回退授权策略。

    RequireAuthenticatedUserDenyAnonymousAuthorizationRequirement 添加到当前实例,这将强制对当前用户进行身份验证。

    wwwroot 下的静态资产是可公开访问的,因为在 UseAuthentication 之前会调用默认静态文件中间件 (app.UseStaticFiles();)。 MyStaticFiles 文件夹中的静态资产需要身份验证。 示例代码对此进行了演示。

    还有一种根据授权提供文件的方法是:

  • 将文件存储在 wwwroot 和静态文件中间件可访问的任何目录之外。
  • 通过应用授权的操作方法为其提供服务,并返回 FileResult 对象:
  • [Authorize]
    public IActionResult BannerImage()
        var filePath = Path.Combine(
            _env.ContentRootPath, "MyStaticFiles", "images", "red-rose.jpg");
        return PhysicalFile(filePath, "image/jpeg");
    

    目录浏览允许在指定目录中列出目录。

    出于安全考虑,目录浏览默认处于禁用状态。 有关详细信息,请参阅静态文件的安全注意事项

    通过以下方式启用目录浏览:

  • Startup.ConfigureServices 中的 AddDirectoryBrowser
  • Startup.Configure 中的 UseDirectoryBrowser
  • public void ConfigureServices(IServiceCollection services)
        services.AddControllersWithViews();
        services.AddDirectoryBrowser();
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        app.UseHttpsRedirection();
        // using Microsoft.Extensions.FileProviders;
        // using System.IO;
        app.UseStaticFiles(new StaticFileOptions
            FileProvider = new PhysicalFileProvider(
                Path.Combine(env.WebRootPath, "images")),
            RequestPath = "/MyImages"
        app.UseDirectoryBrowser(new DirectoryBrowserOptions
            FileProvider = new PhysicalFileProvider(
                Path.Combine(env.WebRootPath, "images")),
            RequestPath = "/MyImages"
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
            endpoints.MapDefaultControllerRoute();
    

    上述代码允许使用 URL https://<hostname>/MyImages 浏览 wwwroot/images 文件夹的目录,并链接到每个文件和文件夹:

    提供默认文档

    设置默认页面为访问者提供网站的起点。 若要从 wwwroot 提供默认文件,而不要求请求 URL 包含文件名,请调用 UseDefaultFiles 方法:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        app.UseHttpsRedirection();
        app.UseDefaultFiles();
        app.UseStaticFiles();
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
            endpoints.MapDefaultControllerRoute();
    

    要提供默认文件,必须在 UseStaticFiles 前调用 UseDefaultFilesUseDefaultFiles 是不提供文件的 URL 重写工具。

    使用 UseDefaultFiles 请求对 wwwroot 中的文件夹搜索:

  • default.htm
  • default.html
  • index.htm
  • index.html
  • 如同请求包含了文件名一样,提供从列表中找到的第一个文件。 浏览器 URL 继续反映请求的 URI。

    以下代码将默认文件名更改为 mydefault.html

    var options = new DefaultFilesOptions();
    options.DefaultFileNames.Clear();
    options.DefaultFileNames.Add("mydefault.html");
    app.UseDefaultFiles(options);
    app.UseStaticFiles();
    

    以下代码通过上述代码演示了 Startup.Configure

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        app.UseHttpsRedirection();
        var options = new DefaultFilesOptions();
        options.DefaultFileNames.Clear();
        options.DefaultFileNames.Add("mydefault.html");
        app.UseDefaultFiles(options);
        app.UseStaticFiles();
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
            endpoints.MapDefaultControllerRoute();
    

    默认文档的 UseFileServer

    UseFileServer 结合了 UseStaticFilesUseDefaultFilesUseDirectoryBrowser(可选)的功能。

    调用 app.UseFileServer 以提供静态文件和默认文件。 未启用目录浏览。 以下代码通过 UseFileServer 演示了 Startup.Configure

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        app.UseHttpsRedirection();
        app.UseFileServer();
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
            endpoints.MapDefaultControllerRoute();
    

    以下代码提供静态文件、默认文件和目录浏览:

    app.UseFileServer(enableDirectoryBrowsing: true);
    

    以下代码通过上述代码演示了 Startup.Configure

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        app.UseHttpsRedirection();
        app.UseFileServer(enableDirectoryBrowsing: true);
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
            endpoints.MapDefaultControllerRoute();
    

    考虑以下目录层次结构:

  • wwwroot
  • images
  • MyStaticFiles
  • images
  • MyImage.jpg
  • default.html
  • 以下代码提供静态文件、默认文件和 MyStaticFiles 的目录浏览:

    public void ConfigureServices(IServiceCollection services)
        services.AddControllersWithViews();
        services.AddDirectoryBrowser();
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        app.UseHttpsRedirection();
        app.UseStaticFiles(); // For the wwwroot folder.
        // using Microsoft.Extensions.FileProviders;
        // using System.IO;
        app.UseFileServer(new FileServerOptions
            FileProvider = new PhysicalFileProvider(
                Path.Combine(env.ContentRootPath, "MyStaticFiles")),
            RequestPath = "/StaticFiles",
            EnableDirectoryBrowsing = true
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
            endpoints.MapDefaultControllerRoute();
    

    必须在 EnableDirectoryBrowsing 属性值为 true 时调用 AddDirectoryBrowser

    使用文件层次结构和前面的代码,URL 解析如下:

    如果 MyStaticFiles 目录中不存在默认命名文件,则 https://<hostname>/StaticFiles 返回包含可单击链接的目录列表:

    UseDefaultFilesUseDirectoryBrowser 执行从不带尾部 / 的目标 URI 到带尾部 / 的目标 URI 的客户端重定向。 例如,从 https://<hostname>/StaticFileshttps://<hostname>/StaticFiles/。 如果没有尾部斜杠 (/),StaticFiles 目录中的相对 URL 无效。

    FileExtensionContentTypeProvider

    FileExtensionContentTypeProvider 类包含 Mappings 属性,用作文件扩展名到 MIME 内容类型的映射。 在以下示例中,多个文件扩展名映射到了已知的 MIME 类型。 替换了 .rtf 扩展名,删除了 .mp4 :

    // using Microsoft.AspNetCore.StaticFiles;
    // using Microsoft.Extensions.FileProviders;
    // using System.IO;
    // Set up custom content types - associating file extension to MIME type
    var provider = new FileExtensionContentTypeProvider();
    // Add new mappings
    provider.Mappings[".myapp"] = "application/x-msdownload";
    provider.Mappings[".htm3"] = "text/html";
    provider.Mappings[".image"] = "image/png";
    // Replace an existing mapping
    provider.Mappings[".rtf"] = "application/x-msdownload";
    // Remove MP4 videos.
    provider.Mappings.Remove(".mp4");
    app.UseStaticFiles(new StaticFileOptions
        FileProvider = new PhysicalFileProvider(
            Path.Combine(env.WebRootPath, "images")),
        RequestPath = "/MyImages",
        ContentTypeProvider = provider
    app.UseDirectoryBrowser(new DirectoryBrowserOptions
        FileProvider = new PhysicalFileProvider(
            Path.Combine(env.WebRootPath, "images")),
        RequestPath = "/MyImages"
    

    以下代码通过上述代码演示了 Startup.Configure

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        app.UseHttpsRedirection();
        // using Microsoft.AspNetCore.StaticFiles;
        // using Microsoft.Extensions.FileProviders;
        // using System.IO;
        // Set up custom content types - associating file extension to MIME type
        var provider = new FileExtensionContentTypeProvider();
        // Add new mappings
        provider.Mappings[".myapp"] = "application/x-msdownload";
        provider.Mappings[".htm3"] = "text/html";
        provider.Mappings[".image"] = "image/png";
        // Replace an existing mapping
        provider.Mappings[".rtf"] = "application/x-msdownload";
        // Remove MP4 videos.
        provider.Mappings.Remove(".mp4");
        app.UseStaticFiles(new StaticFileOptions
            FileProvider = new PhysicalFileProvider(
                Path.Combine(env.WebRootPath, "images")),
            RequestPath = "/MyImages",
            ContentTypeProvider = provider
        app.UseDirectoryBrowser(new DirectoryBrowserOptions
            FileProvider = new PhysicalFileProvider(
                Path.Combine(env.WebRootPath, "images")),
            RequestPath = "/MyImages"
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
            endpoints.MapDefaultControllerRoute();
    

    请参阅 MIME 内容类型

    非标准内容类型

    静态文件中间件可理解近 400 种已知文件内容类型。 如果用户请求文件类型未知的文件,则静态文件中间件将请求传递给管道中的下一个中间件。 如果没有中间件处理请求,则返回“404 未找到”响应。 如果启用了目录浏览,则在目录列表中会显示该文件的链接。

    以下代码提供未知类型,并以图像形式呈现未知文件:

    app.UseStaticFiles(new StaticFileOptions
        ServeUnknownFileTypes = true,
        DefaultContentType = "image/png"
    

    以下代码通过上述代码演示了 Startup.Configure

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        if (env.IsDevelopment())
            app.UseDeveloperExceptionPage();
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        app.UseHttpsRedirection();
        app.UseStaticFiles(new StaticFileOptions
            ServeUnknownFileTypes = true,
            DefaultContentType = "image/png"
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
            endpoints.MapDefaultControllerRoute();
    

    使用前面的代码,请求的文件含未知内容类型时,以图像形式返回请求。

    启用 ServeUnknownFileTypes 会形成安全隐患。 它默认处于禁用状态,不建议使用。 FileExtensionContentTypeProvider 提供了更安全的替代方法来提供含非标准扩展名的文件。

    从多个位置提供文件

    UseStaticFilesUseFileServer 默认为指向 wwwroot 的文件提供程序。 可使用其他文件提供程序提供 UseStaticFilesUseFileServer 的其他实例,从多个位置提供文件。 有关详细信息,请参阅此 GitHub 问题

    静态文件的安全注意事项

    UseDirectoryBrowserUseStaticFiles 可能会泄漏机密。 强烈建议在生产中禁用目录浏览。 请仔细查看 UseStaticFilesUseDirectoryBrowser 启用了哪些目录。 整个目录及其子目录均可公开访问。 将适合公开的文件存储在专用目录中,如 <content_root>/wwwroot。 将这些文件与 MVC 视图、Razor Pages 和配置文件等分开。

  • 使用 UseDirectoryBrowserUseStaticFiles 公开的内容的 URL 受大小写和基础文件系统字符限制的影响。 例如,Windows 不区分大小写,但 macOS 和 Linux 区分大小写。

  • 托管于 IIS 中的 ASP.NET Core 应用使用 ASP.NET Core 模块将所有请求转发到应用,包括静态文件请求。 不使用 IIS 静态文件处理程序,并且没有机会处理请求。

  • 在 IIS Manager 中完成以下步骤,删除服务器或网站级别的 IIS 静态文件处理程序:

  • 转到“模块”功能。
  • 在列表中选择 StaticFileModule。
  • 单击“操作”侧栏中的“删除” 。
  • 将代码文件(包括 .cs.cshtml)放在应用项目的 Web 根目录之外。 这样就在应用的客户端内容和基于服务器的代码间创建了逻辑分隔。 可以防止服务器端代码泄漏。
  • ASP.NET Core 简介
  •