Compartilhar via


Mapear arquivos estáticos no ASP.NET Core

Por Rick Anderson

Arquivos estáticos, como HTML, CSS, imagens e JavaScript, são ativos que um aplicativo ASP.NET Core fornece diretamente para os clientes por padrão.

Para obter Blazor diretrizes de arquivos estáticos, que adiciona ou substitui as diretrizes neste artigo, consulte Arquivos estáticos do ASP.NET Core Blazor.

Servir arquivos estáticos

Os arquivos estáticos são armazenados no diretório raiz Web do projeto. O diretório padrão é {content root}/wwwroot, mas pode ser alterado com o método UseWebRoot. Para obter mais informações, consulte Raiz de conteúdo e Raiz web.

O método CreateBuilder define a raiz do conteúdo como o diretório atual:

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

Os arquivos estáticos são acessíveis por meio de um caminho relativo à raiz Web. Por exemplo, os modelos de projeto do Aplicativo Web contêm várias pastas dentro da pasta wwwroot:

  • wwwroot
    • css
    • js
    • lib

Considere um aplicativo com o wwwroot/images/MyImage.jpg arquivo. O formato de URI para acessar um arquivo na pasta images é https://<hostname>/images/<image_file_name>. Por exemplo, https://localhost:5001/images/MyImage.jpg

Convenções de ponto de extremidade de encaminhamento de Mapear Ativos Estáticos (MapStaticAssets)

A criação de aplicativos Web com bom desempenho requer a otimização do fornecimento de ativos ao navegador. As possíveis otimizações com MapStaticAssets incluem:

  • Sirva um determinado ativo uma vez até que o arquivo seja alterado ou o navegador limpe seu cache. Defina os cabeçalhos ETag e Last-Modified .
  • Impedir que o navegador use ativos antigos ou obsoletos depois que um aplicativo for atualizado. Definir o cabeçalho Last-Modified.
  • Configurar os cabeçalhos de cache adequadamente.
  • Use o Middleware de Cache.
  • Fornecer versões comprimidas dos ativos quando possível. Essa otimização não inclui a minificação.
  • Use um CDN para distribuir os conteúdos mais próximos do usuário.
  • Ativos de impressão digital para evitar reutilizações de versões antigas de arquivos.

MapStaticAssets:

  • Integra as informações coletadas sobre ativos da Web estáticos durante o processo de compilação e publicação com uma biblioteca de runtime que processa essas informações para otimizar o serviço de arquivos para o navegador.
  • São convenções de pontos de extremidade de roteamento que otimizam a entrega de ativos estáticos no aplicativo. Ele foi projetado para funcionar com todas as estruturas de interface do usuário, incluindo Blazor, Razor Pages e MVC.

MapStaticAssets contra UseStaticFiles

MapStaticAssets está disponível no ASP.NET Core no .NET 9 ou posterior. UseStaticFiles deve ser usado em versões anteriores ao .NET 9.

UseStaticFiles serve arquivos estáticos, mas não fornece o mesmo nível de otimização que MapStaticAssets. MapStaticAssets é otimizado para atender ativos dos quais o aplicativo tem conhecimento no runtime. Se o aplicativo atender ativos de outros locais, como disco ou recursos inseridos, o UseStaticFiles deverá ser usado.

O Mapa de Ativos Estáticos fornece os seguintes benefícios que não estão disponíveis ao chamar UseStaticFiles:

  • Compactação no tempo de compilação para todos os recursos no aplicativo, incluindo JavaScript (JS) e folhas de estilo, mas excluindo recursos de imagem e fonte que já estão compactados. A compactação Gzip (Content-Encoding: gz) é usada durante o desenvolvimento. A compactação Gzip com Brotli (Content-Encoding: br) é usada durante a publicação.
  • Impressão Digital para todos os ativos em tempo de compilação com uma cadeia de caracteres codificada em Base64do hash SHA-256 do conteúdo de cada arquivo. Isso impede a reutilização de uma versão antiga de um arquivo, mesmo que o arquivo antigo seja armazenado em cache. Os ativos com impressão digital são armazenados em cache usando a immutable diretiva, o que faz com que o navegador nunca mais solicite o ativo até que ele seja alterado. Para navegadores que não dão suporte à immutable diretiva, uma max-age diretiva é adicionada.
    • Mesmo que um ativo não seja fingerprintado, o conteúdo baseado em ETags é gerado para cada ativo estático usando o hash de impressão digital do arquivo como valor ETag. Isso garante que o navegador baixe apenas um arquivo se o conteúdo for alterado (ou se o arquivo estiver sendo baixado pela primeira vez).
    • Internamente, a estrutura mapeia ativos físicos para suas impressões digitais, o que permite ao aplicativo:
      • Encontre ativos gerados automaticamente, como Razor CSS com escopo de componente para o Blazorrecurso de isolamento CSS e ativos JS descritos por JS mapas de importação.
      • Gere tags de link no conteúdo <head> da página para pré-carregar recursos.
  • Durante os testes de desenvolvimento de Recarga Dinâmica do Visual Studio:
    • As informações de integridade são removidas dos ativos para evitar problemas quando um arquivo é alterado enquanto o aplicativo está em execução.
    • Os ativos estáticos não são armazenados em cache para garantir que o navegador sempre recupere o conteúdo atual.

O Mapa de Ativos Estáticos não fornece recursos para minificação ou outras transformações de arquivo. A minificação geralmente é feita com código personalizado ou ferramentas de terceiros.

Os seguintes recursos têm suporte com UseStaticFiles, mas não com MapStaticAssets:

Fornecer arquivos na raiz Web

Os templates de aplicativo Web padrão chamam o método MapStaticAssets no Program.cs, que permite que arquivos estáticos sejam servidos.

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

A sobrecarga do método MapStaticAssets sem parâmetro marca os arquivos raiz Web como fornecíveis. As seguintes referências de marcação wwwroot/images/MyImage.jpg:

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

Na marcação anterior, o caractere ~ de til aponta para a raiz Web.

Fornecer arquivos fora do diretório base

Considere uma hierarquia de diretórios na qual os arquivos estáticos a serem fornecidos residam fora da raiz Web:

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

Uma solicitação pode acessar o arquivo red-rose.jpg configurando o middleware de arquivo estático da seguinte maneira:

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

No código anterior, a hierarquia de diretórios MyStaticFiles é exposta publicamente por meio do segmento do URI StaticFiles. Uma solicitação para https://<hostname>/StaticFiles/images/red-rose.jpg fornece ao arquivo red-rose.jpg.

As seguintes referências de marcação MyStaticFiles/images/red-rose.jpg:

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

Para fornecer arquivos de vários locais, consulte Fornecer arquivos de vários locais.

Definir cabeçalhos de resposta HTTP

Um objeto StaticFileOptions pode ser usado para definir cabeçalhos de resposta HTTP. Além de configurar a distribuição de arquivos estáticos a partir da Raiz Web, o código a seguir define o cabeçalho 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();

O código anterior disponibiliza arquivos estáticos publicamente no cache local por uma semana.

Autorização de arquivo estático

Os modelos de ASP.NET Core chamam MapStaticAssets antes de chamar UseAuthorization. A maioria dos aplicativos segue esse padrão. Quando MapStaticAssets é chamado antes do middleware de autorização:

  • Nenhuma verificação de autorização é executada nos arquivos estáticos.
  • Os arquivos estáticos fornecidos pelo Middleware de arquivo estático, como aqueles em wwwroot, são acessíveis publicamente.

Para fornecer arquivos estáticos com base na autorização, consulte a autorização de arquivo estático.

Servir arquivos de vários locais

Considere a seguinte página Razor que exibe o arquivo /MyStaticFiles/image3.png:

@page

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

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

UseStaticFiles e UseFileServer padrão para o provedor de arquivos apontando para wwwroot. Instâncias adicionais de UseStaticFiles e UseFileServer podem ser fornecidas com outros provedores de arquivos para fornecer arquivos de outros locais. O exemplo a seguir chama UseStaticFiles duas vezes para fornecer arquivos de wwwroot e MyStaticFiles:

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

Usando o código anterior:

O código a seguir atualiza o WebRootFileProvider, o que permite que o Image Tag Helper forneça uma versão:

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

Observação

A abordagem anterior se aplica a Razor Pages e aplicativos MVC. Para obter diretrizes que se aplicam aos Blazor Web Apps, consulte Arquivos estáticos do ASP.NET Core Blazor.

Servir arquivos fora de wwwroot ao atualizar IWebHostEnvironment.WebRootPath

Quando IWebHostEnvironment.WebRootPath é definido como uma pasta diferente de wwwroot:

  • No ambiente de desenvolvimento, os ativos estáticos encontrados tanto em wwwroot quanto nos atualizados IWebHostEnvironment.WebRootPath são fornecidos a partir de wwwroot.
  • Em qualquer ambiente que não seja de desenvolvimento, os recursos estáticos duplicados são servidos da pasta atualizada IWebHostEnvironment.WebRootPath.

Considere um aplicativo Web criado com o modelo da Web vazio:

  • Contendo um arquivo Index.html em wwwroot e wwwroot-custom.

  • Com o seguinte arquivo Program.cs atualizado que define 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();
    

No código anterior, solicitações para /:

  • No ambiente de desenvolvimento retorna wwwroot/Index.html
  • Em qualquer ambiente que não seja o retorno do desenvolvimento wwwroot-custom/Index.html

Para garantir que os ativos de wwwroot-custom sejam retornados, use uma das seguintes abordagens:

  • Exclua ativos nomeados duplicados em wwwroot.

  • Defina "ASPNETCORE_ENVIRONMENT" em Properties/launchSettings.json para qualquer valor diferente de "Development".

  • Desabilite completamente os ativos da Web estáticos definindo <StaticWebAssetsEnabled>false</StaticWebAssetsEnabled> no arquivo de projeto. AVISO, desabilitar ativos da Web estáticos desabilita Razor bibliotecas de classes.

  • Adicione o seguinte XML ao arquivo de projeto:

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

O código a seguir atualiza IWebHostEnvironment.WebRootPath para um valor que não é de desenvolvimento, garantindo que o conteúdo duplicado seja retornado de wwwroot-custom ao invés de 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();

Recursos adicionais