次の方法で共有


ASP.NET Core で静的ファイルをマップする

作成者: Rick Anderson

HTML、CSS、イメージ、JavaScript などの静的ファイルは、ASP.NET Core アプリが既定でクライアントに直接提供するアセットです。

この記事のガイダンスに追加または置き換わる静的ファイルのガイダンス Blazor については、「 ASP.NET Core Blazor 静的ファイル」を参照してください。

静的ファイルを配信する

静的ファイルは、プロジェクトの 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.MapStaticAssets();

app.UseAuthorization();

app.MapDefaultControllerRoute().WithStaticAssets();
app.MapRazorPages().WithStaticAssets();

app.Run();

静的ファイルには、 Web ルートに対する相対パスを使用してアクセスできます。 たとえば、 Web アプリケーション プロジェクト テンプレートには、 wwwroot フォルダー内のいくつかのフォルダーが含まれています。

  • wwwroot
    • css
    • js
    • lib

wwwroot/images/MyImage.jpg ファイルを含むアプリについて考えてみましょう。 images フォルダー内のファイルにアクセスするための URI 形式はhttps://<hostname>/images/<image_file_name>。 たとえば、https://localhost:5001/images/MyImage.jpg のように指定します。

静的資産ルーティング エンドポイント規則のマップ (MapStaticAssets)

高パフォーマンスの Web アプリを作成するには、ブラウザーへのアセット デリバリーを最適化する必要があります。 MapStaticAssetsで考えられる最適化は次のとおりです。

MapStaticAssets:

  • ビルドおよび発行プロセス中に収集された静的 Web 資産に関する情報を、ブラウザーへのファイルの提供を最適化するためにこの情報を処理するランタイム ライブラリと統合します。
  • アプリ内の静的資産の配信を最適化するルーティング エンドポイント規則です。 これは、Blazor、Razor Pages、MVC など、すべての UI フレームワークで動作するように設計されています。

MapStaticAssetsUseStaticFiles

MapStaticAssets は、.NET 9 以降の ASP.NET Core で使用できます。 UseStaticFiles は、.NET 9 より前のバージョンで使用する必要があります。

UseStaticFiles は静的ファイルを提供しますが、 MapStaticAssetsと同じレベルの最適化を提供しません。 MapStaticAssets は、アプリが実行時に知っている資産を提供するために最適化されています。 アプリがディスクや埋め込みリソースなど、他の場所からアセットを提供する場合は、UseStaticFiles を使用する必要があります。

静的アセットのマップには、 UseStaticFilesを呼び出すときに使用できない次の利点があります。

  • JavaScript (JS) やスタイルシートを含むが、既に圧縮されているイメージアセットとフォントアセットを除く、アプリ内のすべてのアセットのビルド時圧縮。 Gzip (Content-Encoding: gz) 圧縮は開発中に使用されます。 発行時に Brotli (Content-Encoding: br) 圧縮を使用した Gzip が使用されます。
  • ビルド時に、各ファイルのコンテンツに基づいたSHA-256ハッシュのBase64エンコードされた文字列を使って、すべての資産のフィンガープリントを行います。 これにより、古いファイルがキャッシュされている場合でも、古いバージョンのファイルを再利用できなくなります。 フィンガープリントされた資産はimmutable ディレクティブを使用してキャッシュされるため、ブラウザーは変更されるまで資産を再度要求しません。 immutable ディレクティブをサポートしていないブラウザーの場合は、max-age ディレクティブが追加されます。
    • アセットがフィンガープリントされていない場合でも、ファイルのフィンガープリント ハッシュをETags値として使用して、静的アセットごとにコンテンツ ベースのETagが生成されます。 これにより、コンテンツが変更された場合 (またはファイルが初めてダウンロードされる) 場合にのみ、ブラウザーでファイルがダウンロードされるようになります。
    • 内部的には、フレームワークは物理資産を指紋にマップします。これにより、アプリは次のことができます。
      • RazorのBlazor用にCSSスコープのコンポーネントや、JSで記述されたJSアセットなど、 自動生成されたアセットを検索します。
      • ページの <head> コンテンツにリンク タグを生成して、アセットをプリロードします。
  • Visual Studio ホット リロード開発テスト中:
    • アプリの実行中にファイルが変更されたときに問題が発生しないように、資産から整合性情報が削除されます。
    • ブラウザーが常に現在のコンテンツを確実に取得できるように、静的資産はキャッシュされません。

静的アセットのマップでは、縮小やその他のファイル変換の機能は提供されません。 縮小は通常、カスタム コードまたは サード パーティ製ツールによって処理されます。

次の機能は、 UseStaticFiles ではサポートされていますが、 MapStaticAssetsではサポートされていません。

Web ルートでファイルを提供する

既定の Web アプリ テンプレートでは、MapStaticAssetsProgram.cs メソッドが呼び出され、静的ファイルを提供できます。

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

app.UseAuthorization();

app.MapDefaultControllerRoute().WithStaticAssets();
app.MapRazorPages().WithStaticAssets();

app.Run();

パラメーターなしの MapStaticAssets メソッドオーバーロードは、 Web ルート 内のファイルを予約可能としてマークします。 次のマークアップは wwwroot/images/MyImage.jpg を参照しています。

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

上記のマークアップでは、 ~ チルダ文字が Web ルートを指しています。

Web ルートの外部でファイルを提供する

サービスを提供する静的ファイルが Web ルートの外部にあるディレクトリ階層を考えてみましょう。

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

要求は、次のように静的ファイル ミドルウェアを構成することで、 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();    //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();

前のコードでは、 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 ヘッダーを設定します。

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().WithStaticAssets();
app.MapRazorPages().WithStaticAssets();

app.Run();

上記のコードでは、静的ファイルをローカル キャッシュで 1 週間公開しています。

静的ファイルの承認

ASP.NET Core テンプレートは、MapStaticAssetsを呼び出す前にUseAuthorizationを呼び出します。 ほとんどのアプリは、このパターンに従います。 承認ミドルウェアの前に MapStaticAssets が呼び出されたとき:

  • 静的ファイルに対して承認チェックは実行されません。
  • 静的ファイル ミドルウェアによって提供される静的ファイル ( wwwroot下の静的ファイルなど) には、パブリックにアクセスできます。

承認に基づいて静的ファイルを処理するには、 静的ファイルの承認に関するページを参照してください。

複数の場所からファイルを提供する

Razor ファイルを表示する次の/MyStaticFiles/image3.png ページについて考えてみましょう。

@page

<p> Test /MyStaticFiles/image3.png</p>

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

UseStaticFilesUseFileServerwwwrootを指すファイルプロバイダーにデフォルト設定されています。 UseStaticFilesUseFileServerの追加インスタンスは、他の場所のファイルを提供するために、他のファイル プロバイダーと共に提供できます。 次の例では、 UseStaticFiles を 2 回呼び出して、 wwwrootMyStaticFilesの両方のファイルを処理します。

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

上記のコードの使用:

  • /MyStaticFiles/image3.png ファイルが表示されます。
  • タグ ヘルパーAppendVersionに依存するためWebRootFileProviderイメージ タグ ヘルパーは適用されません。 WebRootFileProvider は、 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.MapStaticAssets();

上記の方法は、 Razor Pages および MVC アプリに適用されます。 Blazor Web Appに適用されるガイダンスについては、ASP.NET Core Blazor静的ファイルに関するページを参照してください。

IWebHostEnvironment.WebRootPath を更新して wwwroot の外部でファイルを提供する

IWebHostEnvironment.WebRootPathwwwroot以外のフォルダーに設定されている場合:

  • 開発環境では、 wwwroot と更新された IWebHostEnvironment.WebRootPath の両方で見つかった静的資産が wwwrootから提供されます。
  • 開発以外の環境では、更新された IWebHostEnvironment.WebRootPath フォルダーから重複する静的資産が提供されます。

空の Web テンプレートを使用して作成された Web アプリについて考えてみましょう。

  • Index.htmlおよびwwwrootwwwroot-custom ファイルを含む。

  • Program.csを設定する次の更新されたWebRootPath = "wwwroot-custom" ファイルを使用します。

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

上記のコードで、/ に要求を行うと:

  • 開発環境でwwwroot/Index.htmlを返す
  • 開発以外のどの環境でも wwwroot-custom/Index.html が返される

wwwroot-customから資産が確実に返されるようにするには、次のいずれかの方法を使用します。

  • wwwrootで重複する名前付き資産を削除します。

  • "ASPNETCORE_ENVIRONMENT"Properties/launchSettings.json"Development"以外の値に設定します。

  • プロジェクト ファイルで <StaticWebAssetsEnabled>false</StaticWebAssetsEnabled> を設定して、静的 Web アセットを完全に無効にします。 警告:静的 Web アセットを無効にすると、クラス ライブラリRazor無効になります

  • 次の XML をプロジェクト ファイルに追加します。

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

app.Run();

その他のリソース