Desenvolver aplicativos ASP.NET Core usando um observador de arquivo

Por Rick Anderson e Victor Hurdugaci

dotnet watch é uma ferramenta que executa um comando do CLI do .NET Core quando os arquivos de origem são alterados. Por exemplo, uma alteração de arquivo pode disparar uma compilação, execução de teste ou uma implantação.

Este tutorial usa um aplicativo de API Web existente com dois pontos de extremidade: um que retorna uma soma e outro que retorna um produto. O método de produto tem um bug, que é corrigido neste tutorial.

Baixe o aplicativo de exemplo. Ele consiste em dois projetos: WebApp (uma API Web ASP.NET Core) e WebAppTests (testes de unidade para a API Web).

Em um shell de comando, navegue até a pasta WebApp. Execute o comando a seguir:

dotnet run

Observação

dotnet run --project <PROJECT> pode ser usado para especificar um projeto a ser executado. Por exemplo, a execução de dotnet run --project WebApp da raiz do aplicativo de exemplo também executará o projeto WebApp.

O resultado do console mostra mensagens semelhantes à seguinte (indicando que o aplicativo está em execução e aguarda solicitações):

$ dotnet run
Hosting environment: Development
Content root path: C:/Docs/aspnetcore/tutorials/dotnet-watch/sample/WebApp
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.

Em um navegador web, navegue até http://localhost:<port number>/api/math/sum?a=4&b=5. Você deve ver o resultado de 9.

Navegue para o API do produto (http://localhost:<port number>/api/math/product?a=4&b=5). Ele retorna 9, não 20, conforme o esperado. Esse problema é corrigido mais adiante no tutorial.

Adicionar dotnet watch a um projeto

A ferramenta de observador de arquivo dotnet watch está incluída com a versão 2.1.300 do SDK do .NET Core. As etapas a seguir são necessárias ao usar uma versão anterior do SDK do .NET Core.

  1. Adicione uma referência de pacote Microsoft.DotNet.Watcher.Tools ao arquivo .csproj:

    <ItemGroup>
        <DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="2.0.0" />
    </ItemGroup>
    
  2. Instale o pacote Microsoft.DotNet.Watcher.Tools executando o seguinte comando:

    dotnet restore
    

Executar os comandos da CLI do .NET Core usando dotnet watch

Qualquer comando da CLI do .NET Core pode ser executado com dotnet watch. Por exemplo:

Comando Comando com inspeção
dotnet run dotnet watch run
dotnet run -f netcoreapp3.1 dotnet watch run -f netcoreapp3.1
dotnet run -f netcoreapp3.1 -- --arg1 dotnet watch run -f netcoreapp3.1 -- --arg1
dotnet test dotnet watch test

Executar dotnet watch run na pasta WebApp. O resultado do console indica que watch foi iniciado.

A execução de dotnet watch run em um aplicativo Web inicia um navegador que navega até a URL do aplicativo quando estiver pronto. dotnet watch faz isso lendo a saída do console do aplicativo e aguardando a mensagem pronta exibida por WebHost.

dotnet watch atualiza o navegador quando detecta alterações em arquivos observados. Para fazer isso, o comando watch injeta um middleware no aplicativo que modifica as respostas HTML criadas pelo aplicativo. O middleware adiciona um bloco de script JavaScript à página que permite a dotnet watch instruir o navegador a atualizar. Atualmente, as alterações em todos os arquivos observados, incluindo conteúdo estático, como arquivos .html e .css, fazem com que o aplicativo seja recriado.

dotnet watch:

  • Só observa arquivos que afetam builds por padrão.
  • Os arquivos observados adicionalmente (por meio da configuração) ainda resultam em um build em execução.

Para saber mais sobre a configuração, confira Configuração de dotnet-watch neste documento.

Observação

dotnet watch --project <PROJECT> pode ser usado para especificar um projeto a ser observado. Por exemplo, a execução de dotnet watch --project WebApp run da raiz do aplicativo de exemplo também executará e observará o projeto WebApp.

Fazer alterações com dotnet watch

Verifique se dotnet watch está em execução.

Corrija o bug no método Product do MathController.cs para que retorne o produto e não a soma:

public static int Product(int a, int b)
{
    return a * b;
}

Salve o arquivo. O resultado do console indica que o dotnet watch detectou uma alteração de arquivo e reiniciou o aplicativo.

Verifique se http://localhost:<port number>/api/math/product?a=4&b=5 retorna o resultado correto.

Executar testes usando o dotnet watch

  1. Altere o método Product de MathController.cs de volta para retornar a soma. Salve o arquivo.

  2. Em um shell de comando, navegue até a pasta WebAppTests.

  3. Execute dotnet restore.

  4. Execute dotnet watch test. Seu resultado indica que um teste falhou e que o observador está aguardando as alterações de arquivo:

    Total tests: 2. Passed: 1. Failed: 1. Skipped: 0.
    Test Run Failed.
    
  5. Corrija o código do método Product para que ele retorne o produto. Salve o arquivo.

dotnet watch detecta a alteração de arquivo e executa os testes novamente. O resultado do console indica a aprovação nos testes.

Personalizar lista de arquivos a observar

Por padrão, o dotnet-watch controla todos os arquivos que correspondem aos seguintes padrões glob:

  • **/*.cs
  • *.csproj
  • **/*.resx
  • Arquivos de conteúdo: wwwroot/**, **/*.config, **/*.json

Mais itens podem ser adicionados à lista de inspeção editando o arquivo .csproj. Os itens podem ser especificados individualmente ou usando padrões glob.

<ItemGroup>
    <!-- extends watching group to include *.js files -->
    <Watch Include="**\*.js" Exclude="node_modules\**\*;**\*.js.map;obj\**\*;bin\**\*" />
</ItemGroup>

Recusa de arquivos a serem observados

dotnet-watch pode ser configurado para ignorar as configurações padrão. Para ignorar arquivos específicos, adicione o atributo Watch="false" à definição de um item no arquivo .csproj:

<ItemGroup>
    <!-- exclude Generated.cs from dotnet-watch -->
    <Compile Include="Generated.cs" Watch="false" />

    <!-- exclude Strings.resx from dotnet-watch -->
    <EmbeddedResource Include="Strings.resx" Watch="false" />

    <!-- exclude changes in this referenced project -->
    <ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj" Watch="false" />
</ItemGroup>
<ItemGroup>
     <!-- Exclude all Content items from being watched. -->
    <Content Update="@(Content)" Watch="false" />
</ItemGroup>

Projetos de inspeção personalizados

dotnet-watch não é restrito a projetos C#. Projetos de inspeção personalizados podem ser criados para lidar com cenários diferentes. Considere o layout de projeto a seguir:

  • test/
    • UnitTests/UnitTests.csproj
    • IntegrationTests/IntegrationTests.csproj

Se a meta é observar ambos os projetos, crie um arquivo de projeto personalizados configurado para observar os dois projetos:

<Project>
    <ItemGroup>
        <TestProjects Include="**\*.csproj" />
        <Watch Include="**\*.cs" />
    </ItemGroup>

    <Target Name="Test">
        <MSBuild Targets="VSTest" Projects="@(TestProjects)" />
    </Target>

    <Import Project="$(MSBuildExtensionsPath)\Microsoft.Common.targets" />
</Project>

Para iniciar a observação de arquivo em ambos os projetos, mude para a pasta de teste. Execute o comando a seguir:

dotnet watch msbuild /t:Test

O VSTest é executado quando há qualquer mudança de arquivo no projeto de teste.

Configuração de dotnet-watch

Algumas opções de configuração podem ser passadas para dotnet watch por meio de variáveis de ambiente. As variáveis disponíveis são:

Configuração Descrição
DOTNET_USE_POLLING_FILE_WATCHER Se definidas como "1" ou "true", dotnet watch usa um observador de arquivo de sondagem em vez de FileSystemWatcher do CoreFx. Usado ao observar arquivos em compartilhamentos de rede ou volumes montados no Docker.
DOTNET_WATCH_SUPPRESS_MSBUILD_INCREMENTALISM Por padrão, dotnet watch otimiza o build evitando determinadas operações, como executar a restauração ou reavaliar o conjunto de arquivos assistidos em cada alteração de arquivo. Se definido como "1" ou "true", essas otimizações serão desabilitadas.
DOTNET_WATCH_SUPPRESS_LAUNCH_BROWSER dotnet watch run tenta iniciar navegadores para aplicativos Web com launchBrowser configurado no launchSettings.json. Se definido como "1" ou "true", esse comportamento será suprimido.
DOTNET_WATCH_SUPPRESS_BROWSER_REFRESH dotnet watch run tenta atualizar navegadores quando detecta alterações de arquivo. Se definido como "1" ou "true", esse comportamento será suprimido. Esse comportamento também será suprimido se DOTNET_WATCH_SUPPRESS_LAUNCH_BROWSER estiver definido.

Atualização de navegador

dotnet watch injeta um script no aplicativo que permite que ele atualize o navegador quando o conteúdo for alterado. Em alguns cenários, como quando o aplicativo habilita a compactação de resposta, dotnet watch pode não ser capaz de injetar o script. Para esses casos em desenvolvimento, injete manualmente o script no aplicativo. Por exemplo, para configurar o aplicativo Web para injetar manualmente o script, atualize o arquivo de layout para incluir _framework/aspnet-browser-refresh.js:

@* _Layout.cshtml *@
<environment names="Development">
    <script src="/_framework/aspnetcore-browser-refresh.js"></script>
</environment>

Caracteres não ASCII

O Visual Studio 17.2 e posterior inclui o SDK do .NET 6.0.300 e posterior. Com o SDK do .NET e 6.0.300 e posteriores, dotnet-watch emite caracteres não ASCII para o console durante uma sessão de recarregamento frequente. Em determinados hosts de console, como o conhost do Windows, esses caracteres podem aparecer embaralhados. Para evitar caracteres embaralhados, considere uma das seguintes abordagens:

  • Configure a variável de ambiente DOTNET_WATCH_SUPPRESS_EMOJIS=1 para suprimir a emissão desses valores.
  • Alterne para um terminal diferente, como https://github.com/microsoft/terminal, que dá suporte à renderização de caracteres não ASCII.