Поделиться через


Сопоставление статических файлов в ASP.NET Core

Автор: Рик Андерсон

Статические файлы, такие как HTML, CSS, изображения и JavaScript, являются ресурсами, которыми приложение ASP.NET Core по умолчанию обслуживает непосредственно клиентов.

Руководство по Blazor статическим файлам, которое дополняет или заменяет рекомендации, приведенные в этой статье, см. раздел ASP.NET Core Blazor статические файлы.

Обслуживание статических файлов

Статические файлы хранятся в каталоге корневого веб- проекта. Каталог по умолчанию {content root}/wwwroot, но его можно изменить с помощью метода UseWebRoot. Для получения дополнительной информации см. корневой каталог содержимого и корневой каталог веб-.

Метод 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();

Статические файлы доступны по пути относительно корневого каталога веб-сайта. Например, шаблоны проекта веб-приложения содержат несколько папок в папке wwwroot:

  • wwwroot
    • css
    • js
    • lib

Рассмотрим приложение с файлом wwwroot/images/MyImage.jpg. Формат URI для доступа к файлу в папке imageshttps://<hostname>/images/<image_file_name>. Например, https://localhost:5001/images/MyImage.jpg

Отображение конечных точек маршрутизации для статических ресурсов (MapStaticAssets)

Для создания выполняющихся веб-приложений требуется оптимизация доставки ресурсов в браузер. Возможные оптимизации с включением MapStaticAssets :

  • Обслуживают указанный ресурс один раз, пока файл не изменится или браузер очищает его кэш. Задайте заголовки ETag и Last-Modified .
  • Запретить браузеру использовать старые или устаревшие ресурсы после обновления приложения. Установите заголовок Last-Modified.
  • Настройте правильные заголовки кэширования .
  • Используйте промежуточное ПО кэширования.
  • По возможности обслуживайте сжатые версии ресурсов. Эта оптимизация не включает минификации.
  • Используйте CDN для обслуживания ресурсов ближе к пользователю.
  • Фингерпринтинг ресурсов для предотвращения повторного использования старых версий файлов.

MapStaticAssets:

  • Интегрирует сведения, собранные о статических веб-ресурсах во время сборки и публикации, с библиотекой среды выполнения, которая обрабатывает эти сведения для оптимизации обслуживания файлов в браузере.
  • Являются соглашениями о конечной точке маршрутизации, которые оптимизируют доставку статических ресурсов в приложении. Он предназначен для работы со всеми платформами пользовательского интерфейса, включая Blazor, Razor Pages и MVC.

MapStaticAssets и UseStaticFiles

MapStaticAssets доступна в ASP.NET Core в .NET 9.0 и более поздних версиях. UseStaticFiles необходимо использовать в версиях до .NET 9.0.

UseStaticFiles обслуживает статические файлы, но не обеспечивает тот же уровень оптимизации, что и MapStaticAssets. MapStaticAssets оптимизирован для обслуживания ресурсов, о которых приложению известно во время выполнения. Если приложение обслуживает данные из других источников, таких как диск или встроенные ресурсы, необходимо использовать UseStaticFiles.

Статические ресурсы карты предоставляют следующие преимущества, которые недоступны при вызове UseStaticFiles:

  • Сжатие во время сборки для всех ресурсов в приложении, включая JavaScript (JS) и таблицы стилей, но исключая ресурсы изображений и шрифтов, которые уже сжаты. Сжатие Gzip (Content-Encoding: gz) используется во время разработки. Gzip с сжатием Brotli (Content-Encoding: br) используется при публикации.
  • Отпечаток для всех ресурсов формируется во время сборки в виде строки в формате Base64, содержащей хэш SHA-256 каждого файла. Это предотвращает повторное использование старой версии файла, даже если старый файл кэшируется. Ресурсы с отпечатками кэшируются при помощи директивы immutable, что приводит к тому, что браузер больше не запрашивает ресурс до его изменения. Для браузеров, которые не поддерживают директиву immutable, добавляется директива max-age.
    • Даже если ресурсу не присвоен отпечаток, содержимое ETags создается для каждого статического ресурса с использованием хеша отпечатка файла в качестве значения ETag. Это гарантирует, что браузер загружает только файл, если его содержимое изменяется (или файл загружается в первый раз).
    • Внутри фреймворка сопоставляются физические активы с их уникальными идентификаторами, что позволяет приложению:
      • Найдите автоматически созданные ресурсы, например Razor компонент с областной видимостью CSS для Blazorфункции изоляции CSS и JS ресурсы, описанные JS в картах импорта.
      • Создайте теги ссылок в <head> содержимом страницы для предварительной загрузки ресурсов.
  • Во время тестирования разработки с использованием Hot Reload в Visual Studio
    • Сведения о целостности удаляются из ресурсов, чтобы избежать проблем при изменении файла во время работы приложения.
    • Статические ресурсы не кэшируются, чтобы браузер всегда извлекает текущее содержимое.

Статические ресурсы карты не предоставляют функции для минификации или других преобразований файлов. Минификация обычно обрабатывается пользовательским кодом или сторонними инструментами.

Следующие функции поддерживаются с UseStaticFiles, но не с использованием MapStaticAssets.

Обслуживание файлов в корневом веб-каталоге

Шаблоны веб-приложений по умолчанию вызывают метод MapStaticAssets в Program.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 делает файлы в корневом каталоге веб-сайта доступными для обслуживания. Приведены следующие ссылки на разметку wwwroot/images/MyImage.jpg:

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

В приведенной выше разметке символ тильды ~ указывает на веб-корневой каталог .

Обслуживание файлов за пределами корневой папки веб-сайта

Рассмотрим иерархию каталогов, в которой обслуживаемые статические файлы находятся за пределами корневого веб-:

  • 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. Помимо настройки обслуживания статических файлов из корневого каталога веб-, следующий код устанавливает заголовок 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();

Приведенный выше код делает статические файлы общедоступными в локальном кэше в течение одной недели.

Авторизация статических файлов

Шаблоны 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">

UseStaticFiles и UseFileServer по умолчанию направляются к поставщику файлов, который указывает на wwwroot. Дополнительные экземпляры UseStaticFiles и UseFileServer могут предоставляться другим поставщикам файлов для обслуживания файлов из других расположений. В следующем примере выполняется вызов UseStaticFiles дважды для обслуживания файлов из wwwroot и MyStaticFiles:

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 статические файлы.

Обслуживание файлов за пределами wwwroot путем обновления IWebHostEnvironment.WebRootPath

Когда IWebHostEnvironment.WebRootPath установлено в папку, отличную от wwwroot:

  • В среде разработки статические ресурсы, найденные как в wwwroot, так и обновленные IWebHostEnvironment.WebRootPath, обслуживаются из wwwroot.
  • В любой среде, отличной от разработки, повторяющиеся статические ресурсы обслуживаются из обновленной папки IWebHostEnvironment.WebRootPath.

Рассмотрим веб-приложение, созданное с помощью пустого веб-шаблона:

  • Файл Index.html, содержащийся в wwwroot и wwwroot-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> в файле проекта. ВНИМАНИЕ, отключение статических веб-ресурсов приводит к отключению библиотек классов 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();

Дополнительные ресурсы