Статические файлы в ASP.NET Core

Авторы: Рик Андерсон (Rick Anderson) и Кирк Ларкин (Kirk Larkin)

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

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

Статические файлы хранятся в корневом каталоге документов проекта. Каталог по умолчанию — {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.UseStaticFiles();

app.UseAuthorization();

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

app.Run();

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

  • wwwroot
    • css
    • js
    • lib

Мы рекомендуем создать папку wwwroot/images и добавить в нее файл wwwroot/images/MyImage.jpg. Формат URI для доступа к файлу в папке images: https://<hostname>/images/<image_file_name>. Например: https://localhost:5001/images/MyImage.jpg

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

Шаблоны веб-приложений по умолчанию вызывают метод UseStaticFiles в 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.UseStaticFiles();

app.UseAuthorization();

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

app.Run();

Эта перегрузка метода UseStaticFiles не принимает параметров, она помечает файлы в корневом каталоге документов как обслуживаемые. Следующая разметка ссылается на 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(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
           Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles")),
    RequestPath = "/StaticFiles"
});

app.UseAuthorization();

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

app.Run();

В приведенном выше коде доступ к иерархии каталога MyStaticFiles представляется через сегмент URI StaticFiles. Запрос к 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-ответов

Для установки заголовков HTTP-ответов можно использовать объект StaticFileOptions. Кроме настройки обслуживания статических файлов в корневом каталоге документов, в следующем коде также устанавливается заголовок 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();
app.MapRazorPages();

app.Run();

В приведенном выше коде статические файлы становятся общедоступными в локальном кэше на одну неделю (604 800 секунд).

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

Шаблоны ASP.NET Core вызывают UseStaticFiles перед вызовом UseAuthorization. Большинство приложений используют этот шаблон. При вызове ПО промежуточного слоя статического файла перед ПО промежуточного слоя авторизации:

  • для статических файлов не выполняются проверки авторизации;
  • статические файлы, обслуживаемые ПО промежуточного слоя статического файла, например те, которые находятся в wwwroot, являются общедоступными.

Для обслуживания статических файлов на основе авторизации:

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();
}
else
{
    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 и т. д., которые определяют собственные требования к авторизации, не используют резервную политику авторизации. Например, Razor Pages, контроллеры или методы действий с [AllowAnonymous] или [Authorize(PolicyName="MyPolicy")] используют примененный атрибут авторизации вместо резервной политики авторизации.

RequireAuthenticatedUser добавляет DenyAnonymousAuthorizationRequirement к текущему экземпляру, что обеспечивает проверку подлинности текущего пользователя.

Статические ресурсы в wwwroot являются общедоступными, так как ПО промежуточного слоя для статического файла по умолчанию (app.UseStaticFiles();) вызывается перед UseAuthentication. Для статических ресурсов в папке 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");
    }
}

Просмотр каталогов

Просмотр каталогов позволяет просматривать список каталогов в указанных каталогах.

По соображениям безопасности просмотр каталогов отключен по умолчанию. Дополнительные сведения см. в разделе Замечания по безопасности для статических файлов.

Включите просмотр каталогов с помощью AddDirectoryBrowser и UseDirectoryBrowser:

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

Приведенный выше код разрешает просмотр папки wwwroot/images с помощью URL-адреса https://<hostname>/MyImages, со ссылками на все файлы и папки:

directory browsing

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

Для обслуживания файла по умолчанию метод UseDefaultFilesдолжен быть вызван до метода UseStaticFiles. UseDefaultFiles — это средство переопределения 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 объединяет функции UseStaticFiles, UseDefaultFiles и при необходимости UseDirectoryBrowser.

Вызовите 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();

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
    • css
    • images
    • js
  • 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();

AddDirectoryBrowser должно вызываться, когда свойство EnableDirectoryBrowsing имеет значение true.

При указанных выше иерархии файлов и коде URL-адреса будут разрешаться следующим образом:

URI Ответ
https://<hostname>/StaticFiles/images/MyImage.jpg MyStaticFiles/images/MyImage.jpg
https://<hostname>/StaticFiles MyStaticFiles/default.html

Если в каталоге MyStaticFiles отсутствует файл с именем по умолчанию, https://<hostname>/StaticFiles возвращает список содержимого каталога с доступными для перехода ссылками:

Static files list

UseDefaultFiles и UseDirectoryBrowser выполняют перенаправление на стороне клиента из целевого URI без / в конце в целевой URI с / в конце. Например, из https://<hostname>/StaticFiles в https://<hostname>/StaticFiles/. Относительные URL-адреса в каталоге StaticFiles считаются недопустимыми без косой черты в конце (/).

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 является более безопасной альтернативой для обслуживания файлов с нестандартными расширениями.

Предоставление файлов из нескольких расположений

Рассмотрим следующую страницу 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(); // Serve files from wwwroot
app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles"))
});

С помощью приведенного выше кода:

Следующий код обновляет WebRootFileProvider, чтобы Image Tag Helper мог предоставлять версию:

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

Безопасность статических файлов

Предупреждение

Использование UseDirectoryBrowser и UseStaticFiles может привести к утечке конфиденциальной информации. Настоятельно рекомендуется отключать просмотр каталогов в рабочей среде. Тщательно проверьте, просмотр каких каталогов разрешен посредством UseStaticFiles или UseDirectoryBrowser. Весь каталог и его подкаталоги становятся общедоступными. Храните файлы, предназначенные для общего доступа, в выделенных каталогах, таких как <content_root>/wwwroot. Отделите эти файлы от представлений MVC, Razor Pages, файлов конфигурации и т. д.

  • К URL-адресам содержимого, к которому предоставлен доступ методами UseDirectoryBrowser и UseStaticFiles, применяются те же требования по регистрозависимости и запрещенным символам, что и к базовой файловой системе. Например, в Windows регистр не учитывается, а в macOS и Linux учитывается.

  • Приложения ASP.NET Core, размещенные в IIS, используют Модуль Core ASP.NET для перенаправления всех запросов к приложению, включая запросы статических файлов. Обработчик статических файлов IIS не используется и не выполняет обработку запросов.

  • Выполните следующие шаги в диспетчере служб IIS для удаления обработчика статических файлов IIS на уровне сервера или веб-сайта:

    1. Перейдите к компоненту Модули.
    2. Выберите в списке модуль StaticFileModule.
    3. Нажмите кнопку Удалить в боковой панели Действия.

Предупреждение

Если обработчик статических файлов IIS включен и модуль ASP.NET Core настроен неправильно, то статические файлы будут обслуживаться. Это может случиться, если, например, не был развернут файл web.config.

  • Размещайте файлы с кодом, включая файлы .cs и .cshtml, за пределами корневого веб-каталога проекта приложения. Таким образом, в приложении создается логическое разделение между клиентским содержимым и серверным кодом. Это предотвращает утечку серверного кода.

Обслуживание файлов за пределами 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.UseStaticFiles();
    
    app.Run();
    

В приведенном выше коде запросы к /:

  • В среде разработки возвращают wwwroot/Index.html.
  • В любой среде, отличной от среды разработки, возвращают wwwroot-custom/Index.html.

Чтобы обеспечить возврат ресурсов из wwwroot-custom, используйте один из следующих подходов:

  • Удаление повторяющихся именованных ресурсов в wwwroot.

  • Задание для "ASPNETCORE_ENVIRONMENT" в Properties/launchSettings.json любого значения, отличного от "Development".

  • Полностью отключите статические веб-ресурсы, задав <StaticWebAssetsEnabled>false</StaticWebAssetsEnabled> в файле проекта. ПРЕДУПРЕЖДЕНИЕ. Отключение статических веб-ресурсов отключает библиотеки классов 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();

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

Авторы: Рик Андерсон (Rick Anderson) и Кирк Ларкин (Kirk Larkin)

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

Просмотреть или скачать образец кода (как скачивать)

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

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

Метод 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 Application содержат несколько папок в папке wwwroot:

  • wwwroot
    • css
    • js
    • lib

Мы рекомендуем создать папку wwwroot/images и добавить в нее файл wwwroot/images/MyImage.jpg. Формат URI для доступа к файлу в папке images: https://<hostname>/images/<image_file_name>. Например: https://localhost:5001/images/MyImage.jpg

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

Шаблоны веб-приложений по умолчанию вызывают метод UseStaticFiles в Startup.Configure, который позволяет обслуживать статические файлы:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });
}

Эта перегрузка метода UseStaticFiles не принимает параметров, она помечает файлы в корневом каталоге документов как обслуживаемые. Следующая разметка ссылается на 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, настроив ПО промежуточного слоя для статических файлов следующим образом:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        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 представляется через сегмент URI StaticFiles. Запрос к 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-ответов

Для установки заголовков HTTP-ответов можно использовать объект StaticFileOptions. Кроме настройки обслуживания статических файлов в корневом каталоге документов, следующий код также устанавливает заголовок Cache-Control:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        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();
    });
}

В приведенном выше коде для параметра max-age устанавливается значение 604800 секунд (7 дней).

Response headers showing the Cache-Control header has been added

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

Шаблоны ASP.NET Core вызывают UseStaticFiles перед вызовом UseAuthorization. Большинство приложений используют этот шаблон. При вызове ПО промежуточного слоя статического файла перед ПО промежуточного слоя авторизации:

  • для статических файлов не выполняются проверки авторизации;
  • статические файлы, обслуживаемые ПО промежуточного слоя статического файла, например те, которые находятся в wwwroot, являются общедоступными.

Для обслуживания статических файлов на основе авторизации:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        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 и т. д., которые определяют собственные требования к авторизации, не используют резервную политику авторизации. Например, Razor Pages, контроллеры или методы действий с [AllowAnonymous] или [Authorize(PolicyName="MyPolicy")] используют примененный атрибут авторизации вместо резервной политики авторизации.

RequireAuthenticatedUser добавляет DenyAnonymousAuthorizationRequirement к текущему экземпляру, что обеспечивает проверку подлинности текущего пользователя.

Статические ресурсы в wwwroot являются общедоступными, так как ПО промежуточного слоя для статического файла по умолчанию (app.UseStaticFiles();) вызывается перед UseAuthentication. Для статических ресурсов в папке MyStaticFiles требуется проверка подлинности. Это показано в следующем примере кода.

Альтернативный подход к обработке файлов на основе авторизации:

  • Сохраните файлы за пределами каталога wwwroot в любом каталоге, к которому имеет доступ ПО промежуточного слоя для статических файлов.
  • Обслуживайте их через метод действия, к которому применима авторизация, и получите объект FileResult:
[Authorize]
public IActionResult BannerImage()
{
    var filePath = Path.Combine(
        _env.ContentRootPath, "MyStaticFiles", "images", "red-rose.jpg");

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

Просмотр каталогов

Просмотр каталогов позволяет просматривать список каталогов в указанных каталогах.

По соображениям безопасности просмотр каталогов отключен по умолчанию. Дополнительные сведения см. в разделе Замечания по безопасности для статических файлов.

Включите просмотр каталогов с помощью:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddDirectoryBrowser();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        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();
    });
}

Приведенный выше код разрешает просмотр папки wwwroot/images с помощью URL-адреса https://<hostname>/MyImages, со ссылками на все файлы и папки:

directory browsing

Обслуживание документов по умолчанию

Страница по умолчанию является для пользователей отправной точкой на веб-сайте. Чтобы обслужить файл по умолчанию из wwwroot, не требуя, чтобы URL-адрес запроса включал имя файла, вызовите метод UseDefaultFiles:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();

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

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });
}

Для обслуживания файла по умолчанию метод UseDefaultFilesдолжен быть вызван до метода UseStaticFiles. UseDefaultFiles — это средство переопределения 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();
    }
    else
    {
        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 объединяет функции UseStaticFiles, UseDefaultFiles и при необходимости UseDirectoryBrowser.

Вызовите app.UseFileServer, чтобы включить обслуживание статических файлов и файла по умолчанию. Просмотр каталогов отключен. В следующем примере кода демонстрируется Startup.Configure с UseFileServer:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        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();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();

    app.UseFileServer(enableDirectoryBrowsing: true);

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });
}

Пусть имеется следующая иерархия каталогов:

  • wwwroot
    • css
    • images
    • js
  • 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();
    }
    else
    {
        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();
    });
}

AddDirectoryBrowser должно вызываться, когда свойство EnableDirectoryBrowsing имеет значение true.

При указанных выше коде и иерархии файлов URL-адреса будут разрешаться следующим образом:

URI Ответ
https://<hostname>/StaticFiles/images/MyImage.jpg MyStaticFiles/images/MyImage.jpg
https://<hostname>/StaticFiles MyStaticFiles/default.html

Если в каталоге MyStaticFiles отсутствует файл с именем по умолчанию, https://<hostname>/StaticFiles возвращает список содержимого каталога с доступными для перехода ссылками:

Static files list

UseDefaultFiles и UseDirectoryBrowser выполняют перенаправление на стороне клиента из целевого URI без / в конце в целевой URI с / в конце. Например, из https://<hostname>/StaticFiles в https://<hostname>/StaticFiles/. Относительные URL-адреса в каталоге StaticFiles считаются недопустимыми без косой черты в конце (/).

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();
    }
    else
    {
        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();
    }
    else
    {
        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 является более безопасной альтернативой для обслуживания файлов с нестандартными расширениями.

Предоставление файлов из нескольких расположений

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

Безопасность статических файлов

Предупреждение

Использование UseDirectoryBrowser и UseStaticFiles может привести к утечке конфиденциальной информации. Настоятельно рекомендуется отключать просмотр каталогов в рабочей среде. Тщательно проверьте, просмотр каких каталогов разрешен посредством UseStaticFiles или UseDirectoryBrowser. Весь каталог и его подкаталоги становятся общедоступными. Храните файлы, предназначенные для общего доступа, в выделенных каталогах, таких как <content_root>/wwwroot. Отделите эти файлы от представлений MVC, Razor Pages, файлов конфигурации и т. д.

  • К URL-адресам содержимого, к которому предоставлен доступ методами UseDirectoryBrowser и UseStaticFiles, применяются те же требования по регистрозависимости и запрещенным символам, что и к базовой файловой системе. Например, в Windows регистр не учитывается, а в macOS и Linux учитывается.

  • Приложения ASP.NET Core, размещенные в IIS, используют Модуль Core ASP.NET для перенаправления всех запросов к приложению, включая запросы статических файлов. Обработчик статических файлов IIS не используется и не выполняет обработку запросов.

  • Выполните следующие шаги в диспетчере служб IIS для удаления обработчика статических файлов IIS на уровне сервера или веб-сайта:

    1. Перейдите к компоненту Модули.
    2. Выберите в списке модуль StaticFileModule.
    3. Нажмите кнопку Удалить в боковой панели Действия.

Предупреждение

Если обработчик статических файлов IIS включен и модуль ASP.NET Core настроен неправильно, то статические файлы будут обслуживаться. Это может случиться, если, например, не был развернут файл web.config.

  • Размещайте файлы с кодом, включая файлы .cs и .cshtml, за пределами корневого веб-каталога проекта приложения. Таким образом, в приложении создается логическое разделение между клиентским содержимым и серверным кодом. Это предотвращает утечку серверного кода.

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