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)

IntroduçãoGet started

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

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

    1. Confirme a presença do identificador de tempo de execução ou adicione-o ao <PropertyGroup> que contém a estrutura de destino:Confirm the presence of the runtime identifier 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>
      
    2. 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)
              .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 da linha de comando, execute o seguinte comando em uma janela do console da pasta de projeto:To publish the sample app from the command line, run the following command in a console window from the project folder:

    dotnet publish --configuration Release
    
  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 está 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:

    1. 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.
    2. 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. O comando sc query <SERVICE_NAME> pode ser usado para verificar o status do serviço para determinar seu status:The sc query <SERVICE_NAME> command can be used to check the status of the service to determine its status:

    • 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
    

Fornecer uma maneira de executar fora de um serviçoProvide a way to run 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)
        .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)
            .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.

Recursos adicionaisAdditional resources