Novidades do ASP.NET Core 6.0
Este artigo destaca as alterações mais significativas no ASP.NET Core 6.0, com links para a documentação relevante.
Melhorias do MVC e do Razor do ASP.NET Core
APIs mínimas
APIs mínimas são arquitetadas para criar APIs HTTP com dependências mínimas. Elas são ideais para microsserviços e aplicativos que desejam incluir apenas os arquivos, recursos e dependências mínimos no ASP.NET Core. Para obter mais informações, consulte:
- Tutorial: Criar uma API mínima com o ASP.NET Core
- Diferenças entre APIs mínimas e APIs com controladores
- Referência rápida de APIs mínimas
- Exemplos de código migrados para o novo modelo de hospedagem mínima no 6.0
SignalR
Tag de atividade de execução prolongada para conexões SignalR
SignalR usa o novo Microsoft.AspNetCore.Http.Features.IHttpActivityFeature.Activity para adicionar uma marcação http.long_running
à atividade de solicitação. IHttpActivityFeature.Activity
é usado por serviços do APM, como o Azure Monitor Application Insights, para filtrar solicitações SignalR e evitar a criação de alertas de solicitação de execução prolongada.
Aprimoramentos no desempenho de SignalR
- Aloque HubCallerClients uma vez por conexão em vez de a cada chamada de método de hub.
- Evite a alocação de fechamento em SignalR
DefaultHubDispatcher.Invoke
. O estado é transmitido a uma função estática local por meio de parâmetros para evitar uma alocação de fechamento. Saiba mais nesta solicitação de pull do GitHub. - Aloque um único StreamItemMessage por fluxo em vez de por item de fluxo no streaming de servidor para cliente. Saiba mais nesta solicitação de pull do GitHub.
Compilador do Razor
Compilador Razor atualizado para usar geradores de origem
O compilador Razor agora é baseado em geradores de origem em C#. Os geradores de origem são executados durante a compilação e inspecionam o que está sendo compilado para produzir arquivos adicionais compilados junto com o rest do projeto. O uso de geradores de origem simplifica o compilador Razor e acelera significativamente os tempos de compilação.
O compilador Razor não produz mais um assembly Views separado
O compilador Razor utilizou anteriormente um processo de compilação de duas etapas que produzia um assembly de Exibições separado que continha as exibições e páginas geradas (arquivos .cshtml
) definidos no aplicativo. Os tipos gerados eram públicos e estavam sob o namespace AspNetCore
.
O compilador Razor atualizado cria os tipos de exibições e páginas no assembly principal do projeto. Esses tipos agora são gerados por padrão como internos selados no namespace AspNetCoreGeneratedDocument
. Essa alteração melhora o desempenho do build, habilita a implantação de arquivo único e permite que esses tipos participem da Recarga Dinâmica.
Para obter mais informações sobre essa alteração, confira o anúncio relacionado ao problema no GitHub.
Aprimoramentos de desempenho e API do ASP.NET Core
Muitas alterações foram feitas para reduzir as alocações e melhorar o desempenho em toda a pilha:
- O método de extensão app.Use sem alocação. A nova sobrecarga de
app.Use
exige a transmissão do contexto anext
, o que salva duas alocações internas por solicitação que são exigidas no uso de outra sobrecarga. - Alocações de memória reduzidas ao acessar HttpRequest.Cookies. Saiba mais neste tópico do GitHub.
- Use LoggerMessage.Define para o servidor Web HTTP.sys somente Windows. As chamadas de métodos de extensão ILogger foram substituídas por chamadas para
LoggerMessage.Define
. - Reduza a sobrecarga por conexão em SocketConnection em cerca de 30%. Saiba mais nesta solicitação de pull do GitHub.
- Reduza as alocações removendo delegados de registro em log em tipos genéricos. Saiba mais nesta solicitação de pull do GitHub.
- Acesso GET mais rápido (cerca de 50%) a recursos comumente usados, como IHttpRequestFeature, IHttpResponseFeature, IHttpResponseBodyFeature, IRouteValuesFeature e IEndpointFeature. Saiba mais nesta solicitação de pull do GitHub.
- Use cadeias de caracteres de instância única para nomes de cabeçalho conhecidos, mesmo que não estejam no bloco de cabeçalho preservado. O uso de cadeia de caracteres de instância única ajuda a impedir várias duplicatas da mesma cadeia de caracteres em conexões de longa duração, por exemplo, em Microsoft.AspNetCore.WebSockets. Saiba mais neste tópico do GitHub.
- Reutilize HttpProtocol CancellationTokenSource em Kestrel. Use o novo método CancellationTokenSource.TryReset em
CancellationTokenSource
para reutilizar tokens se eles não tiverem sido cancelados. Para obter mais informações, confira este problema do GitHub e este vídeo. - Implemente e use um AdaptiveCapacityDictionary em Microsoft.AspNetCore.HttpRequestCookieCollection para obter acesso mais eficiente aos dicionários. Saiba mais nesta solicitação de pull do GitHub.
Volume de memória reduzido para conexões TLS ociosas
Para conexões TLS de execução prolongada em que os dados vão e voltam apenas ocasionalmente, reduzimos significativamente o volume de memória de aplicativos ASP.NET Core no .NET 6. Isso deve ajudar a melhorar a escalabilidade de cenários como servidores WebSocket. Isso foi possível devido a inúmeras melhorias em System.IO.Pipelines, SslStream e Kestrel. As seções abaixo detalham alguns dos aprimoramentos que contribuíram para a redução do volume de memória:
Reduzir o tamanho de System.IO.Pipelines.Pipe
Para cada conexão estabelecida, dois pipes são alocados no Kestrel:
- A camada de transporte até o aplicativo para a solicitação.
- A camada de aplicativo até o transporte para a resposta.
Com a redução do tamanho de System.IO.Pipelines.Pipe 368 bytes para 264 bytes (cerca de 28,2% de redução), 208 bytes por conexão são economizados (104 bytes por Pipe).
Pool SocketSender
Objetos SocketSender
(essa subclasse SocketAsyncEventArgs) são cerca de 350 bytes em runtime. Em vez de alocar um novo objeto SocketSender
por conexão, eles podem ser agrupados. Os objetos SocketSender
podem ser agrupados porque os envios geralmente são muito rápidos. O pooling reduz a sobrecarga por conexão. Em vez de alocar 350 bytes por conexão, apenas 350 bytes por IOQueue
são alocados. A alocação é feita por fila para evitar disputa. Nosso servidor WebSocket com 5.000 conexões ociosas passou da alocação de ~1,75 MB (350 bytes * 5000) à alocação de ~2,8 kb (350 bytes * 8) para objetos SocketSender
.
Leituras de zero byte com SslStream
As leituras sem buffer são uma técnica empregada no ASP.NET Core para evitar o aluguel de memória do pool de memória quando não há dados disponíveis no soquete. Antes dessa alteração, nosso servidor WebSocket com 5.000 conexões ociosas exigia cerca de 200 MB sem TLS em comparação com cerca de 800 MB com TLS. Algumas dessas alocações (4k por conexão) eram do Kestrel ter que manter um buffer de ArrayPool<T> enquanto aguardava a conclusão das leituras no SslStream. Considerando que essas conexões estavam ociosas, nenhuma das leituras foi concluída nem retornou seus buffers para o ArrayPool
, forçando o ArrayPool
a alocar mais memória. As alocações restantes estavam no próprio SslStream
: buffer de 4k para handshakes TLS e buffer de 32k para leituras normais. No .NET 6, quando o usuário executa uma leitura zero byte em SslStream
e não tem dados disponíveis, o SslStream
executa internamente uma leitura de zero byte no fluxo encapsulado subjacente. Na melhor das hipóteses (conexão ociosa), essas alterações resultam em uma economia de 40 Kb por conexão, permitindo que o consumidor (Kestrel) seja notificado quando os dados estiverem disponíveis sem precisar manter buffers não utilizados.
Leituras de zero byte com PipeReader
Com leituras sem buffer suportadas pelo SslStream
, uma opção foi adicionada a fim de fazer leituras de zero byte para StreamPipeReader
, o tipo interno que adapta um Stream
a um PipeReader
. Em Kestrel, umStreamPipeReader
é usado para adaptar o SslStream
subjacente a um PipeReader
. Portanto, era necessário expor essas semânticas de leitura de zero byte no PipeReader
.
Um PipeReader
agora pode ser criado para dar suporte a leituras de zero byte em qualquer Stream
subjacente que dê suporte à semântica de leitura de zero byte (por exemplo, SslStream
, NetworkStream, etc.) usando a API abaixo:
var reader = PipeReader.Create(stream, new StreamPipeReaderOptions(useZeroByteReads: true));
Remover sessões do SlabMemoryPool
Para reduzir a fragmentação do heap, o Kestrel empregava uma técnica em que alocava sessões de memória de 128 KB como parte de seu pool de memória. As sessões foram, então, divididas ainda mais em blocos de 4 KB que foram usados pelo Kestrel internamente. As sessões tinham que ser maiores que 85 KB para forçar a alocação no heap de objetos grandes a fim de tentar impedir que a GC realocasse essa matriz. No entanto, com a apresentação da nova geração de GC, o POH (Heap de Objetos Fixados), não faz mais sentido alocar blocos na sessão. O Kestrel agora aloca diretamente blocos no POH, reduzindo a complexidade envolvida no gerenciamento do pool de memória. Essa alteração deve facilitar a realização de melhorias futuras, por exemplo, facilitar a redução do pool de memória usado pelo Kestrel.
Suporte a IAsyncDisposable
IAsyncDisposable agora está disponível para controladores, Razor Pages e componentes de exibição. Versões assíncronas foram adicionadas às interfaces pertinentes em alocadores e ativadores:
- Os novos métodos oferecem uma implementação de interface padrão que delega à versão síncrona e chama Dispose.
- As implementações substituem a implementação padrão e lidam com o descarte de implementações
IAsyncDisposable
. - As implementações favorecem
IAsyncDisposable
acima deIDisposable
quando ambas as interfaces são implementadas. - Os extensores precisam substituir os novos métodos incluídos para dar suporte a instâncias
IAsyncDisposable
.
IAsyncDisposable
é benéfico no trabalho com:
- Enumeradores assíncronos, por exemplo, em fluxos assíncronos.
- Recursos não gerenciados que têm operações de E/S com uso intensivo de recursos a serem liberados.
Ao implementar essa interface, use o método DisposeAsync
para liberar recursos.
Considere um controlador que cria e usa um Utf8JsonWriter. Utf8JsonWriter
é um recurso IAsyncDisposable
:
public class HomeController : Controller, IAsyncDisposable
{
private Utf8JsonWriter? _jsonWriter;
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
_jsonWriter = new Utf8JsonWriter(new MemoryStream());
}
O IAsyncDisposable
precisa implementar DisposeAsync
:
public async ValueTask DisposeAsync()
{
if (_jsonWriter is not null)
{
await _jsonWriter.DisposeAsync();
}
_jsonWriter = null;
}
Porta vcpkg para cliente C++ SignalR
Vcpkg é um gerenciador de pacotes de linha de comando multiplataforma para bibliotecas C e C++. Recentemente, adicionamos uma porta a vcpkg
para adicionar suporte nativo de CMake
ao cliente C++ SignalR. vcpkg
também funciona com o MSBuild.
O cliente SignalR pode ser adicionado a um projeto do CMake com o seguinte snippet quando o vcpkg está incluído no arquivo de cadeia de ferramentas:
find_package(microsoft-signalr CONFIG REQUIRED)
link_libraries(microsoft-signalr::microsoft-signalr)
Com o snippet anterior, o cliente C++ SignalR está pronto para usar #include
e ser usado em um projeto sem nenhuma configuração adicional. Para obter um exemplo completo de um aplicativo C++ que utiliza o cliente C++ SignalR, confira o repositório halter73/SignalR-Client-Cpp-Sample.
Blazor
Alterações no modelo de projeto
Várias alterações de modelo de projeto foram feitas para aplicativos Blazor, incluindo o uso do arquivo Pages/_Layout.cshtml
para conteúdo de layout que aparecia no arquivo _Host.cshtml
para aplicativos Blazor Server anteriores. Estude as alterações com a criação de um aplicativo com base em um modelo de projeto 6.0 ou pelo acesso à fonte de referência do ASP.NET Core para os modelos de projeto:
Suporte Blazor WebAssembly a dependências nativas
Os aplicativos Blazor WebAssembly podem usar dependências nativas criadas para serem executadas no WebAssembly. Para saber mais, confira Dependências nativas do Blazor WebAssembly no ASP.NET Core.
Compilação de WebAssembly AOT (Ahead-of-time) e revinculação de runtime
O Blazor WebAssembly dá suporte à compilação AOT (Ahead Of Time), em que você pode compilar seu código do .NET diretamente no WebAssembly. A compilação AOT resulta em melhorias no desempenho de runtime em detrimento de um aplicativo maior. A nova vinculação de runtime do WebAssembly do .NET corta o código de runtime não utilizado e, portanto, melhora a velocidade de download. Para obter mais informações, confira Compilação AOT (Ahead of Time) e Revinculação de runtime.
Manter o estado pré-gerado
O Blazor dá suporte ao estado persistente em uma página pré-gerada para que o estado não precise ser recriado quando o aplicativo for totalmente carregado. Para obter mais informações, consulte Integrar ASP.NET componentes principais Razor ao MVC ou Razor ao Pages.
Limites de erro
Os limites de erro fornecem uma abordagem conveniente para lidar com exceções no nível da interface do usuário. Para obter mais informações, confira Manipular erros nos aplicativos Blazor do ASP.NET Core.
Suporte a SVG
O <foreignObject>
elemento tem suporte à exibição de HTML arbitrário em um SVG. Para saber mais, confira Componentes Razor do ASP.NET Core.
Suporte Blazor Server à transferência de matriz de bytes na Interoperabilidade JS
O Blazor dá suporte à interoperabilidade de JS da matriz de bytes otimizada que evita codificar e decodificar matrizes de bytes em Base64. Para saber mais, consulte os recursos a seguir:
- Chamar funções JavaScript de métodos .NET no ASP.NET Core Blazor
- Chamar métodos .NET de funções JavaScript no ASP.NET Core Blazor
Aprimoramentos de cadeia de caracteres de consulta
O suporte ao trabalho com cadeias de caracteres de consulta foi aprimorado. Para obter mais informações, confira Roteamento e navegação do Blazor no ASP.NET Core.
Associação para selecionar vários
A associação dá suporte à seleção de várias opções com elementos <input>
. Para saber mais, consulte os recursos a seguir:
Controle de conteúdo de cabeçalho (<head>
)
Os componentes Razor podem modificar o conteúdo do elemento HTML <head>
de uma página, incluindo a definição do título da página (elemento <title>
) e a modificação de metadados (elementos <meta>
). Para obter mais informações, confira Controlar conteúdo <head>
em aplicativos Blazor do ASP.NET Core.
Gerar componentes Angular e React
Gere componentes JavaScript específicos da estrutura com base em componentes Razor para estruturas da Web, como Angular ou React. Para saber mais, confira Componentes Razor do ASP.NET Core.
Renderizar componentes do JavaScript
Renderize componentes Razor dinamicamente do JavaScript para aplicativos JavaScript existentes. Para saber mais, confira Componentes Razor do ASP.NET Core.
Elementos personalizados
O suporte experimental está disponível para a criação de elementos personalizados, que usam interfaces HTML padrão. Para saber mais, confira Componentes Razor do ASP.NET Core.
Inferir tipos genéricos de componentes ancestrais
Um componente ancestral pode colocar em cascata, por nome, para os descendentes um parâmetro de tipo usando o atributo [CascadingTypeParameter]
. Para saber mais, confira Componentes Razor do ASP.NET Core.
Componentes renderizados dinamicamente
Use o novo componente DynamicComponent
interno para renderizar componentes por tipo. Para obter mais informações, confira Componentes Razor do ASP.NET Core renderizados dinamicamente.
Acessibilidade do Blazor aprimorada
Use o novo componente FocusOnNavigate
a fim de definir o foco da interface do usuário em um elemento com base em um seletor CSS depois de navegar de uma página para outra. Para obter mais informações, confira Roteamento e navegação do Blazor no ASP.NET Core.
Suporte a argumentos de evento personalizados
O Blazor dá suporte a argumentos de evento personalizados, que permitem transmitir dados arbitrários a manipuladores de eventos do .NET com eventos personalizados. Para obter mais informações, confira Tratamento de eventos do Blazor no ASP.NET Core.
Parâmetros obrigatórios
Aplique o atributo [EditorRequired]
para especificar um parâmetro de componente necessário. Para saber mais, confira Componentes Razor do ASP.NET Core.
Ordenação de arquivos JavaScript com páginas, exibições e componentes
A colocalização de arquivos JavaScript para páginas, exibições e componentes Razor é uma maneira conveniente de organizar scripts em um aplicativo. Para obter mais informações, confira Interoperabilidade ASP.NET Core Blazor JavaScript (interoperabilidade JS).
Inicializadores de JavaScript
Os inicializadores de JavaScript executam a lógica antes e depois do carregamento de um aplicativo Blazor. Para obter mais informações, confira Interoperabilidade ASP.NET Core Blazor JavaScript (interoperabilidade JS).
Interoperabilidade do JavaScript em streaming
O Blazor agora dá suporte a dados de streaming diretamente entre o .NET e o JavaScript. Para saber mais, consulte os recursos a seguir:
Restrições de tipo genérico
Agora há suporte a parâmetros de tipo genérico. Para saber mais, confira Componentes Razor do ASP.NET Core.
Layout de implantação do WebAssembly
Use um layout de implantação para habilitar downloads de aplicativos Blazor WebAssembly em ambientes de segurança restritos. Para obter mais informações, consulte Layout de implantação para aplicativos hospedados no ASP.NET Core Blazor WebAssembly.
Novos artigos do Blazor
Além dos recursos do Blazor descritos nas seções anteriores, novos artigos do Blazor estão disponíveis nos seguintes assuntos:
- Downloads de arquivos Blazor do ASP.NET Core : saiba como baixar um arquivo usando a interoperabilidade de streaming de
byte[]
nativa para garantir uma transferência eficiente para o cliente. - Exibir imagens e documentos no ASP.NET Core Blazor: descubra como trabalhar com imagens e documentos em aplicativos Blazor, incluindo como transmitir dados de imagens e documentos.
Criar aplicativos Blazor Hybrid com .NET MAUI, WPF e Windows Forms
Use Blazor Hybrid para combinar estruturas de cliente nativas de desktop e móveis com .NET e Blazor:
- O .NET Multi-platform App UI (.NET MAUI) é uma estrutura multiplataforma para criar aplicativos móveis e de área de trabalho nativos com C# e XAML.
- Aplicativos Blazor Hybrid podem ser criados com a WPF (Windows Presentation Foundation) e o Windows Forms.
Importante
O Blazor Hybrid está em versão prévia e não deve ser usado em aplicativos de produção até a versão final.
Para saber mais, consulte os recursos a seguir:
- Confira a Documentação do Blazor Hybrid no ASP.NET Core
- O que é o .NET MAUI?
- Blog do Microsoft .NET (categoria: ".NET MAUI")
Kestrel
O HTTP/3 está atualmente em rascunho, ou seja, sujeito a alterações. O suporte a HTTP/3 no ASP.NET Core não foi lançado; ele é uma versão prévia do recurso incluída no .NET 6.
O Kestrel agora dá suporte a HTTP/3. Para obter mais informações, confira Usar HTTP/3 com o servidor Web doKestrel do ASP.NET Core e a entrada de blog Suporte a HTTP/3 no .NET 6.
Novas categorias de registro em log do Kestrel para registro em log selecionado
Antes dessa alteração, a habilitação do registro em log detalhado para Kestrel era absurdamente caro, já que todo o Kestrel compartilhava o nome da categoria de registro em log doMicrosoft.AspNetCore.Server.Kestrel
. O Microsoft.AspNetCore.Server.Kestrel
ainda está disponível, mas as novas subcategorias abaixo permitem mais controle do registro em log:
Microsoft.AspNetCore.Server.Kestrel
(categoria atual):ApplicationError
,ConnectionHeadResponseBodyWrite
,ApplicationNeverCompleted
,RequestBodyStart
,RequestBodyDone
,RequestBodyNotEntirelyRead
,RequestBodyDrainTimedOut
,ResponseMinimumDataRateNotSatisfied
,InvalidResponseHeaderRemoved
,HeartbeatSlow
.Microsoft.AspNetCore.Server.Kestrel.BadRequests
:ConnectionBadRequest
,RequestProcessingError
,RequestBodyMinimumDataRateNotSatisfied
.Microsoft.AspNetCore.Server.Kestrel.Connections
:ConnectionAccepted
,ConnectionStart
,ConnectionStop
,ConnectionPause
,ConnectionResume
.ConnectionKeepAlive
ApplicationAbortedConnection
ConnectionRejected
ConnectionDisconnect
NotAllConnectionsClosedGracefully
NotAllConnectionsAborted
Microsoft.AspNetCore.Server.Kestrel.Http2
:Http2ConnectionError
,Http2ConnectionClosing
,Http2ConnectionClosed
,Http2StreamError
,Http2StreamResetAbort
Http2FrameSending
HPackDecodingError
Http2FrameReceived
HPackEncodingError
Http2MaxConcurrentStreamsReached
.Microsoft.AspNetCore.Server.Kestrel.Http3
:Http3ConnectionError
,Http3ConnectionClosing
,Http3ConnectionClosed
,Http3StreamAbort
,Http3FrameReceived
,Http3FrameSending
.
As regras existentes continuam funcionando, mas agora você pode ser mais seletivo em relação a quais regras habilita. Por exemplo, a sobrecarga de observabilidade de habilitar o registro em log de Debug
apenas para solicitações inválidas é muito reduzida e pode ser habilitada com a seguinte configuração:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.AspNetCore.Kestrel.BadRequests": "Debug"
}
}
A filtragem de log aplica regras com o prefixo de categoria correspondente mais longo. Para obter mais informações, confira Como as regras de filtragem são aplicadas
Emitir KestrelServerOptions por meio do evento EventSource
O KestrelEventSource emite um novo evento que contém o JSON serializado KestrelServerOptions quando habilitado com EventLevel.LogAlways
de detalhamento. Esse evento facilita o raciocínio sobre o comportamento do servidor ao analisar rastreamentos coletados. O JSON a seguir é um exemplo do conteúdo do evento:
{
"AllowSynchronousIO": false,
"AddServerHeader": true,
"AllowAlternateSchemes": false,
"AllowResponseHeaderCompression": true,
"EnableAltSvc": false,
"IsDevCertLoaded": true,
"RequestHeaderEncodingSelector": "default",
"ResponseHeaderEncodingSelector": "default",
"Limits": {
"KeepAliveTimeout": "00:02:10",
"MaxConcurrentConnections": null,
"MaxConcurrentUpgradedConnections": null,
"MaxRequestBodySize": 30000000,
"MaxRequestBufferSize": 1048576,
"MaxRequestHeaderCount": 100,
"MaxRequestHeadersTotalSize": 32768,
"MaxRequestLineSize": 8192,
"MaxResponseBufferSize": 65536,
"MinRequestBodyDataRate": "Bytes per second: 240, Grace Period: 00:00:05",
"MinResponseDataRate": "Bytes per second: 240, Grace Period: 00:00:05",
"RequestHeadersTimeout": "00:00:30",
"Http2": {
"MaxStreamsPerConnection": 100,
"HeaderTableSize": 4096,
"MaxFrameSize": 16384,
"MaxRequestHeaderFieldSize": 16384,
"InitialConnectionWindowSize": 131072,
"InitialStreamWindowSize": 98304,
"KeepAlivePingDelay": "10675199.02:48:05.4775807",
"KeepAlivePingTimeout": "00:00:20"
},
"Http3": {
"HeaderTableSize": 0,
"MaxRequestHeaderFieldSize": 16384
}
},
"ListenOptions": [
{
"Address": "https://127.0.0.1:7030",
"IsTls": true,
"Protocols": "Http1AndHttp2"
},
{
"Address": "https://[::1]:7030",
"IsTls": true,
"Protocols": "Http1AndHttp2"
},
{
"Address": "http://127.0.0.1:5030",
"IsTls": false,
"Protocols": "Http1AndHttp2"
},
{
"Address": "http://[::1]:5030",
"IsTls": false,
"Protocols": "Http1AndHttp2"
}
]
}
Novo evento DiagnosticSource para solicitações HTTP rejeitadas
O Kestrel agora emite um novo evento DiagnosticSource
para solicitações HTTP rejeitadas na camada do servidor. Antes dessa alteração, não havia como observar essas solicitações rejeitadas. O novo evento DiagnosticSource
Microsoft.AspNetCore.Server.Kestrel.BadRequest
contém um IBadRequestExceptionFeature que pode ser usado para analisar o motivo de rejeição da solicitação.
using Microsoft.AspNetCore.Http.Features;
using System.Diagnostics;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var diagnosticSource = app.Services.GetRequiredService<DiagnosticListener>();
using var badRequestListener = new BadRequestEventListener(diagnosticSource,
(badRequestExceptionFeature) =>
{
app.Logger.LogError(badRequestExceptionFeature.Error, "Bad request received");
});
app.MapGet("/", () => "Hello world");
app.Run();
class BadRequestEventListener : IObserver<KeyValuePair<string, object>>, IDisposable
{
private readonly IDisposable _subscription;
private readonly Action<IBadRequestExceptionFeature> _callback;
public BadRequestEventListener(DiagnosticListener diagnosticListener,
Action<IBadRequestExceptionFeature> callback)
{
_subscription = diagnosticListener.Subscribe(this!, IsEnabled);
_callback = callback;
}
private static readonly Predicate<string> IsEnabled = (provider) => provider switch
{
"Microsoft.AspNetCore.Server.Kestrel.BadRequest" => true,
_ => false
};
public void OnNext(KeyValuePair<string, object> pair)
{
if (pair.Value is IFeatureCollection featureCollection)
{
var badRequestFeature = featureCollection.Get<IBadRequestExceptionFeature>();
if (badRequestFeature is not null)
{
_callback(badRequestFeature);
}
}
}
public void OnError(Exception error) { }
public void OnCompleted() { }
public virtual void Dispose() => _subscription.Dispose();
}
Para obter mais informações, confira Registro em log e diagnóstico no Kestrel.
Criar um ConnectionContext com base em um soquete Accept
O novo SocketConnectionContextFactory possibilita criar um ConnectionContext com base em um soquete aceito. Isso possibilita a criação de uma IConnectionListenerFactory personalizada baseada em soquete sem perder todo o trabalho de desempenho e o pool que ocorre em SocketConnection.
Veja este exemplo de uma IConnectionListenerFactory personalizada que mostra como usar esse SocketConnectionContextFactory
.
O Kestrel é o perfil de inicialização padrão do Visual Studio
O perfil de inicialização padrão para todos os novos projetos Web dotnet é o Kestrel. A inicialização do Kestrel é significativamente mais rápida e resulta em uma experiência mais responsiva ao desenvolver aplicativos.
O IIS Express ainda está disponível como um perfil de inicialização para cenários como Autenticação do Windows ou compartilhamento de porta.
As portas localhost para o Kestrel são aleatórias
Confira Portas geradas por modelo para o Kestrel neste documento para obter mais informações.
Autenticação e autorização
Servidores de autenticação
O .NET 3 para o .NET 5 usou IdentityServer4 como parte do nosso modelo para dar suporte à emissão de tokens JWT para aplicativos SPA e Blazor. Os modelos agora usam o Duende Identity Server.
Se você estiver estendendo os identity modelos e atualizando projetos existentes, atualize os namespaces em seu código de para Duende.IdentityServer
e siga as instruções de IdentityServer4.IdentityServer
migração.
O modelo de licença do Duende Identity Server foi alterado para uma licença recíproca, o que pode exigir valores de licença quando ele é usado comercialmente na produção. Confira a página de licença do Duende para obter mais detalhes.
Negociação de certificado de cliente atrasado
Os desenvolvedores agora podem optar por usar a negociação de certificado de cliente atrasado especificando ClientCertificateMode.DelayCertificate em HttpsConnectionAdapterOptions. Isso só funciona com conexões HTTP/1.1 porque HTTP/2 proíbe a renegociação de certificado atrasado. O chamador dessa API precisa armazenar em buffer o corpo da solicitação antes de solicitar o certificado do cliente:
using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.AspNetCore.WebUtilities;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseKestrel(options =>
{
options.ConfigureHttpsDefaults(adapterOptions =>
{
adapterOptions.ClientCertificateMode = ClientCertificateMode.DelayCertificate;
});
});
var app = builder.Build();
app.Use(async (context, next) =>
{
bool desiredState = GetDesiredState();
// Check if your desired criteria is met
if (desiredState)
{
// Buffer the request body
context.Request.EnableBuffering();
var body = context.Request.Body;
await body.DrainAsync(context.RequestAborted);
body.Position = 0;
// Request client certificate
var cert = await context.Connection.GetClientCertificateAsync();
// Disable buffering on future requests if the client doesn't provide a cert
}
await next(context);
});
app.MapGet("/", () => "Hello World!");
app.Run();
Evento OnCheckSlidingExpiration
para controlar a renovação de cookie
A expiração deslizante de Cookie agora pode ser personalizada ou suprimida com o uso do novo OnCheckSlidingExpiration. Por exemplo, esse evento pode ser usado por um aplicativo de página única que precisa executar ping periodicamente no servidor sem afetar a sessão de autenticação.
Diversos
Hot Reload
Faça rapidamente atualizações de interface do usuário e de código para aplicativos em execução sem perder o estado do aplicativo a fim de ter uma experiência de desenvolvedor mais rápida e produtiva com a Recarga Dinâmica. Para obter mais informações, confira Suporte à Recarga Dinâmica do .NET para ASP.NET Core e Atualização sobre o progresso da Recarga Dinâmica do .NET e Destaques do Visual Studio 2022.
Modelos de SPA (aplicativo de página única) aprimorados
Os modelos de projeto do ASP.NET Core foram atualizados para Angular e React a fim de usar um padrão aprimorado e ter aplicativos de página única mais flexíveis e mais alinhados com padrões comuns para o desenvolvimento moderno de front-end da Web.
Anteriormente, o modelo do ASP.NET Core para Angular e React usava middleware especializado durante o desenvolvimento a fim de iniciar o servidor de desenvolvimento para a estrutura de front-end e, em seguida, fazer proxy de solicitações do ASP.NET Core para o servidor de desenvolvimento. A lógica para iniciar o servidor de desenvolvimento de front-end era específica da interface de linha de comando para a estrutura de front-end correspondente. O suporte a estruturas front-end adicionais usando esse padrão significava adicionar mais lógica ao ASP.NET Core.
Os modelos do ASP.NET Core atualizados para Angular e React no .NET 6 invertem essa disposição e aproveitam o suporte interno de proxying nos servidores de desenvolvimento da maioria das estruturas de front-end modernas. Quando o aplicativo ASP.NET Core é iniciado, o servidor de desenvolvimento de front-end é iniciado exatamente como antes, mas o servidor de desenvolvimento é configurado para fazer proxy de solicitações para o processo de back-end do ASP.NET Core. Toda a configuração específica de front-end para configurar o proxy faz parte do aplicativo, não do ASP.NET Core. A configuração de projetos do ASP.NET Core para trabalhar com outras estruturas de front-end agora é direta: configurar o servidor de desenvolvimento de front-end para a estrutura escolhida a fim de fazer proxy para o back-end do ASP.NET Core usando o padrão estabelecido nos modelos Angular e React.
O código de inicialização do aplicativo ASP.NET Core não precisa mais de nenhuma lógica específica do aplicativo de página única. A lógica para iniciar o servidor de desenvolvimento de front-end durante o desenvolvimento é injetar no aplicativo no runtime com o novo pacote Microsoft.AspNetCore.SpaProxy. O roteamento de fallback é tratado usando o roteamento de ponto de extremidade em vez do middleware específico do SPA.
Os modelos que seguem esse padrão ainda podem ser executados como um único projeto no Visual Studio ou usando a linha de comando dotnet run
. Quando o aplicativo é publicado, o código front-end na pasta ClientApp é compilado e coletado como antes na raiz da Web do aplicativo host do ASP.NET Core e servido como arquivos estáticos. Os scripts incluídos no modelo configuram o servidor de desenvolvimento de front-end para usar HTTPS usando o certificado de desenvolvimento do ASP.NET Core.
Rascunho do suporte a HTTP/3 no .NET 6
O HTTP/3 está atualmente em rascunho, ou seja, sujeito a alterações. O suporte a HTTP/3 no ASP.NET Core não foi lançado; ele é uma versão prévia do recurso incluída no .NET 6.
Confira a entrada do blog Suporte a HTTP/3 no .NET 6.
Anotações de tipo de referência anuláveis
Partes do código-fonte do ASP.NET Core 6.0 tiveram anotações de nulidade aplicadas.
Utilizando o novo recurso Anulável no C# 8, o ASP.NET Core pode fornecer segurança no tempo de compilação adicional no tratamento de tipos de referência. Por exemplo, a proteção contra exceções de referência null
. Projetos que optaram por usar anotações anuláveis podem ver novos avisos de tempo de build de APIs do ASP.NET Core.
Para habilitar tipos de referência anuláveis, adicione a seguinte propriedade aos arquivos de projeto:
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
Para obter mais informações, confira Tipos de referência anuláveis.
Análise de código-fonte
Vários analisadores de plataforma do compilador .NET foram adicionados e inspecionam o código do aplicativo em busca de problemas como configuração ou ordem de middleware incorretas, conflitos de roteamento, etc. Para obter mais informações, confira Análise de código em aplicativos do ASP.NET Core.
Aprimoramentos do modelo de aplicativo Web
Os modelos de aplicativo Web:
- Use o novo modelo de hospedagem mínima.
- Reduz significativamente o número de arquivos e linhas de código necessário para criar um aplicativo. Por exemplo, o aplicativo Web vazio do ASP.NET Core cria um arquivo C# com quatro linhas de código e é um aplicativo completo.
- Unifica
Startup.cs
eProgram.cs
em um único arquivoProgram.cs
. - Usa instruções de nível superior a fim de minimizar o código necessário para um aplicativo.
- Usa diretivas
using
globais para eliminar ou minimizar o número de linhas de instruçãousing
necessárias.
Portas geradas por modelo para Kestrel
Portas aleatórias são atribuídas durante a criação do projeto para uso pelo servidor Web do Kestrel. Portas aleatórias ajudam a minimizar um conflito de porta quando vários projetos são executados no mesmo computador.
Quando um projeto é criado, uma porta HTTP aleatória entre 5000-5300 e uma porta HTTPS aleatória entre 7000-7300 é especificada no arquivo Properties/launchSettings.json
gerado. As portas podem ser alteradas no arquivo Properties/launchSettings.json
. Se nenhuma porta for especificada, o Kestrel assume como padrão as portas HTTP 5000 e HTTPS 5001. Para obter mais informações, confira Configurar pontos de extremidade para o servidor Web Kestrel do ASP.NET Core.
Novos padrões de registro em log
As seguintes alterações foram feitas em appsettings.json
e appsettings.Development.json
:
- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
+ "Microsoft.AspNetCore": "Warning"
A alteração de "Microsoft": "Warning"
para "Microsoft.AspNetCore": "Warning"
resulta no registro em log de todas as mensagens informativas do namespace Microsoft
exceto Microsoft.AspNetCore
. Por exemplo, Microsoft.EntityFrameworkCore
agora está registrado no nível informativo.
Middleware da página de exceção do desenvolvedor adicionado automaticamente
No ambiente de desenvolvimento , o DeveloperExceptionPageMiddleware é adicionado por padrão. Não é mais necessário adicionar o seguinte código aos aplicativos de interface do usuário da Web:
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
Suporte a cabeçalhos de solicitação codificados em Latin1 no HttpSysServer
HttpSysServer
agora dá suporte à decodificação de cabeçalhos de solicitação Latin1
codificados com a definição da propriedade UseLatin1RequestHeaders em HttpSysOptions como true
:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseHttpSys(o => o.UseLatin1RequestHeaders = true);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
Os logs do Módulo ASP.NET Core incluem carimbos de data/hora e PID
Os logs de diagnóstico aprimorados do ANCM (Módulo ASP.NET Core) para IIS (ANCM) incluem carimbos de data/hora e PID do processo que emite os logs. O registro dos carimbos de data/hora e PID facilita o diagnóstico de problemas com reinicializações de processo sobrepostas no IIS quando vários processos de trabalho do IIS estão em execução.
Os logs resultantes agora se assemelham ao exemplo de saída mostrado abaixo:
[2021-07-28T19:23:44.076Z, PID: 11020] [aspnetcorev2.dll] Initializing logs for 'C:\<path>\aspnetcorev2.dll'. Process Id: 11020. File Version: 16.0.21209.0. Description: IIS ASP.NET Core Module V2. Commit: 96475a2acdf50d7599ba8e96583fa73efbe27912.
[2021-07-28T19:23:44.079Z, PID: 11020] [aspnetcorev2.dll] Resolving hostfxr parameters for application: '.\InProcessWebSite.exe' arguments: '' path: 'C:\Temp\e86ac4e9ced24bb6bacf1a9415e70753\'
[2021-07-28T19:23:44.080Z, PID: 11020] [aspnetcorev2.dll] Known dotnet.exe location: ''
Tamanho do buffer de entrada não consumido configurável para o IIS
Anteriormente, o servidor do IIS armazenava em buffer apenas 64 KiB de corpos de solicitação não armazenados. O buffer de 64 KiB resultava em leituras restritas a esse tamanho máximo, o que afeta o desempenho em grandes corpos de entrada, como uploads. No .NET 6, o tamanho do buffer padrão muda de 64 KiB para 1 MiB, o que deve melhorar a taxa de transferência em uploads grandes. Em nossos testes, um upload de 700 MiB que costumava levar 9 segundos agora leva apenas 2,5 segundos.
A desvantagem de um tamanho de buffer maior é um aumento no consumo de memória para cada solicitação quando o aplicativo não está lendo rapidamente do corpo da solicitação. Portanto, além de alterar o tamanho do buffer padrão, o tamanho do buffer é configurável, permitindo que os aplicativos configurem o tamanho do buffer com base na carga de trabalho.
Exibir auxiliares de marcação de componentes de exibição
Considere um componente de exibição com um parâmetro opcional, conforme mostrado no seguinte código:
class MyViewComponent
{
IViewComponentResult Invoke(bool showSomething = false) { ... }
}
Com o ASP.NET Core 6, o auxiliar de marcação pode ser invocado sem precisar especificar um valor para o parâmetro showSomething
:
<vc:my />
Modelo Angular atualizado para Angular 12
O modelo do ASP.NET Core 6.0 para Angular agora usa Angular 12.
O modelo do React foi atualizado para o React 17.
Limite de buffer configurável antes de gravar em disco no formatador de saída Json.NET
Observação: é recomendável usar o formatador de saída System.Text.Json, exceto quando o serializador Newtonsoft.Json
for necessário por motivos de compatibilidade. O serializador System.Text.Json
é totalmente async
e funciona com eficiência em cargas maiores.
O formatador de saída Newtonsoft.Json
armazena por padrão respostas de até 32 KiB na memória antes de fazer buffer em disco. Isso é para evitar a execução de E/S síncrona, o que pode resultar em outros efeitos colaterais, como a falta de threads e deadlocks de aplicativo. No entanto, se a resposta for maior que 32 KiB, ocorrerá uma E/S de disco considerável. O limite de memória agora pode ser configurado por meio da propriedade MvcNewtonsoftJsonOptions.OutputFormatterMemoryBufferThreshold antes de fazer buffer em disco:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages()
.AddNewtonsoftJson(options =>
{
options.OutputFormatterMemoryBufferThreshold = 48 * 1024;
});
var app = builder.Build();
Para obter mais informações, confira esta solicitação de pull do GitHub e o arquivo NewtonsoftJsonOutputFormatterTest.cs.
Get e set mais rápidos para cabeçalhos HTTP
Novas APIs foram adicionadas para expor todos os cabeçalhos comuns disponíveis em Microsoft.Net.Http.Headers.HeaderNames como propriedades no IHeaderDictionary, resultando em uma API mais fácil de usar. Por exemplo, o middleware em linha no código abaixo obtém e define cabeçalhos de solicitação e resposta usando as novas APIs:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Use(async (context, next) =>
{
var hostHeader = context.Request.Headers.Host;
app.Logger.LogInformation("Host header: {host}", hostHeader);
context.Response.Headers.XPoweredBy = "ASP.NET Core 6.0";
await next.Invoke(context);
var dateHeader = context.Response.Headers.Date;
app.Logger.LogInformation("Response date: {date}", dateHeader);
});
app.Run();
Para cabeçalhos implementados, os acessadores get e set são implementados acessando diretamente o campo e ignorando a pesquisa. Para cabeçalhos não implementados, os acessadores podem ignorar a pesquisa inicial em relação aos cabeçalhos implementados e executar diretamente a pesquisa Dictionary<string, StringValues>
. Evitar a pesquisa resulta em acesso mais rápido em ambos os cenários.
Streaming assíncrono
O ASP.NET Core agora dá suporte ao streaming assíncrono de ações e respostas do controlador do formatador JSON. O retorno de um IAsyncEnumerable
de uma ação não armazena mais em buffer o conteúdo da resposta na memória antes do envio. O não buffer ajuda a reduzir o uso de memória ao retornar grandes conjuntos de dados que podem ser enumerados de forma assíncrona.
Observe que o Entity Framework Core fornece implementações de IAsyncEnumerable
para consultar o banco de dados. O suporte aprimorado a IAsyncEnumerable
no ASP.NET Core no .NET 6 pode tornar o uso do EF Core com o ASP.NET Core mais eficiente. Por exemplo, o código abaixo não armazena mais os dados do produto na memória antes de enviar a resposta:
public IActionResult GetMovies()
{
return Ok(_context.Movie);
}
No entanto, ao usar o carregamento lento no EF Core, esse novo comportamento pode resultar em erros devido à execução simultânea da consulta enquanto os dados estão sendo enumerados. Os aplicativos podem reverter para o comportamento anterior armazenando os dados em buffer:
public async Task<IActionResult> GetMovies2()
{
return Ok(await _context.Movie.ToListAsync());
}
Confira o comunicado relacionado para obter detalhes adicionais sobre essa alteração no comportamento.
Middleware de registro em log HTTP
O registro em log HTTP é um novo middleware interno que registra informações sobre solicitações e respostas HTTP, incluindo os cabeçalhos e todo o corpo:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.UseHttpLogging();
app.MapGet("/", () => "Hello World!");
app.Run();
A navegação até /
com as informações de logs de código anteriores semelhantes à seguinte saída:
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]
Request:
Protocol: HTTP/2
Method: GET
Scheme: https
PathBase:
Path: /
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cache-Control: max-age=0
Connection: close
Cookie: [Redacted]
Host: localhost:44372
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36 Edg/95.0.1020.30
sec-ch-ua: [Redacted]
sec-ch-ua-mobile: [Redacted]
sec-ch-ua-platform: [Redacted]
upgrade-insecure-requests: [Redacted]
sec-fetch-site: [Redacted]
sec-fetch-mode: [Redacted]
sec-fetch-user: [Redacted]
sec-fetch-dest: [Redacted]
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]
Response:
StatusCode: 200
Content-Type: text/plain; charset=utf-8
A saída anterior foi habilitada com o seguinte arquivo appsettings.Development.json
:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"
}
}
}
O registro em log HTTP fornece logs de:
- Informações de solicitação HTTP
- Propriedades comuns
- Cabeçalhos
- Corpo
- Informações de resposta HTTP
Para configurar o middleware de registro em log HTTP, especifique HttpLoggingOptions:
using Microsoft.AspNetCore.HttpLogging;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHttpLogging(logging =>
{
// Customize HTTP logging.
logging.LoggingFields = HttpLoggingFields.All;
logging.RequestHeaders.Add("My-Request-Header");
logging.ResponseHeaders.Add("My-Response-Header");
logging.MediaTypeOptions.AddText("application/javascript");
logging.RequestBodyLogLimit = 4096;
logging.ResponseBodyLogLimit = 4096;
});
var app = builder.Build();
app.UseHttpLogging();
app.MapGet("/", () => "Hello World!");
app.Run();
IConnectionSocketFeature
O recurso de solicitação IConnectionSocketFeature fornece acesso ao soquete de aceitação subjacente associado à solicitação atual. Ele pode ser acessado por meio do FeatureCollection no HttpContext
.
Por exemplo, o aplicativo abaixo define a propriedade LingerState no soquete aceito:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ConfigureEndpointDefaults(listenOptions => listenOptions.Use((connection, next) =>
{
var socketFeature = connection.Features.Get<IConnectionSocketFeature>();
socketFeature.Socket.LingerState = new LingerOption(true, seconds: 10);
return next();
}));
});
var app = builder.Build();
app.MapGet("/", (Func<string>)(() => "Hello world"));
await app.RunAsync();
Restrições de tipo genérico no Razor
Ao definir parâmetros de tipo genérico no Razor usando a diretiva @typeparam
, as restrições de tipo genérico agora podem ser especificadas usando a sintaxe C# padrão:
Scripts de SignalR, Blazor Servere MessagePack menores
Os scripts de SignalR, MessagePack e Blazor Server agora são significativamente menores, permitindo downloads menores, menos análise e compilação de JavaScript pelo navegador e inicialização mais rápida. As reduções de tamanho:
signalr.js
: 70%blazor.server.js
: 45%
Os scripts menores são resultado de uma contribuição da comunidade do Ben Adams. Para obter mais informações sobre os detalhes da redução de tamanho, confira Solicitação de pull do GitHub do Ben.
Habilitar sessões de criação de perfil do Redis
Uma contribuição da comunidade do Gabriel Lucaci habilita a sessão de criação de perfil do Redis com Microsoft.Extensions.Caching.StackExchangeRedis:
using StackExchange.Redis.Profiling;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddStackExchangeRedisCache(options =>
{
options.ProfilingSession = () => new ProfilingSession();
});
Para obter mais informações, confira as Criação de perfil do StackExchange.Redis.
Cópia de sombra no IIS
Um recurso experimental foi adicionado ao ANCM (Módulo do ASP.NET Core) para IIS a fim de adicionar suporte a cópia de sombra dos assemblies de aplicativo. Atualmente, o .NET bloqueia binários de aplicativos ao ser executado no Windows, impossibilitando a substituição de binários quando o aplicativo está em execução. Embora nossa recomendação permaneça usar um arquivo offline do aplicativo, reconhecemos que há determinados cenários (por exemplo, implantações FTP) em que não é possível fazer isso.
Nesses cenários, habilite a cópia de sombra personalizando as configurações do manipulador do módulo do ASP.NET Core. Na maioria dos casos, os aplicativos do ASP.NET Core não têm um web.config
verificado no controle do código-fonte que você possa modificar. No ASP.NET Core, o web.config
normalmente é gerado pelo SDK. O exemplo a seguir do web.config
pode ser usado para começar:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- To customize the asp.net core module uncomment and edit the following section.
For more info see https://go.microsoft.com/fwlink/?linkid=838655 -->
<system.webServer>
<handlers>
<remove name="aspNetCore"/>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified"/>
</handlers>
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout">
<handlerSettings>
<handlerSetting name="experimentalEnableShadowCopy" value="true" />
<handlerSetting name="shadowCopyDirectory" value="../ShadowCopyDirectory/" />
<!-- Only enable handler logging if you encounter issues-->
<!--<handlerSetting name="debugFile" value=".\logs\aspnetcore-debug.log" />-->
<!--<handlerSetting name="debugLevel" value="FILE,TRACE" />-->
</handlerSettings>
</aspNetCore>
</system.webServer>
</configuration>
A cópia de sombra no IIS é um recurso experimental e não é garantido como parte do ASP.NET Core. Deixe comentários sobre a cópia de sombra do IIS neste problema do GitHub.