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

Примечание.

Это не последняя версия этой статьи. В текущем выпуске см . версию .NET 8 этой статьи.

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

Эта версия ASP.NET Core больше не поддерживается. Дополнительные сведения см. в статье о политике поддержки .NET и .NET Core. В текущем выпуске см . версию .NET 8 этой статьи.

Внимание

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

В текущем выпуске см . версию .NET 8 этой статьи.

Автор: Рик Андерсон (Rick Anderson)

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

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

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

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

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

  • wwwroot
    • css
    • js
    • lib

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

MapStaticAssets

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

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

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

UseStaticFiles также служит статическим файлам, но он не обеспечивает тот же уровень оптимизации, что MapStaticAssetsи . Сравнение и MapStaticAssetsоптимизация доставки статических UseStaticFiles веб-ресурсов см. в разделе "Оптимизация доставки статических веб-ресурсов".

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

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

Эта перегрузка метода 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();    //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 представляется через сегмент 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().WithStaticAssets();
app.MapRazorPages().WithStaticAssets();

app.Run();

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

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

Шаблоны ASP.NET Core вызывают MapStaticAssets перед вызовом 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");
        }
    }
    

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

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

        if (File.Exists(filePath))
        {
           return TypedResults.PhysicalFile(filePath, fileDownloadName: $"{fileName}");
        }

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

app.MapPost("/files", 
       async (IFormFile file, LinkGenerator linker, HttpContext context) =>
    {
        // Don't rely on the file.FileName as it is only metadata that can be
        // manipulated by the end-user. See 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");

app.Run();

IFormFile в предыдущем примере использует буфер памяти для отправки. Для обработки больших файлов используется потоковая передача. См. статью "Отправка больших файлов с потоковой передачей".

Полный пример см. в папке StaticFileAuth GitHub.

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

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

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

Включите просмотр каталогов с помощью 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.MapStaticAssets();

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

app.Run();

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

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

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

app.Run();

Для обслуживания файла по умолчанию метод UseDefaultFilesдолжен быть вызван до метода UseStaticFiles. UseDefaultFiles — это средство переопределения URL-адресов, которое не обслуживает файл.

При использовании UseDefaultFiles запросы к папке в wwwroot будут искать следующие файлы:

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

Первый найденный файл из списка будет обслужен, как если бы запрос включал имя файла. URL-адрес в браузере будет соответствовать запрошенному URI. Например, в примере приложения запрос на https://localhost:<port>/def/ обслуживание default.html из wwwroot/def.

Следующий код изменяет имя 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().WithStaticAssets();
app.MapRazorPages().WithStaticAssets();

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

app.Run();

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

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

builder.Services.AddDirectoryBrowser();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseFileServer(enableDirectoryBrowsing: true);

app.UseRouting();

app.UseAuthorization();

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

app.Run();

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

  • wwwroot
    • css
    • images
    • js
  • MyStaticFiles
    • defaultFiles
      • default.html
      • image3.png
    • images
      • MyImage.jpg

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

app.Run();

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

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

URI-адрес Response
https://<hostname>/StaticFiles/images/MyImage.jpg MyStaticFiles/images/MyImage.jpg
https://<hostname>/StaticFiles Список каталогов
https://<hostname>/StaticFiles/defaultFiles MyStaticFiles/defaultFiles/default.html
https://<hostname>/StaticFiles/defaultFiles/image3.png MyStaticFiles/defaultFiles//image3.png

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

Список статических файлов

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

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

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

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();
app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(
        Path.Combine(builder.Environment.ContentRootPath, "MyStaticFiles"))
});

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

  • Отображается файл /MyStaticFiles/image3.png.
  • Вспомогательные функции тегов AppendVersion изображений не применяются, так как вспомогательные функции тегов зависят WebRootFileProviderот. В WebRootFileProvider не была добавлена папка 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.MapStaticAssets();

Примечание.

Приведенный выше подход применяется к Razor приложениям Pages и MVC. Рекомендации, применимые к Blazor Web Apps, см. в разделе ASP.NET Статические файлы CoreBlazor.

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

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

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

  • URL-адреса содержимого, предоставляемого с UseDirectoryBrowserпомощью , UseStaticFilesи MapStaticAssets подвергаются ограничениям конфиденциальности регистра и символов базовой файловой системы. Например, в 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.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();

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

Автор: Рик Андерсон (Rick Anderson)

Статические файлы, такие как 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");
        }
    }
    

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

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

        if (File.Exists(filePath))
        {
            return TypedResults.PhysicalFile(filePath, fileDownloadName: $"{fileName}");
        }

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

// IFormFile uses memory buffer for uploading. For handling large file use streaming instead.
// https://video2.skills-academy.com/aspnet/core/mvc/models/file-uploads#upload-large-files-with-streaming
app.MapPost("/files", async (IFormFile file, LinkGenerator linker, HttpContext context) =>
    {
        // Don't rely on the file.FileName as it is only metadata that can be manipulated by the end-user
        // Take a look at 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");

app.Run();

Полный пример см. в папке StaticFileAuth GitHub.

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

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

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

Включите просмотр каталогов с помощью 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, со ссылками на все файлы и папки:

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

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

builder.Services.AddDirectoryBrowser();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseFileServer(enableDirectoryBrowsing: true);

app.UseRouting();

app.UseAuthorization();

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

app.Run();

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

  • wwwroot
    • 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-адрес Response
https://<hostname>/StaticFiles/images/MyImage.jpg MyStaticFiles/images/MyImage.jpg
https://<hostname>/StaticFiles MyStaticFiles/default.html

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

Список статических файлов

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

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

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

  • Отображается файл /MyStaticFiles/image3.png.
  • Вспомогательные функции тегов AppendVersion изображений не применяются, так как вспомогательные функции тегов зависят WebRootFileProviderот. В WebRootFileProvider не была добавлена папка 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();

Примечание.

Приведенный выше подход применяется к Razor приложениям Pages и MVC. Рекомендации, применимые к Blazor Web Apps, см. в разделе ASP.NET Статические файлы CoreBlazor.

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

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

Использование 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. См. разделы Корневой каталог содержимого и Корневой веб-каталог.

Метод 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, со ссылками на все файлы и папки:

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

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

builder.Services.AddDirectoryBrowser();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseFileServer(enableDirectoryBrowsing: true);

app.UseRouting();

app.UseAuthorization();

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

app.Run();

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

  • wwwroot
    • 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-адрес Response
https://<hostname>/StaticFiles/images/MyImage.jpg MyStaticFiles/images/MyImage.jpg
https://<hostname>/StaticFiles MyStaticFiles/default.html

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

Список статических файлов

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

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

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

  • Отображается файл /MyStaticFiles/image3.png.
  • Вспомогательные функции тегов AppendVersion изображений не применяются, так как вспомогательные функции тегов зависят WebRootFileProviderот. В WebRootFileProvider не была добавлена папка 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 дней).

Добавлены заголовки ответов, включая заголовок Cache-Control

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

Шаблоны 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, со ссылками на все файлы и папки:

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

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

Страница по умолчанию является для пользователей отправной точкой на веб-сайте. Чтобы обслужить файл по умолчанию из 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-адрес Response
https://<hostname>/StaticFiles/images/MyImage.jpg MyStaticFiles/images/MyImage.jpg
https://<hostname>/StaticFiles MyStaticFiles/default.html

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

Список статических файлов

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вне корневого веб-сайта проекта приложения. Таким образом, в приложении создается логическое разделение между клиентским содержимым и серверным кодом. Это предотвращает утечку серверного кода.

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