ASP.NET Core의 정적 파일

참고 항목

이 문서의 최신 버전은 아닙니다. 현재 릴리스는 이 문서의 ASP.NET Core 8.0 버전을 참조 하세요.

작성자: 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();

정적 파일은 웹 루트에 상대적인 경로를 통해 액세스할 수 있습니다. 예를 들어 웹 애플리케이션 프로젝트 템플릿에는 wwwroot 폴더 내에 여러 폴더가 포함되어 있습니다.

  • wwwroot
    • css
    • js
    • lib

wwwroot/images 폴더를 만들고 파일을 추가하는 것이 좋습니다wwwroot/images/MyImage.jpg. images 폴더의 파일에 액세스하기 위한 URI 형식은 https://<hostname>/images/<image_file_name>입니다. 예를 들어 https://localhost:5001/images/MyImage.jpg

웹 루트의 파일 제공

기본 웹앱 템플릿은 Program.cs에서 UseStaticFiles 메서드를 호출하여 정적 파일을 제공할 수 있습니다.

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 디렉터리 계층 구조는 StaticFiles URI 세그먼트를 통해 공개적으로 노출됩니다. 파일을 제공하는 https://<hostname>/StaticFiles/images/red-rose.jpg 요청입니다 red-rose.jpg .

다음 태그 참조는 다음과 같습니다.MyStaticFiles/images/red-rose.jpg

<img src="~/StaticFiles/images/red-rose.jpg" class="img" alt="A red rose" />

여러 위치에서 파일을 제공하려면 여러 위치에서 파일 제공을 참조하세요.

HTTP 응답 헤더 설정

StaticFileOptions 개체를 사용하여 HTTP 응답 헤더를 설정할 수 있습니다. 다음 코드는 웹 루트에서 제공되는 정적 파일을 구성하는 것 외에도 Cache-Control 헤더를 설정합니다.

var builder = WebApplication.CreateBuilder(args);

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

var app = builder.Build();

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

app.UseHttpsRedirection();

var cacheMaxAgeOneWeek = (60 * 60 * 24 * 7).ToString();
app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = ctx =>
    {
        ctx.Context.Response.Headers.Append(
             "Cache-Control", $"public, max-age={cacheMaxAgeOneWeek}");
    }
});

app.UseAuthorization();

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

app.Run();

위의 코드는 로컬 캐시에서 정적 파일을 1주 동안(604,800초) 공개적으로 사용할 수 있도록 합니다.

정적 파일 권한 부여

ASP.NET Core 템플릿은 UseAuthorization을 호출하기 전에 UseStaticFiles를 호출합니다. 대부분의 앱은 이 패턴을 따릅니다. 정적 파일 미들웨어가 권한 부여 미들웨어 전에 호출되는 경우:

  • 정적 파일에서 권한 부여 검사가 수행되지 않습니다.
  • wwwroot 아래에 있는 정적 파일과 같이 정적 파일 미들웨어가 제공하는 정적 파일은 공개적으로 액세스할 수 있습니다.

권한 부여를 기반으로 정적 파일을 제공하려면 다음을 수행합니다.

  • wwwroot 외부에 정적 파일을 저장합니다.
  • UseAuthorization을 호출한 후 경로를 지정하여 UseStaticFiles를 호출합니다.
  • 대체 권한 부여 정책을 설정합니다.
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 Pages 등과 같은 엔드포인트는 대체 권한 부여 정책을 사용하지 않습니다. 예를 들어 [AllowAnonymous] 또는 [Authorize(PolicyName="MyPolicy")]가 있는 Razor Pages, 컨트롤러 또는 작업 메서드는 대체 권한 부여 정책이 아닌 적용된 권한 부여 특성을 사용합니다.

RequireAuthenticatedUserDenyAnonymousAuthorizationRequirement를 현재 인스턴스에 추가하여 현재 사용자가 인증될 것을 요구합니다.

기본 정적 파일 미들웨어(app.UseStaticFiles();)가 UseAuthentication 전에 호출되기 때문에 wwwroot 아래의 정적 자산은 공개적으로 액세스할 수 있습니다. 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://learn.microsoft.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 폴더를 참조하세요.

디렉터리 검색

디렉터리 검색을 통해 지정된 디렉터리 내에 디렉터리를 나열할 수 있습니다.

기본적으로 디렉터리 검색은 보안상의 이유로 사용하지 않도록 설정됩니다. 자세한 내용은 정적 파일의 보안 고려 사항을 참조하세요.

AddDirectoryBrowserUseDirectoryBrowser를 통해 디렉터리 검색을 사용하도록 설정합니다.

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

위의 코드를 통해 URL https://<hostname>/MyImages를 사용하여 각 파일 및 폴더에 대한 링크가 제공되는 wwwroot/images 폴더의 디렉터리 검색을 사용할 수 있습니다.

directory browsing

AddDirectoryBrowserHtmlEncoder를 비롯하여 디렉터리 검색 미들웨어에 필요한 서비스를 추가합니다. 이러한 서비스는 AddRazorPages와 같은 다른 호출에서 추가될 수 있지만 서비스가 모든 앱에 추가되도록 AddDirectoryBrowser를 호출하는 것이 좋습니다.

기본 문서 제공

기본 페이지를 설정하면 방문자에게 사이트의 시작 지점이 제공됩니다. 요청 URL에 파일 이름을 포함하도록 요구하지 않고 wwwroot에서 기본 파일을 제공하려면 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();

기본 파일을 제공하려면 UseStaticFiles 전에 UseDefaultFiles를 먼저 호출해야 합니다. 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

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

AddDirectoryBrowserEnableDirectoryBrowsing 속성 값이 true인 경우 호출해야 합니다.

URL은 위의 파일 계층 구조 및 코드를 사용하여 다음과 같이 확인됩니다.

URI 응답
https://<hostname>/StaticFiles/images/MyImage.jpg MyStaticFiles/images/MyImage.jpg
https://<hostname>/StaticFiles MyStaticFiles/default.html

MyStaticFiles 디렉터리에 기본 이름이 지정된 파일이 없는 경우 https://<hostname>/StaticFiles는 클릭 가능한 링크와 함께 디렉터리 목록을 반환합니다.

Static files list

UseDefaultFilesUseDirectoryBrowser는 후행 /가 없는 대상 URI에서 후행 /가 있는 대상 URI로 클라이언트 쪽 리디렉션을 수행합니다. 예를 들어 https://<hostname>/StaticFiles에서 https://<hostname>/StaticFiles/로 리디렉션합니다. StaticFiles 디렉터리 내의 상대 URL은 후행 슬래시(/)가 있어야 유효합니다. 단 DefaultFilesOptionsRedirectToAppendTrailingSlash 옵션을 사용하는 경우에는 제외입니다.

FileExtensionContentTypeProvider

FileExtensionContentTypeProvider 클래스에는 MIME 콘텐츠 형식에 대한 파일 확장명 매핑 역할을 하는 Mappings 속성이 포함되어 있습니다. 다음 샘플에서는 여러 파일 확장명이 알려진 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();

위의 코드를 사용하면 알 수 없는 콘텐츠 형식의 파일에 대한 요청은 이미지로 반환됩니다.

Warning

ServeUnknownFileTypes를 사용하도록 설정하는 것은 보안상 위험합니다. 기본적으로 비활성화되어 있으며 사용은 권장되지 않습니다. FileExtensionContentTypeProvider는 비표준 확장명을 가진 파일을 제공하는 것보다 안전한 대체 방법을 제공합니다.

여러 위치에서 파일 제공

/MyStaticFiles/image3.png 파일을 표시하는 다음 Razor 페이지를 고려하세요.

@page

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

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

UseStaticFilesUseFileServer는 기본적으로 wwwroot를 가리키는 파일 공급자로 설정됩니다. 다른 위치에서 파일을 제공하기 위해 다른 파일 공급자와 UseStaticFilesUseFileServer의 추가 인스턴스를 제공할 수 있습니다. 다음 예제에서는 UseStaticFiles를 두 번 호출하여 wwwrootMyStaticFiles 모두에서 파일을 제공합니다.

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

위의 코드를 사용하여 다음을 수행합니다.

다음 코드는 WebRootFileProvider를 업데이트하여 이미지 태그 도우미가 버전을 제공할 수 있도록 합니다.

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

var compositeProvider = new CompositeFileProvider(webRootProvider,
                                                  newPathProvider);

// Update the default provider.
app.Environment.WebRootFileProvider = compositeProvider;

app.UseStaticFiles();

정적 파일의 보안 고려 사항

Warning

UseDirectoryBrowserUseStaticFiles는 비밀 정보를 누출 할 수 있습니다. 프로덕션 환경에서는 디렉터리 검색을 비활성화하는 것이 좋습니다. UseStaticFiles 또는 UseDirectoryBrowser를 통해 어떤 디렉터리가 활성화되었는지 주의 깊게 검토하세요. 전체 디렉터리와 해당 하위 디렉터리는 공개적으로 액세스할 수 있습니다. <content_root>/wwwroot와 같은 전용 디렉터리에 공개적으로 제공하는 데 적합한 파일을 저장합니다. MVC 뷰, Razor Pages, 구성 파일 등과 해당 파일을 분리합니다.

  • UseDirectoryBrowserUseStaticFiles로 노출된 콘텐츠에 대한 URL은 대/소문자 구분 및 기본 파일 시스템의 문자 제한이 적용됩니다. 예를 들어 Windows는 대/소문자를 구분하지 않지만 macOS 및 Linux는 대/소문자를 구분합니다.

  • IIS에서 호스팅되는 ASP.NET Core 앱은 ASP.NET Core 모듈을 사용하여 정적 파일 요청을 비롯한 모든 요청을 앱에 전달합니다. IIS 정적 파일 처리기는 사용되지 않으며 요청을 처리할 수 없습니다.

  • 서버 또는 웹 사이트 수준에서 IIS 정적 파일 처리기를 제거하려면 IIS 관리자에서 다음 단계를 완료합니다.

    1. 모듈 기능으로 이동합니다.
    2. 목록에서 StaticFileModule을 선택합니다.
    3. 동작 사이드바에서 제거를 클릭합니다.

Warning

IIS 정적 파일 처리기를 사용하도록 설정되었으며 그리고 ASP.NET Core 모듈이 올바르게 구성되지 않은 경우, 정적 파일이 제공됩니다. 이는 예를 들어 web.config 파일이 배포되지 않는 경우에 발생합니다.

  • 앱 프로젝트의 웹 루트 외부에 포함 .cs.cshtml외부에 코드 파일을 배치합니다. 따라서 논리적 분리가 앱의 클라이언트 쪽 콘텐츠 및 서버 기반 코드 사이에 만들어집니다. 그러면 서버 쪽 코드가 유출되지 않습니다.

IWebHostEnvironment.WebRootPath를 업데이트하여 wwwroot 외부의 파일 제공

IWebHostEnvironment.WebRootPathwwwroot 이외의 폴더로 설정된 경우:

  • 개발 환경에서는 wwwroot 및 업데이트된 IWebHostEnvironment.WebRootPath 모두에서 찾은 정적 자산이 wwwroot에서 제공됩니다.
  • 개발 환경 이외의 환경에서는 중복된 정적 자산이 업데이트된 IWebHostEnvironment.WebRootPath 폴더에서 제공됩니다.

빈 웹 템플릿으로 만든 웹앱을 고려합니다.

  • wwwrootwwwroot-customIndex.html 파일 포함

  • WebRootPath = "wwwroot-custom"을 설정하는 다음과 같은 업데이트된 Program.cs 파일 포함

    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에서 중복 명명된 자산을 삭제합니다.

  • Properties/launchSettings.json에서 "ASPNETCORE_ENVIRONMENT""Development" 이외의 값으로 설정합니다.

  • 프로젝트 파일에서 <StaticWebAssetsEnabled>false</StaticWebAssetsEnabled>를 설정하여 정적 웹 자산을 완전히 사용하지 않도록 설정합니다. 경고, 정적 웹 자산을 사용하지 않도록 설정하면 Razor 클래스 라이브러리를 사용할 수 없습니다.

  • 프로젝트 파일에 다음 JSON을 추가합니다.

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

다음 코드에서는 IWebHostEnvironment.WebRootPath를 개발이 아닌 값으로 업데이트하여 중복 콘텐츠가 wwwroot가 아니라 wwwroot-custom에서 반환되도록 합니다.

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

정적 파일은 웹 루트에 상대적인 경로를 통해 액세스할 수 있습니다. 예를 들어 웹 애플리케이션 프로젝트 템플릿에는 wwwroot 폴더 내에 여러 폴더가 포함되어 있습니다.

  • wwwroot
    • css
    • js
    • lib

wwwroot/images 폴더를 만들고 파일을 추가하는 것이 좋습니다wwwroot/images/MyImage.jpg. images 폴더의 파일에 액세스하기 위한 URI 형식은 https://<hostname>/images/<image_file_name>입니다. 예를 들어 https://localhost:5001/images/MyImage.jpg

웹 루트의 파일 제공

기본 웹앱 템플릿은 Program.cs에서 UseStaticFiles 메서드를 호출하여 정적 파일을 제공할 수 있습니다.

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 디렉터리 계층 구조는 StaticFiles URI 세그먼트를 통해 공개적으로 노출됩니다. 파일을 제공하는 https://<hostname>/StaticFiles/images/red-rose.jpg 요청입니다 red-rose.jpg .

다음 태그 참조는 다음과 같습니다.MyStaticFiles/images/red-rose.jpg

<img src="~/StaticFiles/images/red-rose.jpg" class="img" alt="A red rose" />

여러 위치에서 파일을 제공하려면 여러 위치에서 파일 제공을 참조하세요.

HTTP 응답 헤더 설정

StaticFileOptions 개체를 사용하여 HTTP 응답 헤더를 설정할 수 있습니다. 다음 코드는 웹 루트에서 제공되는 정적 파일을 구성하는 것 외에도 Cache-Control 헤더를 설정합니다.

var builder = WebApplication.CreateBuilder(args);

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

var app = builder.Build();

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

app.UseHttpsRedirection();

var cacheMaxAgeOneWeek = (60 * 60 * 24 * 7).ToString();
app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = ctx =>
    {
        ctx.Context.Response.Headers.Append(
             "Cache-Control", $"public, max-age={cacheMaxAgeOneWeek}");
    }
});

app.UseAuthorization();

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

app.Run();

위의 코드는 로컬 캐시에서 정적 파일을 1주 동안(604,800초) 공개적으로 사용할 수 있도록 합니다.

정적 파일 권한 부여

ASP.NET Core 템플릿은 UseAuthorization을 호출하기 전에 UseStaticFiles를 호출합니다. 대부분의 앱은 이 패턴을 따릅니다. 정적 파일 미들웨어가 권한 부여 미들웨어 전에 호출되는 경우:

  • 정적 파일에서 권한 부여 검사가 수행되지 않습니다.
  • wwwroot 아래에 있는 정적 파일과 같이 정적 파일 미들웨어가 제공하는 정적 파일은 공개적으로 액세스할 수 있습니다.

권한 부여를 기반으로 정적 파일을 제공하려면 다음을 수행합니다.

  • wwwroot 외부에 정적 파일을 저장합니다.
  • UseAuthorization을 호출한 후 경로를 지정하여 UseStaticFiles를 호출합니다.
  • 대체 권한 부여 정책을 설정합니다.
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 Pages 등과 같은 엔드포인트는 대체 권한 부여 정책을 사용하지 않습니다. 예를 들어 [AllowAnonymous] 또는 [Authorize(PolicyName="MyPolicy")]가 있는 Razor Pages, 컨트롤러 또는 작업 메서드는 대체 권한 부여 정책이 아닌 적용된 권한 부여 특성을 사용합니다.

RequireAuthenticatedUserDenyAnonymousAuthorizationRequirement를 현재 인스턴스에 추가하여 현재 사용자가 인증될 것을 요구합니다.

기본 정적 파일 미들웨어(app.UseStaticFiles();)가 UseAuthentication 전에 호출되기 때문에 wwwroot 아래의 정적 자산은 공개적으로 액세스할 수 있습니다. 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");
    }
}

디렉터리 검색

디렉터리 검색을 통해 지정된 디렉터리 내에 디렉터리를 나열할 수 있습니다.

기본적으로 디렉터리 검색은 보안상의 이유로 사용하지 않도록 설정됩니다. 자세한 내용은 정적 파일의 보안 고려 사항을 참조하세요.

AddDirectoryBrowserUseDirectoryBrowser를 통해 디렉터리 검색을 사용하도록 설정합니다.

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

위의 코드를 통해 URL https://<hostname>/MyImages를 사용하여 각 파일 및 폴더에 대한 링크가 제공되는 wwwroot/images 폴더의 디렉터리 검색을 사용할 수 있습니다.

directory browsing

AddDirectoryBrowserHtmlEncoder를 비롯하여 디렉터리 검색 미들웨어에 필요한 서비스를 추가합니다. 이러한 서비스는 AddRazorPages와 같은 다른 호출에서 추가될 수 있지만 서비스가 모든 앱에 추가되도록 AddDirectoryBrowser를 호출하는 것이 좋습니다.

기본 문서 제공

기본 페이지를 설정하면 방문자에게 사이트의 시작 지점이 제공됩니다. 요청 URL에 파일 이름을 포함하도록 요구하지 않고 wwwroot에서 기본 파일을 제공하려면 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();

기본 파일을 제공하려면 UseStaticFiles 전에 UseDefaultFiles를 먼저 호출해야 합니다. 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

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

AddDirectoryBrowserEnableDirectoryBrowsing 속성 값이 true인 경우 호출해야 합니다.

URL은 위의 파일 계층 구조 및 코드를 사용하여 다음과 같이 확인됩니다.

URI 응답
https://<hostname>/StaticFiles/images/MyImage.jpg MyStaticFiles/images/MyImage.jpg
https://<hostname>/StaticFiles MyStaticFiles/default.html

MyStaticFiles 디렉터리에 기본 이름이 지정된 파일이 없는 경우 https://<hostname>/StaticFiles는 클릭 가능한 링크와 함께 디렉터리 목록을 반환합니다.

Static files list

UseDefaultFilesUseDirectoryBrowser는 후행 /가 없는 대상 URI에서 후행 /가 있는 대상 URI로 클라이언트 쪽 리디렉션을 수행합니다. 예를 들어 https://<hostname>/StaticFiles에서 https://<hostname>/StaticFiles/로 리디렉션합니다. StaticFiles 디렉터리 내의 상대 URL은 후행 슬래시(/)가 있어야 유효합니다. 단 DefaultFilesOptionsRedirectToAppendTrailingSlash 옵션을 사용하는 경우에는 제외입니다.

FileExtensionContentTypeProvider

FileExtensionContentTypeProvider 클래스에는 MIME 콘텐츠 형식에 대한 파일 확장명 매핑 역할을 하는 Mappings 속성이 포함되어 있습니다. 다음 샘플에서는 여러 파일 확장명이 알려진 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();

위의 코드를 사용하면 알 수 없는 콘텐츠 형식의 파일에 대한 요청은 이미지로 반환됩니다.

Warning

ServeUnknownFileTypes를 사용하도록 설정하는 것은 보안상 위험합니다. 기본적으로 비활성화되어 있으며 사용은 권장되지 않습니다. FileExtensionContentTypeProvider는 비표준 확장명을 가진 파일을 제공하는 것보다 안전한 대체 방법을 제공합니다.

여러 위치에서 파일 제공

/MyStaticFiles/image3.png 파일을 표시하는 다음 Razor 페이지를 고려하세요.

@page

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

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

UseStaticFilesUseFileServer는 기본적으로 wwwroot를 가리키는 파일 공급자로 설정됩니다. 다른 위치에서 파일을 제공하기 위해 다른 파일 공급자와 UseStaticFilesUseFileServer의 추가 인스턴스를 제공할 수 있습니다. 다음 예제에서는 UseStaticFiles를 두 번 호출하여 wwwrootMyStaticFiles 모두에서 파일을 제공합니다.

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

위의 코드를 사용하여 다음을 수행합니다.

다음 코드는 WebRootFileProvider를 업데이트하여 이미지 태그 도우미가 버전을 제공할 수 있도록 합니다.

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

var compositeProvider = new CompositeFileProvider(webRootProvider,
                                                  newPathProvider);

// Update the default provider.
app.Environment.WebRootFileProvider = compositeProvider;

app.UseStaticFiles();

정적 파일의 보안 고려 사항

Warning

UseDirectoryBrowserUseStaticFiles는 비밀 정보를 누출 할 수 있습니다. 프로덕션 환경에서는 디렉터리 검색을 비활성화하는 것이 좋습니다. UseStaticFiles 또는 UseDirectoryBrowser를 통해 어떤 디렉터리가 활성화되었는지 주의 깊게 검토하세요. 전체 디렉터리와 해당 하위 디렉터리는 공개적으로 액세스할 수 있습니다. <content_root>/wwwroot와 같은 전용 디렉터리에 공개적으로 제공하는 데 적합한 파일을 저장합니다. MVC 뷰, Razor Pages, 구성 파일 등과 해당 파일을 분리합니다.

  • UseDirectoryBrowserUseStaticFiles로 노출된 콘텐츠에 대한 URL은 대/소문자 구분 및 기본 파일 시스템의 문자 제한이 적용됩니다. 예를 들어 Windows는 대/소문자를 구분하지 않지만 macOS 및 Linux는 대/소문자를 구분합니다.

  • IIS에서 호스팅되는 ASP.NET Core 앱은 ASP.NET Core 모듈을 사용하여 정적 파일 요청을 비롯한 모든 요청을 앱에 전달합니다. IIS 정적 파일 처리기는 사용되지 않으며 요청을 처리할 수 없습니다.

  • 서버 또는 웹 사이트 수준에서 IIS 정적 파일 처리기를 제거하려면 IIS 관리자에서 다음 단계를 완료합니다.

    1. 모듈 기능으로 이동합니다.
    2. 목록에서 StaticFileModule을 선택합니다.
    3. 동작 사이드바에서 제거를 클릭합니다.

Warning

IIS 정적 파일 처리기를 사용하도록 설정되었으며 그리고 ASP.NET Core 모듈이 올바르게 구성되지 않은 경우, 정적 파일이 제공됩니다. 이는 예를 들어 web.config 파일이 배포되지 않는 경우에 발생합니다.

  • 앱 프로젝트의 웹 루트 외부에 포함 .cs.cshtml외부에 코드 파일을 배치합니다. 따라서 논리적 분리가 앱의 클라이언트 쪽 콘텐츠 및 서버 기반 코드 사이에 만들어집니다. 그러면 서버 쪽 코드가 유출되지 않습니다.

IWebHostEnvironment.WebRootPath를 업데이트하여 wwwroot 외부의 파일 제공

IWebHostEnvironment.WebRootPathwwwroot 이외의 폴더로 설정된 경우:

  • 개발 환경에서는 wwwroot 및 업데이트된 IWebHostEnvironment.WebRootPath 모두에서 찾은 정적 자산이 wwwroot에서 제공됩니다.
  • 개발 환경 이외의 환경에서는 중복된 정적 자산이 업데이트된 IWebHostEnvironment.WebRootPath 폴더에서 제공됩니다.

빈 웹 템플릿으로 만든 웹앱을 고려합니다.

  • wwwrootwwwroot-customIndex.html 파일 포함

  • WebRootPath = "wwwroot-custom"을 설정하는 다음과 같은 업데이트된 Program.cs 파일 포함

    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에서 중복 명명된 자산을 삭제합니다.

  • Properties/launchSettings.json에서 "ASPNETCORE_ENVIRONMENT""Development" 이외의 값으로 설정합니다.

  • 프로젝트 파일에서 <StaticWebAssetsEnabled>false</StaticWebAssetsEnabled>를 설정하여 정적 웹 자산을 완전히 사용하지 않도록 설정합니다. 경고, 정적 웹 자산을 사용하지 않도록 설정하면 Razor 클래스 라이브러리를 사용할 수 없습니다.

  • 프로젝트 파일에 다음 JSON을 추가합니다.

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

다음 코드에서는 IWebHostEnvironment.WebRootPath를 개발이 아닌 값으로 업데이트하여 중복 콘텐츠가 wwwroot가 아니라 wwwroot-custom에서 반환되도록 합니다.

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

위의 코드는 웹앱 템플릿을 사용하여 만들었습니다.

정적 파일은 웹 루트에 상대적인 경로를 통해 액세스할 수 있습니다. 예를 들어 웹 애플리케이션 프로젝트 템플릿에는 wwwroot 폴더 내에 여러 폴더가 포함되어 있습니다.

  • wwwroot
    • css
    • js
    • lib

wwwroot/images 폴더를 만들고 파일을 추가하는 것이 좋습니다wwwroot/images/MyImage.jpg. images 폴더의 파일에 액세스하기 위한 URI 형식은 https://<hostname>/images/<image_file_name>입니다. 예를 들어 https://localhost:5001/images/MyImage.jpg

웹 루트의 파일 제공

기본 웹앱 템플릿은 Startup.Configure에서 UseStaticFiles 메서드를 호출하여 정적 파일을 제공할 수 있습니다.

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 디렉터리 계층 구조는 StaticFiles URI 세그먼트를 통해 공개적으로 노출됩니다. 파일을 제공하는 https://<hostname>/StaticFiles/images/red-rose.jpg 요청입니다 red-rose.jpg .

다음 태그 참조는 다음과 같습니다.MyStaticFiles/images/red-rose.jpg

<img src="~/StaticFiles/images/red-rose.jpg" class="img" alt="A red rose" />

HTTP 응답 헤더 설정

StaticFileOptions 개체를 사용하여 HTTP 응답 헤더를 설정할 수 있습니다. 다음 코드는 웹 루트에서 제공되는 정적 파일을 구성하는 것 외에도 Cache-Control 헤더를 설정합니다.

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

위의 코드는 최대 사용 기간을 604,800초(7일)로 설정합니다.

Response headers showing the Cache-Control header has been added

정적 파일 권한 부여

ASP.NET Core 템플릿은 UseAuthorization을 호출하기 전에 UseStaticFiles를 호출합니다. 대부분의 앱은 이 패턴을 따릅니다. 정적 파일 미들웨어가 권한 부여 미들웨어 전에 호출되는 경우:

  • 정적 파일에서 권한 부여 검사가 수행되지 않습니다.
  • wwwroot 아래에 있는 정적 파일과 같이 정적 파일 미들웨어가 제공하는 정적 파일은 공개적으로 액세스할 수 있습니다.

권한 부여를 기반으로 정적 파일을 제공하려면 다음을 수행합니다.

  • wwwroot 외부에 정적 파일을 저장합니다.
  • UseAuthorization을 호출한 후 경로를 지정하여 UseStaticFiles를 호출합니다.
  • 대체 권한 부여 정책을 설정합니다.
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 Pages 등과 같은 엔드포인트는 대체 권한 부여 정책을 사용하지 않습니다. 예를 들어 [AllowAnonymous] 또는 [Authorize(PolicyName="MyPolicy")]가 있는 Razor Pages, 컨트롤러 또는 작업 메서드는 대체 권한 부여 정책이 아닌 적용된 권한 부여 특성을 사용합니다.

RequireAuthenticatedUserDenyAnonymousAuthorizationRequirement를 현재 인스턴스에 추가하여 현재 사용자가 인증될 것을 요구합니다.

기본 정적 파일 미들웨어(app.UseStaticFiles();)가 UseAuthentication 전에 호출되기 때문에 wwwroot 아래의 정적 자산은 공개적으로 액세스할 수 있습니다. 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();
    });
}

위의 코드를 통해 URL https://<hostname>/MyImages를 사용하여 각 파일 및 폴더에 대한 링크가 제공되는 wwwroot/images 폴더의 디렉터리 검색을 사용할 수 있습니다.

directory browsing

기본 문서 제공

기본 페이지를 설정하면 방문자에게 사이트의 시작 지점이 제공됩니다. 요청 URL에 파일 이름을 포함하도록 요구하지 않고 wwwroot에서 기본 파일을 제공하려면 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();
    });
}

기본 파일을 제공하려면 UseStaticFiles 전에 UseDefaultFiles를 먼저 호출해야 합니다. 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

UseFileServerUseStaticFiles, UseDefaultFiles 및 선택적으로 UseDirectoryBrowser의 기능을 병행합니다.

app.UseFileServer를 호출하여 정적 파일 및 기본 파일을 제공할 수 있도록 합니다. 디렉터리 검색은 활성화되지 않습니다. 다음 코드는 UseFileServer와 함께 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();

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

AddDirectoryBrowserEnableDirectoryBrowsing 속성 값이 true인 경우 호출해야 합니다.

URL은 파일 계층 구조 및 이전 코드를 사용하여 다음과 같이 확인합니다.

URI 응답
https://<hostname>/StaticFiles/images/MyImage.jpg MyStaticFiles/images/MyImage.jpg
https://<hostname>/StaticFiles MyStaticFiles/default.html

MyStaticFiles 디렉터리에 기본 이름이 지정된 파일이 없는 경우 https://<hostname>/StaticFiles는 클릭 가능한 링크와 함께 디렉터리 목록을 반환합니다.

Static files list

UseDefaultFilesUseDirectoryBrowser는 후행 /가 없는 대상 URI에서 후행 /가 있는 대상 URI로 클라이언트 쪽 리디렉션을 수행합니다. 예를 들어 https://<hostname>/StaticFiles에서 https://<hostname>/StaticFiles/로 리디렉션합니다. StaticFiles 디렉터리 내의 상대 URL은 후행 슬래시(/)가 있어야 유효합니다.

FileExtensionContentTypeProvider

FileExtensionContentTypeProvider 클래스에는 MIME 콘텐츠 형식에 대한 파일 확장명 매핑 역할을 하는 Mappings 속성이 포함되어 있습니다. 다음 샘플에서는 여러 파일 확장명이 알려진 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();
    });
}

위의 코드를 사용하면 알 수 없는 콘텐츠 형식의 파일에 대한 요청은 이미지로 반환됩니다.

Warning

ServeUnknownFileTypes를 사용하도록 설정하는 것은 보안상 위험합니다. 기본적으로 비활성화되어 있으며 사용은 권장되지 않습니다. FileExtensionContentTypeProvider는 비표준 확장명을 가진 파일을 제공하는 것보다 안전한 대체 방법을 제공합니다.

여러 위치에서 파일 제공

UseStaticFilesUseFileServer는 기본적으로 wwwroot를 가리키는 파일 공급자로 설정됩니다. 다른 위치에서 파일을 제공하기 위해 다른 파일 공급자와 UseStaticFilesUseFileServer의 추가 인스턴스를 제공할 수 있습니다. 자세한 내용은 GitHub 이슈를 참조하세요.

정적 파일의 보안 고려 사항

Warning

UseDirectoryBrowserUseStaticFiles는 비밀 정보를 누출 할 수 있습니다. 프로덕션 환경에서는 디렉터리 검색을 비활성화하는 것이 좋습니다. UseStaticFiles 또는 UseDirectoryBrowser를 통해 어떤 디렉터리가 활성화되었는지 주의 깊게 검토하세요. 전체 디렉터리와 해당 하위 디렉터리는 공개적으로 액세스할 수 있습니다. <content_root>/wwwroot와 같은 전용 디렉터리에 공개적으로 제공하는 데 적합한 파일을 저장합니다. MVC 뷰, Razor Pages, 구성 파일 등과 해당 파일을 분리합니다.

  • UseDirectoryBrowserUseStaticFiles로 노출된 콘텐츠에 대한 URL은 대/소문자 구분 및 기본 파일 시스템의 문자 제한이 적용됩니다. 예를 들어 Windows는 대/소문자를 구분하지 않지만 macOS 및 Linux는 대/소문자를 구분합니다.

  • IIS에서 호스팅되는 ASP.NET Core 앱은 ASP.NET Core 모듈을 사용하여 정적 파일 요청을 비롯한 모든 요청을 앱에 전달합니다. IIS 정적 파일 처리기는 사용되지 않으며 요청을 처리할 수 없습니다.

  • 서버 또는 웹 사이트 수준에서 IIS 정적 파일 처리기를 제거하려면 IIS 관리자에서 다음 단계를 완료합니다.

    1. 모듈 기능으로 이동합니다.
    2. 목록에서 StaticFileModule을 선택합니다.
    3. 동작 사이드바에서 제거를 클릭합니다.

Warning

IIS 정적 파일 처리기를 사용하도록 설정되었으며 그리고 ASP.NET Core 모듈이 올바르게 구성되지 않은 경우, 정적 파일이 제공됩니다. 이는 예를 들어 web.config 파일이 배포되지 않는 경우에 발생합니다.

  • 앱 프로젝트의 웹 루트 외부에 포함 .cs.cshtml외부에 코드 파일을 배치합니다. 따라서 논리적 분리가 앱의 클라이언트 쪽 콘텐츠 및 서버 기반 코드 사이에 만들어집니다. 그러면 서버 쪽 코드가 유출되지 않습니다.

추가 리소스