在 ASP.NET Core 應用程式中提供靜態檔案

Note

這不是這篇文章的最新版本。 關於目前版本,請參閱 本文的 .NET 10 版本。

Warning

不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支持原則。 關於目前版本,請參閱 本文的 .NET 10 版本。

學習如何透過 Map Static Assets 端點慣例或靜態檔案中介軟體,在 ASP.NET Core 應用程式中提供、保護及優化靜態檔案。 靜態檔案,也稱為靜態資產,並非動態產生,而是直接提供給客戶端,包括 HTML、CSS、圖片和 JavaScript。

如需新增或取代本文中指引的 Blazor 靜態檔案指引,請參閱 ASP.NET Core Blazor 靜態檔案。

若要在 ASP.NET Core 中啟用靜態檔案處理,請呼叫 MapStaticAssets。

預設情況下,將靜態檔案儲存在專案的 網頁根 目錄中。 預設目錄是 {CONTENT ROOT}/wwwroot,其中 {CONTENT ROOT} 預留位置是應用程式的內容 根目錄。 只有資料夾裡 wwwroot 的檔案是可位址的,所以你不用擔心其他程式碼。

只有具有特定副檔名對應至支援媒體類型的檔案才會被視為靜態網頁資產。

靜態 Web 資產會在建置時被發現,並透過內容指紋進行最佳化,以防止重複利用舊檔案。 資產也會 壓縮 ,以減少資產交付時間。

在執行階段,探索到的靜態 Web 資產會公開為套用 HTTP 標頭的端點,例如 快取標頭 和內容類型標頭。 資源只會提供一次,直到檔案變更或瀏覽器清除其快取為止。 ETag、Last-Modified 和 Content-Type 標頭已設定。 更新應用程式後,瀏覽器無法使用過時的資產。

靜態資產的傳遞是以 端點路由為基礎,因此它可與其他端點感知功能搭配使用,例如授權。 其設計目的是要處理所有 UI 架構,包括 Blazor、Razor Pages 和 MVC。

映射靜態資產提供下列優點:

  • 應用程式中所有資產的建置時間壓縮,包括 JavaScript (JS)和樣式表單,但不包括已壓縮的影像和字型資產。 Gzip (Content-Encoding: gz) 壓縮會在開發期間使用。 Gzip 和 Brotli (Content-Encoding: br) 壓縮在發佈期間都會使用。
  • 使用每個檔案內容之 SHA-256 哈希的 Base64編碼字串,在建置時間 所有資產的指紋。 這可防止重複使用舊版的檔案,即使已快取舊檔案也一樣。 帶有指紋的資產會使用 immutable 指令來快取,這樣一來,直到資產變更為止,瀏覽器都不會再次請求該資產。 對於不支援 immutable 指示詞的瀏覽器,會新增 max-age 指示詞。
    • 即使一個資產沒有經過指紋處理,也會為每個靜態資產生成基於內容的 ETags,並使用檔案的指紋哈希作為 ETag 值。 這可確保瀏覽器只有在內容變更時才會下載檔案(或檔案第一次下載)。
    • 在內部,架構會將實體資產對應至其指紋,讓應用程式能夠:
      • 尋找自動產生的資產,例如 Razor 元件範圍的 CSS(用於 Blazor 的 CSS 隔離功能),以及由 JS描述的 JS 資產。
      • 在頁面 <head> 內容中產生連結標籤,以預先載入資產。

Map Static Assets 不提供縮小化或其他檔案轉換功能。 縮小化通常是由自訂程式碼或 第三方工具來處理。

Note

MapStaticAssets 不會自行提供 預設文件。 要提供預設文件,請先呼叫 UseDefaultFiles 並接著 UseStaticFiles。 欲了解更多資訊,請參閱 「預設文件服務 」章節。

若要在 ASP.NET Core 中啟用靜態檔案處理,請呼叫 UseStaticFiles。

預設情況下,將靜態檔案儲存在專案的 網頁根 目錄中。 預設目錄是 {CONTENT ROOT}/wwwroot,其中 {CONTENT ROOT} 預留位置是應用程式的內容 根目錄。 只有資料夾裡 wwwroot 的檔案是可位址的,所以你不用擔心其他程式碼。

在執行階段,當要求靜態 Web 資產時,靜態檔案中介軟體會傳回這些資產,並套用資產修改相關標頭和內容類型標頭。 ETag、Last-Modified 和 Content-Type 標頭已設定。

靜態檔案中介軟體可提供靜態檔案,並且當應用程式在其要求處理管線中呼叫 UseStaticFiles 時會使用它。 檔案會從 或 IWebHostEnvironment.WebRootPath中WebRootFileProvider指定的路徑提供,該路徑預設為 Web 根資料夾,通常是 wwwroot。

您也可以從 參考的專案和套件提供靜態 Web 資產。

變更 Web 根目錄

要更改網頁根,請使用這個 UseWebRoot 方法。 如需詳細資訊,請參閱 ASP.NET Core 基本概念概觀。

在專案檔中使用 wwwroot,避免發佈 <Content> 中的檔案。 以下範例可防止在 wwwroot/local 及其子目錄中發佈內容:

<ItemGroup>
  <Content Update="wwwroot\local\**\*.*" CopyToPublishDirectory="Never" />
</ItemGroup>

CreateBuilder 方法可將內容根目錄設定為目前的目錄:

var builder = WebApplication.CreateBuilder(args);

CreateDefaultBuilder 方法可將內容根目錄設定為目前的目錄:

Host.CreateDefaultBuilder(args)

在請求處理管線中,於呼叫 UseHttpsRedirection 之後,請呼叫 MapStaticAssets,以啟用從應用程式的 Web 根目錄 提供靜態檔案:

app.MapStaticAssets();

在請求處理管線中,於呼叫 UseHttpsRedirection 之後,請呼叫 UseStaticFiles,以啟用從應用程式的 Web 根目錄 提供靜態檔案:

app.UseStaticFiles();

您可透過 Web 根目錄的相對路徑來存取靜態檔案。

若要存取影像,請造訪 wwwroot/images/favicon.png:

  • 網址格式: https://{HOST}/images/{FILE NAME}
    • 佔位符是 {HOST} 主機。
    • {FILE NAME} 預留位置表示檔案名稱。
  • 範例
    • 絕對網址: https://localhost:5001/images/favicon.png
    • 根相對網址: images/favicon.png

在應用程式中Blazor,images/favicon.png從應用程式資料夾favicon.png載入圖示影像 (wwwroot/images):

<link rel="icon" type="image/png" href="images/favicon.png" />

在 Pages 和 MVC 應用程式中 Razor ,波浪號字元 ~ 指向網頁根目錄。 在下列範例中,~/images/favicon.png從應用程式的 favicon.png 資料夾中載入圖示影像 (wwwroot/images) :

<link rel="icon" type="image/png" href="~/images/favicon.png" />

中斷中間件管道

若要避免在比對靜態資產之後執行整個中介軟體管線,這是 UseStaticFiles 的行為,請在 ShortCircuit 上呼叫 MapStaticAssets。 立即呼叫 ShortCircuit 會執行端點並回傳回應,防止其他中間件執行靜態資產請求。

app.MapStaticAssets().ShortCircuit();

在開發期間管理靜態檔案快取

在 Development 環境中執行時,例如在 Visual Studio 即時重載 開發測試期間,框架會覆寫快取頭,以防止瀏覽器快取靜態檔案。 此行為有助於確保檔案變更時仍使用最新版本,避免內容陳舊的問題。 在生產環境中,框架會設定正確的快取標頭,讓瀏覽器能如預期快取靜態資產。

要停用此行為,請在EnableStaticAssetsDevelopmentCaching環境中的應用程式設定檔true()中設定Development為 appsettings.Development.json ()。

非Development 環境中的靜態檔案

當應用程式在本地運行時, Development 環境是唯一能啟用靜態網頁資產的環境。 若要在本地開發與測試期間以外的環境(例如在 Development 環境中)啟用靜態檔案,請呼叫 Staging 上的 UseStaticWebAssets。

Warning

呼叫UseStaticWebAssets以獲取精確的環境,避免在生產環境中意外啟用此功能,因為此功能會從磁碟上的不同位置提供檔案,而不是從專案。 本節範例使用Staging檢查IsStaging環境。

if (builder.Environment.IsStaging())
{
    builder.WebHost.UseStaticWebAssets();
}

使用IWebHostEnvironment.WebRootPath在 Web 根目錄之外提供檔案

當你設定 IWebHostEnvironment.WebRootPath 為非 wwwroot的資料夾時,應用程式會呈現以下預設行為:

  • 在Development環境中,若同名資產同時存在於wwwroot與分配給wwwroot的不同資料夾,靜態資產則會從WebRootPath中提供。
  • 在Development之外的任何環境中,重複的靜態資產會從 WebRootPath 資料夾提供。

假設從空白 Web 範本建立的 Web 應用程式:

  • 在 Index.html 和 wwwroot 中包含 wwwroot-custom 檔案。
  • Program檔案已更新以進行WebRootPath = "wwwroot-custom"配置設定。
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
    Args = args,
    WebRootPath = "wwwroot-custom"
});

依預設,對 / 的請求:

  • 在Development環境中,wwwroot/Index.html會被返回。
  • 在任何非Development環境中,會返回wwwroot-custom/Index.html。

若要確保來自 wwwroot-custom 的資產一律傳回,請使用下列 一種 方法:

  • 刪除重複命名的資產於 wwwroot。

  • 將 ASPNETCORE_ENVIRONMENT 中的 Properties/launchSettings.json 設定為 Development 以外的任何值。

  • 在應用程式的專案檔中將 <StaticWebAssetsEnabled> 設定為 false 以停用靜態 Web 資產。 警告: 停用靜態 Web 資產會停用Razor類別庫。

  • 將下列 XML 新增至項目檔:

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

下列程式碼會將 WebRootPath 更新為非 Development 值(Staging),保證重複的內容是從 wwwroot-custom 而非 wwwroot 傳回:

var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
    Args = args,
    EnvironmentName = Environments.Staging,
    WebRootPath = "wwwroot-custom"
});

靜態檔案中介軟體

靜態檔案中介軟體可在特定的靜態檔案情境中提供靜態檔案服務,通常是作為 Map Static Assets 端點路由慣例(MapStaticAssets)之外的補充。

當您在應用程式的要求處理管線中呼叫 UseStaticFiles 時,請將靜態檔案中介軟體納入要求處理,通常是在新增 Map Static Assets 端點慣例之後 (MapStaticAssets)。

在針對 .NET 9 或更新版本的應用程式中使用 Map Static Assets 端點慣例。 在以 .NET 9 之前版本為目標的應用程式中使用靜態檔案中介軟體。

靜態檔案中介軟體提供靜態檔案,但無法提供像 Map Static Assets 端點慣例那樣的優化。 當你僅依賴靜態檔案中介軟體時,將無法使用 Map Static Assets 端點慣例的建置時壓縮和指紋識別功能。

端點慣例是為了服務應用程式在執行時已知的資產而優化的。 若應用程式提供來自其他位置的資產,如磁碟或嵌入式資源,則使用靜態檔案中介軟體。

本文涵蓋的以下功能在靜態檔案中介軟體中支援,但不支援 Map Static Assets 端點慣例:

使用UseStaticFiles在 Web 根目錄之外提供檔案

請考慮下列目錄階層,其中靜態檔案位於應用程式 Web 根 目錄外部的資料夾中,名為 ExtraStaticFiles:

  • wwwroot
    • css
    • images
    • js
  • ExtraStaticFiles
    • images
      • red-rose.jpg

請求可以透過設定新的靜態檔案中介軟體實例來存取 red-rose.jpg :

下列 API 的命名空間:

using Microsoft.Extensions.FileProviders;

在請求處理流程中,在對現有呼叫MapStaticAssets(.NET 9或更新版本)或UseStaticFiles(.NET 8或更早版本)之後:

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(builder.Environment.ContentRootPath, "ExtraStaticFiles")),
    RequestPath = "/static-files"
});

在前述程式碼中, ExtraStaticFiles 目錄階層可透過 static-files URL 區段公開存取。 向 https://{HOST}/StaticFiles/images/red-rose.jpg 發出的請求,其中 {HOST} 佔位元是主機,提供 red-rose.jpg 檔案。

下列標記參考 ExtraStaticFiles/images/red-rose.jpg:

<img src="static-files/images/red-rose.jpg" alt="A red rose" />

在前述範例中, Razor 頁面與 MVC 視圖支援波浪斜線符號(src="~/StaticFiles/images/red-rose.jpg"),但 Razor 應用程式中的 Blazor 元件不支援此符號。

從多個位置提供檔案

本節中的指引適用於 Razor Pages 和 MVC 應用程式。 如需適用於 Blazor Web App 的指導,請參閱 ASP.NET Core Blazor 靜態檔案。

請考慮下列顯示公司標誌的標記:

<img src="~/logo.png" asp-append-version="true" alt="Company logo">

開發人員打算使用 影像標籤協助程式 來附加版本,並從自訂位置(名為 ExtraStaticFiles的資料夾)提供檔案。

下列範例會呼叫 MapStaticAssets 以提供來自 wwwroot 的檔案,以及呼叫 UseStaticFiles 以提供來自 ExtraStaticFiles 的檔案。

在請求處理流程中,在對現有呼叫MapStaticAssets(.NET 9或更新版本)或UseStaticFiles(.NET 8或更早版本)之後:

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(builder.Environment.ContentRootPath, "ExtraStaticFiles"))
});

下列範例會呼叫 UseStaticFiles 兩次,以同時提供來自 wwwroot 和 ExtraStaticFiles的檔案。

在請求處理管線中,於現有對 UseStaticFiles 的呼叫之後:

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(builder.Environment.ContentRootPath, "ExtraStaticFiles"))
});

使用上述程式碼,會顯示檔案 ExtraStaticFiles/logo.png 。 不過,不會套用 影像標籤協助程式(AppendVersion),因為標籤協助程式相依於 WebRootFileProvider,而該協助程式尚未更新以包含 ExtraStaticFiles 資料夾。

下列程式碼會使用WebRootFileProvider將ExtraStaticFiles更新以包含CompositeFileProvider資料夾。 這可讓影像標籤輔助器將版本套用至位於 ExtraStaticFiles 資料夾中的影像。

下列 API 的命名空間:

using Microsoft.Extensions.FileProviders;

在現有呼叫 MapStaticAssets (.NET 9 或更新版本) 或 UseStaticFiles (.NET 8 或更早版本) 之前的要求處理管線中:

var webRootProvider = new PhysicalFileProvider(builder.Environment.WebRootPath);
var newPathProvider = new PhysicalFileProvider(
    Path.Combine(builder.Environment.ContentRootPath, "ExtraStaticFiles"));

var compositeProvider = new CompositeFileProvider(webRootProvider, newPathProvider);

app.Environment.WebRootFileProvider = compositeProvider;

UseStaticFiles 和 UseFileServer 預設為指向 wwwroot 的檔案提供者。 你可以提供額外的實例 UseStaticFiles ,並 UseFileServer 與其他檔案提供者合作,以提供來自其他地點的檔案。 如需詳細資訊,請參閱 UseStaticFiles 與 UseFileServer for wwwroot 仍然需要搭配使用(dotnet/AspNetCore.Docs #15578)。

設定 HTTP 回應標頭

用於 StaticFileOptions 設定 HTTP 回應標頭。 除了設定靜態檔案中介軟體以提供靜態檔案外,以下程式碼將標頭設定Cache-Control為 604,800 秒(一週)。

下列 API 的命名空間:

using Microsoft.AspNetCore.Http;

在請求處理流程中,在對現有呼叫MapStaticAssets(.NET 9或更新版本)或UseStaticFiles(.NET 8或更早版本)之後:

app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = ctx =>
    {
        ctx.Context.Response.Headers.Append(
            "Cache-Control", "public, max-age=604800");
    }
});

大量資產集合

當你處理大量資產(約 1,000 個或更多)時,請使用打包工具來減少應用程式最終提供的資產數量,或將 MapStaticAssets 與 UseStaticFiles 合併。

MapStaticAssets 急切地載入資源建置程式期間擷取的預先計算中繼資料,以支援壓縮、快取和指紋識別。 這些功能是以應用程式使用更多記憶體為代價的。 對於經常存取的資產,通常值得付出代價。 對於不經常存取的資產,這種權衡可能不值得付出代價。

如果你不使用捆綁,請將 MapStaticAssets 與 UseStaticFiles 結合。 下列範例示範此方法。

在專案檔案 .csproj 中,使用 StaticWebAssetEndpointExclusionPattern MSBuild 屬性來從 MapStaticAssets 的最終資訊清單中篩選端點。 排除的檔案由 UseStaticFiles 提供,不會受益於壓縮、快取和指紋識別。

為維持框架預設的排除模式,設定 值StaticWebAssetEndpointExclusionPattern時保留 $(StaticWebAssetEndpointExclusionPattern) 。 在分號分隔的列表中加入更多模式。

在以下範例中,排除模式會加入資料夾中的 lib/icons 靜態檔案,該資料夾代表一組假設的圖示:

<StaticWebAssetEndpointExclusionPattern>
  $(StaticWebAssetEndpointExclusionPattern);lib/icons/**
</StaticWebAssetEndpointExclusionPattern>

在 app.UseHttpsRedirection(); 檔案中經過 HTTPS 重新導向中介軟體 (Program) 處理後:

app.UseStaticFiles();

app.UseAuthorization();

app.MapStaticAssets();

靜態資產清單

MapStaticAssets 根據 靜態資產資訊清單 提供資產,而不是在執行階段掃描 網站根目錄。 清單會在建置與發佈時產生,記錄為應用程式發現的靜態網頁資產,以及內容指紋、 Content-Type 標頭、快取標頭和預先計算的壓縮表示(Gzip 和 Brotli)等元資料。 執行時,讀取 MapStaticAssets 清單,為每個資產註冊端點,並提供最佳化回應。

建置過程會在建置輸出目錄中產生清單。 其檔案名稱基於專案的組合名稱(例如 {ASSEMBLY NAME}.staticwebassets.endpoints.json, {ASSEMBLY NAME} 佔位符是應用程式的 MSBuild AssemblyName 值)。 若想提供來自不同地點的清單,請參閱 「提供自訂靜態檔案清單 」章節。

因為它 MapStaticAssets 只服務清單中列出的資產,不會服務不屬於清單的檔案。 在以下情況下,檔案不屬於資訊清單的一部分:

  • 位於建置階段 Web 根目錄之外,例如從磁碟提供的檔案、嵌入式資源,或在執行階段設定的自訂 WebRootPath。
  • 以 StaticWebAssetEndpointExclusionPattern MSBuild 屬性排除在清單之外(見 「資產大集合 」章節)。

若要提供不在清單中的檔案,請呼叫 UseStaticFiles,執行時會直接從網頁根提供檔案。 這也是為什麼以 提供 MapStaticAssets 需要呼叫 UseStaticFiles 的原因。

將建置產生的檔案整合到靜態網頁資產中

建置工具,如 TypeScript 編譯器和 JavaScript 打包器,通常會在建置過程中產生檔案。 若要以 MapStaticAssets 提供的指紋辨識、壓縮和快取機制來提供這些產生的檔案,則必須在建置期間將這些檔案辨識為靜態 Web 資產。 產生的檔案通常會被放在網頁根wwwroot目錄外(),並排除在原始碼控制之外,因此預設不會被發現為靜態網頁資產。 只有在建置期間解析靜態 Web 資產時被判定為位於 wwwroot 之下的檔案,才會加入 靜態資產資訊清單,並由 MapStaticAssets 提供。 連結到 wwwroot (例如,包含 <Content> item 和 Link)的檔案也會被包含在內,即使原始檔案存放在 wwwroot之外。

若要將建置產生的檔案納入靜態網頁資產,請採用以下任一方法。

新增一個<Content>項目,並使用 Link 將每個產生的檔案放置於 wwwroot。 連結到 wwwroot 的檔案會由靜態 Web 資產管線探索到,並由 MapStaticAssets 提供,且套用指紋識別、壓縮和快取,即使原始檔案儲存在 wwwroot 之外也是如此。

以下範例中,建置步驟會在 main.js 資料夾 generated 中產生,該 <Content> 項目將檔案連結至 wwwroot:

<ItemGroup>
  <Content Include="generated\main.js" Link="wwwroot\main.js"
    CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

當建置過程解析靜態網頁資產時,產生的檔案必須存在。

使用 JavaScript 專案來處理複雜的建置流程

對於複雜的 JavaScript 或 TypeScript 用戶端建置,請使用一個獨立的 JavaScript 專案,該專案透過 JavaScript 專案系統Microsoft.VisualStudio.JavaScript.Sdk(MSBuild SDK 及專案檔案).esproj來建立客戶端資產。 參考 ASP.NET Core 應用程式中的 JavaScript 專案,使其輸出以靜態網頁資產的形式被使用。 舉例來說,請參考Microsoft.FluentUI.AspNetCore.Components.Assets.esproj專案檔案(microsoft/fluentui-blazorGitHub 倉庫)。

靜態檔案授權

當應用程式採用 後援授權原則時,若授權中介軟體所處理的要求無法從授權中繼資料產生任何原則,則會要求授權。 此要求包括對映射為端點的靜態資產請求。 若要允許匿名存取靜態資產,請套用 AllowAnonymousAttribute 到端點建構器:

app.MapStaticAssets().Add(endpointBuilder => 
    endpointBuilder.Metadata.Add(new AllowAnonymousAttribute()));

當應用程式採用 備用授權原則 時,如果授權中介軟體處理要求時未根據授權中繼資料產生任何原則,則必須進行授權。 ASP.NET Core 範本允許在呼叫UseStaticFiles之前呼叫UseAuthorization靜態檔案,以匿名存取靜態檔案。 大部分的應用程式都遵循此模式。 當靜態檔案中介軟體先於授權中介軟體被呼叫時:

  • 靜態檔案上不會執行授權檢查。
  • 靜態檔案中介軟體所提供的靜態檔案,例如網頁根目錄(通常為 wwwroot),是公開可存取的。

若要根據授權提供靜態檔案:

  • 確認應用程式已將 後援授權原則 設定為需要已驗證的使用者。
  • 將靜態檔案儲存在應用程式的 Web 根目錄之外。
  • 呼叫 UseAuthorization之後,呼叫 UseStaticFiles,指定 Web 根目錄外部靜態檔案資料夾的路徑。

下列 API 的命名空間:

using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.FileProviders;

服務報名:

builder.Services.AddAuthorization(options =>
{
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
});

呼叫 UseAuthorization 後的請求處理管線中:

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(builder.Environment.ContentRootPath, "SecureStaticFiles")),
    RequestPath = "/static-files"
});

下列 API 的命名空間:

using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.FileProviders;

在 Startup.ConfigureServices 中:

services.AddAuthorization(options =>
{
    options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
});

在 Startup.Configure 呼叫 UseAuthorization 後,

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(env.ContentRootPath, "SecureStaticFiles")),
    RequestPath = "/static-files"
});

在上述程式碼中,後援授權原則需要經過驗證的使用者。 指定授權需求的端點會使用由其授權元資料產生的政策,而非備回政策。 完整的政策選擇規則,請參閱 ASP.NET Core 中的基於政策的授權。

RequireAuthenticatedUser 會將 DenyAnonymousAuthorizationRequirement 新增至目前的執行個體,這會確保目前的使用者已通過驗證。

儲存在應用程式網頁根目錄中的靜態資產是公開存取的,因為預設的靜態檔案中介軟體(UseStaticFiles)會在 之前被呼叫 UseAuthorization。 資料夾中的 SecureStaticFiles 靜態資產需要驗證。

根據授權提供檔案的替代方法是:

  • 將檔案存放在網站根目錄以及任何可由靜態檔案中介軟體存取的目錄之外。
  • 透過套用授權的動作方法提供檔案,並傳回物件 FileResult 。

從頁面 Razor ():Pages/BannerImage.cshtml.cs

public class BannerImageModel : PageModel
{
    private readonly IWebHostEnvironment _env;

    public BannerImageModel(IWebHostEnvironment env) => _env = env;

    public PhysicalFileResult OnGet()
    {
        var filePath = Path.Combine(
            _env.ContentRootPath, "SecureStaticFiles", "images", "red-rose.jpg");

        return PhysicalFile(filePath, "image/jpeg");
    }
}

來自控制器(Controllers/HomeController.cs):

[Authorize]
public IActionResult BannerImage()
{
    var filePath = Path.Combine(
        _env.ContentRootPath, "SecureStaticFiles", "images", "red-rose.jpg");

    return PhysicalFile(filePath, "image/jpeg");
}

上述方法需要每個檔案一個頁面或端點。

下列路由端點範例會傳回已驗證使用者的檔案。

在 Program 檔案中:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AuthenticatedUsers", b => b.RequireAuthenticatedUser());
});

...

app.MapGet("/files/{fileName}", IResult (string fileName) => 
{
    var filePath = GetOrCreateFilePath(fileName);

    if (File.Exists(filePath))
    {
        return TypedResults.PhysicalFile(filePath, fileName);
    }

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

以下路由端點範例為管理員角色admin()上傳已認證使用者的檔案。

在 Program 檔案中:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminsOnly", b => b.RequireRole("admin"));
});

...

// IFormFile uses memory buffer for uploading. For handling large 
// files, use streaming instead. See the *File uploads* article
// in the ASP.NET Core documentation:
// https://learn.microsoft.com/aspnet/core/mvc/models/file-uploads
app.MapPost("/files", async (IFormFile file, LinkGenerator linker, 
    HttpContext context) =>
{
    // Don't rely on the value in 'file.FileName', as it's only metadata that can 
    // be manipulated by the end-user. Consider 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);
    await SaveFileWithCustomFileName(file, fileSaveName);

    context.Response.Headers.Append("Location", linker.GetPathByName(context, 
        "GetFileByName", new { fileName = fileSaveName}));

    return TypedResults.Ok("File Uploaded Successfully!");
})
.RequireAuthorization("AdminsOnly");

在 Startup.ConfigureServices 中:

services.AddAuthorization(options =>
{
    options.AddPolicy("AuthenticatedUsers", b => b.RequireAuthenticatedUser());
});

在 Startup.Configure 中:

app.MapGet("/files/{fileName}", IResult (string fileName) => 
{
    var filePath = GetOrCreateFilePath(fileName);

    if (File.Exists(filePath))
    {
        return TypedResults.PhysicalFile(filePath, fileName);
    }

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

以下程式碼會上傳管理員角色admin()中已認證使用者的檔案。

在 Startup.ConfigureServices 中:

services.AddAuthorization(options =>
{
    options.AddPolicy("AdminsOnly", b => b.RequireRole("admin"));
});

在 Startup.Configure 中:

// IFormFile uses memory buffer for uploading. For handling large 
// files, use streaming instead. See the *File uploads* article
// in the ASP.NET Core documentation:
// https://learn.microsoft.com/aspnet/core/mvc/models/file-uploads
app.MapPost("/files", async (IFormFile file, LinkGenerator linker, 
    HttpContext context) =>
{
    // Don't rely on the value in 'file.FileName', as it's only metadata that can 
    // be manipulated by the end-user. Consider 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);
    await SaveFileWithCustomFileName(file, fileSaveName);

    context.Response.Headers.Append("Location", linker.GetPathByName(context, 
        "GetFileByName", new { fileName = fileSaveName}));

    return TypedResults.Ok("File Uploaded Successfully!");
})
.RequireAuthorization("AdminsOnly");

目錄瀏覽

「目錄瀏覽」允許列出指定目錄中的目錄清單。

出於安全考量,預設是關閉目錄瀏覽功能。 如需詳細資訊,請參閱靜態檔案的安全性考量。

請使用以下 API 啟用目錄瀏覽:

在以下範例中:

  • images應用程式根目錄的資料夾包含用於目錄瀏覽的影像。
  • 瀏覽影像的要求路徑是 /DirectoryImages。
  • 呼叫 UseStaticFiles 並設定 FileProviderStaticFileOptions,可顯示個別檔案的瀏覽器連結。

下列 API 的命名空間:

using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.FileProviders;

服務報名:

builder.Services.AddDirectoryBrowser();

在請求處理流程中,在對現有呼叫MapStaticAssets(.NET 9或更新版本)或UseStaticFiles(.NET 8或更早版本)之後:

var fileProvider = new PhysicalFileProvider(
    Path.Combine(builder.Environment.WebRootPath, "images"));
var requestPath = "/DirectoryImages";

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = fileProvider,
    RequestPath = requestPath
});

app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
    FileProvider = fileProvider,
    RequestPath = requestPath
});

下列 API 的命名空間:

using Microsoft.Extensions.FileProviders;
using System.IO;

在 Startup.ConfigureServices 中:

services.AddDirectoryBrowser();

在Startup.Configure中,於現有對UseStaticFiles的呼叫之後:

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(env.WebRootPath, "images")),
    RequestPath = "/DirectoryImages"
});

app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(env.WebRootPath, "images")),
    RequestPath = "/DirectoryImages"
});

上述程式碼允許使用 URL wwwroot/images 瀏覽資料夾的目錄https://{HOST}/DirectoryImages,其中包含每個檔案和資料夾的連結,其中{HOST}預留位置是主機。

AddDirectoryBrowser 新增目錄瀏覽中介軟體所需的服務,包括 HtmlEncoder。 這些服務可能已透過其他呼叫新增,例如 AddRazorPages,但仍請呼叫 AddDirectoryBrowser 以確保這些服務已新增。

提供預設文件

[設定預設頁面] 為訪客在站台上提供了起點。 若要提供預設檔案wwwroot,而不需要要求 URL 包含檔案名稱,請呼叫UseDefaultFiles

UseDefaultFiles 是 URL 重寫器,其無法提供檔案。 它會將請求 URL 重寫為預設文件(例如 / ,寫到 /index.html),並有另一個元件服務該檔案。

因為 MapStaticAssets 服務的是建置時透過端點路由發現的資產,它不會單獨提供預設文件。 呼叫 UseDefaultFiles 來重寫請求,接著呼叫 UseStaticFiles,為預設文件提供重寫後的請求:

app.UseDefaultFiles();
app.UseStaticFiles();
app.MapStaticAssets();

Important

僅設定 UseDefaultFiles 和 MapStaticAssets(不含 UseStaticFiles)時,對 / 的請求會收到 404 - Not Found 回應。 此現象是因為最小主機會在請求處理管線開始時加入路由中介軟體,因此端點路由會先匹配請求,然後再 UseDefaultFiles 將其重寫為預設文件。 當你使用 WebRootPath 將 網站根目錄 變更為自訂路徑時,這個問題會特別明顯,因為自訂網站根目錄中的檔案不屬於 MapStaticAssets 提供的 建置時的靜態資產資訊清單。 如前述範例所示,在 UseStaticFiles 之後加入對 UseDefaultFiles 的呼叫,以提供預設文件服務。

在請求處理管線中,於現有的 UseStaticFiles 呼叫之前:

app.UseDefaultFiles();

使用 UseDefaultFiles,要求在 wwwroot 資料夾中搜尋:

  • default.htm
  • default.html
  • index.htm
  • index.html

從清單中找到的第一個檔案,會被當作請求中已包含該檔案的名稱來提供。 瀏覽器 URL 仍會繼續反應要求的 URI。

下列程式碼會將預設檔案名稱變更為 default-document.html:

var options = new DefaultFilesOptions();
options.DefaultFileNames.Clear();
options.DefaultFileNames.Add("default-document.html");
app.UseDefaultFiles(options);

結合靜態檔案、預設文件和目錄瀏覽

UseFileServer 結合 UseStaticFiles、UseDefaultFiles 和 UseDirectoryBrowser (選擇性) 的功能。

在請求處理管線中,在現有的 MapStaticAssets 呼叫(.NET 9 或更新版本)或 UseStaticFiles 呼叫(.NET 8 或更早版本)之後,呼叫 UseFileServer 以啟用提供靜態檔案和預設檔案:

app.UseFileServer();

上述範例未啟用目錄瀏覽。

下列程式碼可啟用靜態檔案、預設檔案和目錄瀏覽的服務。

服務報名:

builder.Services.AddDirectoryBrowser();

在請求處理管線中,於現有對 UseStaticFiles 的呼叫之後:

app.UseFileServer(enableDirectoryBrowsing: true);

在 Startup.ConfigureServices 中:

services.AddDirectoryBrowser();

在Startup.Configure中,於現有對UseStaticFiles的呼叫之後:

app.UseFileServer(enableDirectoryBrowsing: true);

對於主機位址 (/),UseFileServer 會傳回預設的 HTML 文件,然後是預設的 Razor 頁面 (Pages/Index.cshtml)或預設的 MVC 檢視 (Home/Index.cshtml)。

請考慮下列目錄階層:

  • wwwroot
    • css
    • images
    • js
  • ExtraStaticFiles
    • images
      • logo.png
    • default.html

下列程式碼會啟用靜態檔案和預設檔案的提供功能,以及ExtraStaticFiles 的目錄瀏覽功能。

下列 API 的命名空間:

using Microsoft.Extensions.FileProviders;

服務報名:

builder.Services.AddDirectoryBrowser();

在請求處理管線中,於現有對 UseStaticFiles 的呼叫之後:

app.UseFileServer(new FileServerOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(builder.Environment.ContentRootPath, "ExtraStaticFiles")),
    RequestPath = "/static-files",
    EnableDirectoryBrowsing = true
});

下列 API 的命名空間:

using Microsoft.Extensions.FileProviders;
using System.IO;

在 Startup.ConfigureServices 中:

services.AddDirectoryBrowser();

在Startup.Configure中,於現有對UseStaticFiles的呼叫之後:

app.UseFileServer(new FileServerOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(env.ContentRootPath, "ExtraStaticFiles")),
    RequestPath = "/static-files",
    EnableDirectoryBrowsing = true
});

必須在 AddDirectoryBrowser 屬性值為 EnableDirectoryBrowsing 時呼叫 true。

使用上述檔案階層和程式碼,URL 會如下表所示解析 ( {HOST} 預留位置是主機)。

URI 回應檔案
https://{HOST}/static-files/images/logo.png ExtraStaticFiles/images/logo.png
https://{HOST}/static-files ExtraStaticFiles/default.html

如果目錄中 ExtraStaticFiles 沒有預設命名的檔案, https://{HOST}/static-files 則會傳回具有可點擊連結的目錄清單,其中 {HOST} 預留位置是主機。

UseDefaultFiles 和 UseDirectoryBrowser 會執行用戶端重新導向,從不含尾端 / 的目標 URI 重新導向至含尾端 / 的目標 URI。 例如,從 https://{HOST}/static-files (無尾端 /) 到 https://{HOST}/static-files/ (包括尾端 /)。 目錄中的ExtraStaticFiles相對 URL 若無尾端斜線(/)則無效,除非使用RedirectToAppendTrailingSlash的DefaultFilesOptions選項。

將副檔名對應至 MIME 類型

Note

如需適用於 Blazor 應用程式的指引,請參閱 ASP.NET 核心 Blazor 靜態檔案。

用 FileExtensionContentTypeProvider.Mappings 來新增或修改副檔名至 MIME 內容類型的對應關係。

Note

FileExtensionContentTypeProvider 對於並行寫入 不是執行緒安全的。 其內部的對應字典是標準的 Dictionary<string, string>,且未經同步處理。 提供者的映射預期會在啟動時一次設定完成。 若後續僅執行讀取作業(查詢),此提供者即可安全地註冊為單例。 在提供者被同時請求使用後,請勿新增、移除或修改映射。

在下列範例中,數個副檔名會對應至已知的 MIME 類型。 擴充套件會被 .rtf 取代,並且 .mp4 將被移除:

using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.FileProviders;

...

// 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
});

若有多個靜態檔案選項要設定,您也可以改為使用 StaticFileOptions 來設定提供者:

var provider = new FileExtensionContentTypeProvider();

...

builder.Services.Configure<StaticFileOptions>(options =>
{
    options.ContentTypeProvider = provider;
});

app.UseStaticFiles();

在 Startup.Configure 中:

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 = "/images",
    ContentTypeProvider = provider
});

app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(env.WebRootPath, "images")),
    RequestPath = "/images"
});

如需詳細資訊,請參閱 MIME 內容類型。

非標準的內容類型

靜態檔案中介軟體可辨識近 400 種已知檔案內容類型。 如果使用者請求的檔案類型未知,靜態檔案中介軟體會將請求轉交給管線中的下一個中介軟體。 如果沒有中介軟體處理請求,伺服器會回傳 404 未找到 回應。 若啟用目錄瀏覽,伺服器會在目錄列表中顯示該檔案的連結。

下列程式碼會啟用提供未知的內容類型,並將未知檔案轉譯為影像:

app.UseStaticFiles(new StaticFileOptions
{
    ServeUnknownFileTypes = true,
    DefaultContentType = "image/png"
});

使用上述程式碼時,系統會以影像來回應具有未知內容類型的檔案要求。

Warning

啟用 ServeUnknownFileTypes 會有安全性風險。 預設為停用,亦不建議您使用。 將檔案副檔名對應至 MIME 類型提供了一種更安全的替代方式,可用來提供具有非標準副檔名的檔案。

提供自訂靜態檔案資訊清單

如果staticAssetsManifestPath是null,那麼IHostEnvironment.ApplicationName會用來定位資訊清單。 或者,指定資訊清單檔案的完整路徑。 如果你使用相對路徑,框架會在 AppContext.BaseDirectory 中搜尋該檔案。

靜態檔案的安全性考量

Warning

UseDirectoryBrowser 和 UseStaticFiles 可能會導致洩漏祕密。 強烈建議您在生產環境中停用目錄瀏覽功能。 透過 UseStaticFiles 或 UseDirectoryBrowser,仔細檢閱要啟用哪些目錄。 因為整個目錄和其子目錄都可供公開存取。 將檔案存放在專門公開提供的目錄,例如 <content_root>/wwwroot。 將這些檔案與 MVC 檢視、Razor Pages、組態檔等區隔開來。

  • 透過 UseDirectoryBrowser 和 UseStaticFiles 公開的內容,其 URL 會遵循底層檔案系統的大小寫區分與字元限制。 例如,Windows 不區分大小寫,但 macOS 和 Linux 則區分大小寫。

  • 裝載於 IIS 中的 ASP.NET Core 應用程式會使用 ASP.NET Core 模組,將所有要求轉送給應用程式 (包括靜態檔案要求), IIS 靜態檔案處理器不被使用,也無法處理請求。

  • 請完成 IIS 管理員中的下列步驟,以移除伺服器或網站層級的 IIS 靜態檔案處理常式:

    1. 導航至模組功能。
    2. 選取清單中的 StaticFileModule。
    3. 在 [動作] 側邊欄中按一下 [移除]。

    Warning

    如果已啟用 IIS 靜態檔案處理常式,但是未正確設定 ASP.NET Core 模組,仍可提供靜態檔案。 例如,當 web.config 檔案未被部署時,就會出現此狀況。

  • 請將程式碼檔案 (包括 .cs 和 .cshtml) 放在應用程式專案的 Web 根目錄之外。 此配置在應用程式的客戶端內容與伺服器端程式碼之間建立了邏輯分離。 這種分離防止伺服器端程式碼外洩。

MSBuild 屬性

下列表格顯示靜態檔案相關的 MSBuild 屬性及其中繼資料描述。

房產 Description
EnableDefaultCompressedItems 啟用預設壓縮包含與排除模式。
CompressionIncludePatterns 使用分號分隔的檔案模式清單,以包含在壓縮中。
CompressionExcludePatterns 要從壓縮中排除的檔案模式的分號分隔清單。 請參閱以下範例。
CompressionEnabled 設定為 false時,完全關閉靜態資產壓縮。 請參閱以下範例。
EnableDefaultCompressionFormats 啟用預設壓縮格式(Gzip 和 Brotli)。
BuildCompressionFormats 在建置期間使用的壓縮格式。
PublishCompressionFormats 發佈期間要使用的壓縮格式。
DisableBuildCompression 在建置期間停用壓縮。
CompressDiscoveredAssetsDuringBuild 在建置期間壓縮探索到的資產。
BrotliCompressionLevel Brotli 演算法的壓縮層級。
StaticWebAssetBuildCompressAllAssets 在建置時壓縮所有資產,而不只是那些在建置過程中被發現或被計算的資產。
StaticWebAssetPublishCompressAllAssets 在發佈時壓縮所有資產,而不僅限於建置期間發現或計算的資產。

以下範例排除了 JavaScript 檔案的壓縮範圍:

<PropertyGroup>
  <CompressionExcludePatterns>$(CompressionExcludePatterns);**\*.js</CompressionExcludePatterns>
</PropertyGroup>

要完全關閉靜態資產壓縮:

<PropertyGroup>
  <CompressionEnabled>false</CompressionEnabled>
</PropertyGroup>
房產 Description
StaticWebAssetBasePath 資料庫中所有資產的基底URL路徑。
StaticWebAssetsFingerprintContent 啟用內容指紋識別以破壞快取。
StaticWebAssetFingerprintingEnabled 啟用靜態 Web 資產的指紋識別功能。
StaticWebAssetsCacheDefineStaticWebAssetsEnabled 啟用靜態 Web 資產定義的快取。
StaticWebAssetEndpointExclusionPattern 排除端點的模式。
項目群組 Description 後設資料
StaticWebAssetContentTypeMapping 將檔案模式對應至端點的內容類型和快取標頭。 Pattern、Cache、Priority
StaticWebAssetFingerprintPattern 定義將指紋套用至靜態 Web 資產以進行快取破壞的模式。 Pattern、Expression

元資料描述:

  • Pattern:用於比對檔案的 glob 模式。 對於 StaticWebAssetContentTypeMapping,它會比對檔案以判斷其內容類型 (例如, *.js JavaScript 檔案)。 對於 StaticWebAssetFingerprintPattern,它會識別包含多種副檔名的檔案,這些檔案需要特殊的指紋處理(例如 *.lib.module.js)。

  • Cache:指定 Cache-Control 相符內容類型的標頭值。 此值控制瀏覽器快取行為(例如 max-age=3600, must-revalidate 媒體檔案)。

  • Priority: 控制多個 StaticWebAssetContentTypeMapping 項目匹配同一檔案時的優先順序。 較高的數值優先於較低的數值。 Priority 是必要的。

  • Expression:定義如何將指紋嵌入到檔案名稱中。 預設值為 #[.{FINGERPRINT}],在副檔名之前插入指紋({FINGERPRINT} 佔位符)。

下列範例將點陣圖檔案的模式(.bmp)對應到 image/bmp 內容類型,其中 {CACHE HEADER} 代表要用於非指紋端點的 Cache-Control 標頭:

<ItemGroup>
  <StaticWebAssetContentTypeMapping Include="image/bmp" Cache="{CACHE HEADER}"
    Pattern="*.bmp" Priority="1" />
</ItemGroup>

執行階段配置選項

下表說明執行階段組態選項。

組態金鑰 Description
ReloadStaticAssetsAtRuntime 啟用靜態資產在開發時的熱重新載入功能:提供已修改的 Web 根目錄 (wwwroot) 檔案(重新計算 ETag,如需則重新壓縮),而非使用建置時的資訊清單版本。 除非明確設定,否則預設為僅在提供組建資訊清單時啟用。
DisableStaticAssetNotFoundRuntimeFallback 當 true時,會隱藏提供建置資訊清單中不存在的新新增檔案的後援端點。 當 false 不存在時,將進行檔案存在檢查的 {**path} 備援(GET/HEAD),記錄警告並使用計算得到的 ETag 提供檔案。
EnableStaticAssetsDevelopmentCaching 當 true時,會保留資產描述元上的原始 Cache-Control 標頭。 當 false 缺失或不存在時,會重寫 Cache-Control 標頭為 no-cache,以避免在開發期間進行過度積極的用戶端快取。
EnableStaticAssetsDevelopmentIntegrity 當 true 時,會保留資產描述子的完整性屬性。 當 false 存在或不存在時,會移除任何完整性屬性,以防止在開發過程中檔案變更時產生不匹配。

其他資源