Hospedar o ASP.NET Core em um serviço WindowsHost ASP.NET Core in a Windows Service

Por Luke Latham e Tom DykstraBy Luke Latham and Tom Dykstra

Um aplicativo ASP.NET Core pode ser hospedado no Windows sem usar o IIS como um Serviço Windows.An ASP.NET Core app can be hosted on Windows without using IIS as a Windows Service. Quando hospedado como um serviço Windows, o aplicativo pode iniciar automaticamente após reinicializações e falhas, sem exigir intervenção humana.When hosted as a Windows Service, the app can automatically start after reboots and crashes without requiring human intervention.

Exibir ou baixar código de exemplo (como baixar)View or download sample code (how to download)

Converter um projeto em um serviço WindowsConvert a project into a Windows Service

As alterações mínimas a seguir são necessárias para configurar um projeto existente do ASP.NET Core a ser executado como um serviço:The following minimum changes are required to set up an existing ASP.NET Core project to run as a service:

  1. No arquivo de projeto:In the project file:

    • Confirme a presença de um RID (Identificador de Tempo de Execução) do Windows ou adicione-a ao <PropertyGroup> que contém a estrutura de destino:Confirm the presence of a Windows Runtime Identifier (RID) or add it to the <PropertyGroup> that contains the target framework:

      <PropertyGroup>
         <TargetFramework>netcoreapp2.1</TargetFramework>
         <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
      </PropertyGroup>
      
      <PropertyGroup>
         <TargetFramework>netcoreapp2.0</TargetFramework>
         <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
      </PropertyGroup>
      
      <PropertyGroup>
         <TargetFramework>netcoreapp1.1</TargetFramework>
         <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
      </PropertyGroup>
      

      Para publicar para vários RIDs:To publish for multiple RIDs:

      • Forneça os RIDs em uma lista delimitada por ponto e vírgula.Provide the RIDs in a semicolon-delimited list.
      • Use o nome da propriedade <RuntimeIdentifiers> (plural).Use the property name <RuntimeIdentifiers> (plural).

      Para obter mais informações, consulte Catálogo de RID do .NET Core.For more information, see .NET Core RID Catalog.

    • Adicionar uma referência de pacote para Microsoft.AspNetCore.Hosting.WindowsServices.Add a package reference for Microsoft.AspNetCore.Hosting.WindowsServices.

  2. Faça as seguintes alterações em Program.Main:Make the following changes in Program.Main:

    • Chame host. RunAsService, em vez de host.Run.Call host.RunAsService instead of host.Run.

    • Chame UseContentRoot e use um caminho para o local publicado do aplicativo, em vez de Directory.GetCurrentDirectory().Call UseContentRoot and use a path to the app's published location instead of Directory.GetCurrentDirectory().

      public static void Main(string[] args)
      {
          CreateWebHostBuilder(args).Build().RunAsService();
      }
      
      public static IWebHostBuilder CreateWebHostBuilder(string[] args)
      {
          var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
          var pathToContentRoot = Path.GetDirectoryName(pathToExe);
      
          return WebHost.CreateDefaultBuilder(args)
              .ConfigureAppConfiguration((context, config) =>
              {
                  // Configure the app here.
              })
              .UseContentRoot(pathToContentRoot)
              .UseStartup<Startup>();
      }
      
      public static void Main(string[] args)
      {
          var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
          var pathToContentRoot = Path.GetDirectoryName(pathToExe);
      
          var host = new WebHostBuilder()
              .UseKestrel()
              .UseContentRoot(pathToContentRoot)
              .UseIISIntegration()
              .UseStartup<Startup>()
              .Build();
      
          host.RunAsService();
      }
      
  3. Publique o aplicativo.Publish the app. Use dotnet publish ou um perfil de publicação do Visual Studio.Use dotnet publish or a Visual Studio publish profile. Ao usar o Visual Studio, selecione FolderProfile.When using a Visual Studio, select the FolderProfile.

    Para publicar o aplicativo de exemplo usando as ferramentas da CLI (interface de linha de comando), execute o comando dotnet publish em um prompt de comando da pasta do projeto.To publish the sample app using command-line interface (CLI) tools, run the dotnet publish command at a command prompt from the project folder. O RID deve ser especificado na propriedade <RuntimeIdenfifier> (ou <RuntimeIdentifiers>) do arquivo de projeto.The RID must be specified in the <RuntimeIdenfifier> (or <RuntimeIdentifiers>) property of the project file. No exemplo a seguir, o aplicativo é publicado na Configuração de versão para o tempo de execução win7-x64:In the following example, the app is published in Release configuration for the win7-x64 runtime:

    dotnet publish --configuration Release --runtime win7-x64
    
  4. Use a ferramenta de linha de comando sc.exe para criar o serviço.Use the sc.exe command-line tool to create the service. O valor binPath é o caminho para o executável do aplicativo, que inclui o nome do arquivo executável.The binPath value is the path to the app's executable, which includes the executable file name. O espaço entre o sinal de igual e o caractere de aspas no início do caminho é necessário.The space between the equal sign and the quote character at the start of the path is required.

    sc create <SERVICE_NAME> binPath= "<PATH_TO_SERVICE_EXECUTABLE>"
    

    Para um serviço publicado na pasta do projeto, use o caminho para a pasta publish para criar o serviço.For a service published in the project folder, use the path to the publish folder to create the service. No exemplo a seguir:In the following example:

    • O projeto reside na pasta c:\my_services\AspNetCoreService.The project resides in the c:\my_services\AspNetCoreService folder.
    • O projeto é publicado na configuração Release.The project is published in Release configuration.
    • O TFM (Moniker da Estrutura de Destino) é netcoreapp2.1.The Target Framework Moniker (TFM) is netcoreapp2.1.
    • O RID (Identificador de Tempo de Execução) é win7-x64.The Runtime Identifer (RID) is win7-x64.
    • O nome do executável de aplicativo é AspNetCoreService.exe.The app executable is named AspNetCoreService.exe.
    • O nome do serviço é MyService.The service is named MyService.

    Exemplo:Example:

    sc create MyService binPath= "c:\my_services\AspNetCoreService\bin\Release\netcoreapp2.1\win7-x64\publish\AspNetCoreService.exe"
    

    Importante

    Verifique se existe o espaço entre o argumento binPath= e seu valor.Make sure the space is present between the binPath= argument and its value.

    Para publicar e iniciar o serviço de uma pasta diferente:To publish and start the service from a different folder:

    • Use a opção --output <OUTPUT_DIRECTORY> no comando dotnet publish.Use the --output <OUTPUT_DIRECTORY> option on the dotnet publish command. Se você usar o Visual Studio, configure o Local de Destino na página de propriedades da publicação FolderProfile antes de selecionar o botão Publicar.If using Visual Studio, configure the Target Location in the FolderProfile publish property page before selecting the Publish button.
    • Crie o serviço com o comando sc.exe usando o caminho da pasta de saída.Create the service with the sc.exe command using the output folder path. Inclua o nome do executável do serviço no caminho fornecido para binPath.Include the name of the service's executable in the path provided to binPath.
  5. Inicie o serviço com o comando sc start <SERVICE_NAME>.Start the service with the sc start <SERVICE_NAME> command.

    Para iniciar o serviço de aplicativo de exemplo, use o seguinte comando:To start the sample app service, use the following command:

    sc start MyService
    

    O comando leva alguns segundos para iniciar o serviço.The command takes a few seconds to start the service.

  6. Para verificar o status do serviço, use o comando sc query <SERVICE_NAME>.To check the status of the service, use the sc query <SERVICE_NAME> command. O status é relatado como um dos seguintes valores:The status is reported as one of the following values:

    • START_PENDING
    • RUNNING
    • STOP_PENDING
    • STOPPED

    Use o seguinte comando para verificar o status do serviço de aplicativo de exemplo:Use the following command to check the status of the sample app service:

    sc query MyService
    
  7. Quando o serviço estiver no estado RUNNING e se o serviço for um aplicativo Web, procure o aplicativo em seu caminho (por padrão, http://localhost:5000, que redireciona para https://localhost:5001 ao usar Middleware de Redirecionamento HTTPS).When the service is in the RUNNING state and if the service is a web app, browse the app at its path (by default, http://localhost:5000, which redirects to https://localhost:5001 when using HTTPS Redirection Middleware).

    Para o serviço de aplicativo de exemplo, procure o aplicativo em http://localhost:5000.For the sample app service, browse the app at http://localhost:5000.

  8. Interrompa o serviço com o comando sc stop <SERVICE_NAME>.Stop the service with the sc stop <SERVICE_NAME> command.

    O comando a seguir interrompe o serviço de aplicativo de exemplo:The following command stops the sample app service:

    sc stop MyService
    
  9. Após um pequeno atraso para interromper um serviço, desinstale o serviço com o comando sc delete <SERVICE_NAME>.After a short delay to stop a service, uninstall the service with the sc delete <SERVICE_NAME> command.

    Verifique o status do serviço de aplicativo de exemplo:Check the status of the sample app service:

    sc query MyService
    

    Quando o serviço de aplicativo de exemplo estiver no estado STOPPED, use o seguinte comando para desinstalar o serviço de aplicativo de exemplo:When the sample app service is in the STOPPED state, use the following command to uninstall the sample app service:

    sc delete MyService
    

Execute o aplicativo fora de um serviçoRun the app outside of a service

É mais fácil testar e depurar ao executar fora de um serviço, então é comum adicionar código que chama RunAsService apenas em determinadas condições.It's easier to test and debug when running outside of a service, so it's customary to add code that calls RunAsService only under certain conditions. Por exemplo, o aplicativo pode ser executado como um aplicativo de console com um argumento de linha de comando --console ou se o depurador está anexado:For example, the app can run as a console app with a --console command-line argument or if the debugger is attached:

public static void Main(string[] args)
{
    var isService = !(Debugger.IsAttached || args.Contains("--console"));
    var builder = CreateWebHostBuilder(args.Where(arg => arg != "--console").ToArray());

    if (isService)
    {
        var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
        var pathToContentRoot = Path.GetDirectoryName(pathToExe);
        builder.UseContentRoot(pathToContentRoot);
    }

    var host = builder.Build();

    if (isService)
    {
        host.RunAsService();
    }
    else
    {
        host.Run();
    }
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((context, config) =>
        {
            // Configure the app here.
        })
        .UseStartup<Startup>();

Porque a configuração do ASP.NET Core requer pares nome-valor para argumentos de linha de comando, a opção --console é removida antes que os argumentos sejam passados para CreateDefaultBuilder.Because ASP.NET Core configuration requires name-value pairs for command-line arguments, the --console switch is removed before the arguments are passed to CreateDefaultBuilder.

Observação

isService não é passado do Main para o CreateWebHostBuilder, porque a assinatura do CreateWebHostBuilder deve ser CreateWebHostBuilder(string[]) para que o teste de integração funcione corretamente.isService isn't passed from Main into CreateWebHostBuilder because the signature of CreateWebHostBuilder must be CreateWebHostBuilder(string[]) in order for integration testing to work properly.

public static void Main(string[] args)
{
    var isService = true;

    if (Debugger.IsAttached || args.Contains("--console"))
    {
        isService = false;
    }

    var pathToContentRoot = Directory.GetCurrentDirectory();

    if (isService)
    {
        var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
        pathToContentRoot = Path.GetDirectoryName(pathToExe);
    }

    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(pathToContentRoot)
        .UseIISIntegration()
        .UseStartup<Startup>()
        .Build();

    if (isService)
    {
        host.RunAsService();
    }
    else
    {
        host.Run();
    }
}

Manipular eventos de início e de paradaHandle stopping and starting events

Para tratar eventos OnStarting, OnStarted e OnStopping, faça as seguintes alterações adicionais:To handle OnStarting, OnStarted, and OnStopping events, make the following additional changes:

  1. Crie uma classe que derive de WebHostService:Create a class that derives from WebHostService:

    internal class CustomWebHostService : WebHostService
    {
        public CustomWebHostService(IWebHost host) : base(host)
        {
        }
    
        protected override void OnStarting(string[] args)
        {
            base.OnStarting(args);
        }
    
        protected override void OnStarted()
        {
            base.OnStarted();
        }
    
        protected override void OnStopping()
        {
            base.OnStopping();
        }
    }
    
  2. Crie um método de extensão para IWebHost que passe o WebHostService personalizado para ServiceBase.Run:Create an extension method for IWebHost that passes the custom WebHostService to ServiceBase.Run:

    public static class WebHostServiceExtensions
    {
        public static void RunAsCustomService(this IWebHost host)
        {
            var webHostService = new CustomWebHostService(host);
            ServiceBase.Run(webHostService);
        }
    }
    
  3. Em Program.Main, chame o novo método de extensão, RunAsCustomService, em vez de RunAsService:In Program.Main, call the new extension method, RunAsCustomService, instead of RunAsService:

    public static void Main(string[] args)
    {
        var isService = !(Debugger.IsAttached || args.Contains("--console"));
        var builder = CreateWebHostBuilder(args.Where(arg => arg != "--console").ToArray());
    
        if (isService)
        {
            var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
            var pathToContentRoot = Path.GetDirectoryName(pathToExe);
            builder.UseContentRoot(pathToContentRoot);
        }
    
        var host = builder.Build();
    
        if (isService)
        {
            host.RunAsCustomService();
        }
        else
        {
            host.Run();
        }
    }
    
    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((context, config) =>
            {
                // Configure the app here.
            })
            .UseStartup<Startup>();
    

    Observação

    isService não é passado do Main para o CreateWebHostBuilder, porque a assinatura do CreateWebHostBuilder deve ser CreateWebHostBuilder(string[]) para que o teste de integração funcione corretamente.isService isn't passed from Main into CreateWebHostBuilder because the signature of CreateWebHostBuilder must be CreateWebHostBuilder(string[]) in order for integration testing to work properly.

    public static void Main(string[] args)
    {
        var isService = true;
    
        if (Debugger.IsAttached || args.Contains("--console"))
        {
            isService = false;
        }
    
        var pathToContentRoot = Directory.GetCurrentDirectory();
    
        if (isService)
        {
            var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
            pathToContentRoot = Path.GetDirectoryName(pathToExe);
        }
    
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(pathToContentRoot)
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();
    
        if (isService)
        {
            host.RunAsCustomService();
        }
        else
        {
            host.Run();
        }
    }
    

Se o código WebHostService personalizado exigir um serviço de injeção de dependência (como um agente), obtenha-o da propriedade IWebHost.Services:If the custom WebHostService code requires a service from dependency injection (such as a logger), obtain it from the IWebHost.Services property:

internal class CustomWebHostService : WebHostService
{
    private ILogger _logger;

    public CustomWebHostService(IWebHost host) : base(host)
    {
        _logger = host.Services
            .GetRequiredService<ILogger<CustomWebHostService>>();
    }

    protected override void OnStarting(string[] args)
    {
        _logger.LogDebug("OnStarting method called.");
        base.OnStarting(args);
    }

    protected override void OnStarted()
    {
        _logger.LogDebug("OnStarted method called.");
        base.OnStarted();
    }

    protected override void OnStopping()
    {
        _logger.LogDebug("OnStopping method called.");
        base.OnStopping();
    }
}

Servidor proxy e cenários de balanceador de cargaProxy server and load balancer scenarios

Serviços que interagem com solicitações da Internet ou de uma rede corporativa e estão atrás de um proxy ou de um balanceador de carga podem exigir configuração adicional.Services that interact with requests from the Internet or a corporate network and are behind a proxy or load balancer might require additional configuration. Para obter mais informações, consulte Configure o ASP.NET Core para trabalhar com servidores proxy e balanceadores de carga.For more information, see Configure o ASP.NET Core para trabalhar com servidores proxy e balanceadores de carga.

Configurar o HTTPSConfigure HTTPS

Especifique uma configuração do ponto de extremidade HTTPS do servidor Kestrel.Specify a Kestrel server HTTPS endpoint configuration.

Diretório atual e a raiz do conteúdoCurrent directory and content root

O diretório de trabalho atual retornado ao chamar Directory.GetCurrentDirectory() de um serviço Windows é a pasta C:\WINDOWS\system32.The current working directory returned by calling Directory.GetCurrentDirectory() for a Windows Service is the C:\WINDOWS\system32 folder. A pasta system32 não é um local adequado para armazenar os arquivos de um serviço (por exemplo, os arquivos de configurações).The system32 folder isn't a suitable location to store a service's files (for example, settings files). Use uma das seguintes abordagens para manter e acessar os arquivos de ativos e de configurações de um serviço com FileConfigurationExtensions.SetBasePath ao usar um IConfigurationBuilder:Use one of the following approaches to maintain and access a service's assets and settings files with FileConfigurationExtensions.SetBasePath when using an IConfigurationBuilder:

  • Use o caminho raiz do conteúdo.Use the content root path. O IHostingEnvironment.ContentRootPath é o mesmo caminho fornecido para o argumento binPath quando o serviço é criado.The IHostingEnvironment.ContentRootPath is the same path provided to the binPath argument when the service is created. Em vez de usar Directory.GetCurrentDirectory() para criar caminhos para arquivos de configurações, use o caminho raiz do conteúdo e mantenha os arquivos na raiz do conteúdo do aplicativo.Instead of using Directory.GetCurrentDirectory() to create paths to settings files, use the content root path and maintain the files in the app's content root.
  • Armazene os arquivos em um local adequado no disco.Store the files in a suitable location on disk. Especifique um caminho absoluto com SetBasePath para a pasta que contém os arquivos.Specify an absolute path with SetBasePath to the folder containing the files.

Recursos adicionaisAdditional resources