在 Windows 服務上裝載 ASP.NET CoreHost ASP.NET Core in a Windows Service

作者:Luke LathamTom DykstraBy Luke Latham and Tom Dykstra

ASP.NET Core 應用程式可以裝載在 Windows 上作為 Windows 服務,不需要使用 IIS。An ASP.NET Core app can be hosted on Windows as a Windows Service without using IIS. 當裝載為 Windows 服務時,應用程式將會在重新開機後自動啟動。When hosted as a Windows Service, the app automatically starts after reboots.

檢視或下載範例程式碼 (英文) (如何下載)View or download sample code (how to download)

必要條件Prerequisites

注意

針對早於 Windows 10 2018 年 10 月更新 (1809 版/組建 10.0.17763) 的 Windows OS,必須使用 WindowsCompatibility module (英文) 來匯入 Microsoft.PowerShell.LocalAccounts 模組,以存取用於建立使用者帳戶一節中的 New-LocalUser Cmdlet:For Windows OS earlier than the Windows 10 October 2018 Update (version 1809/build 10.0.17763), the Microsoft.PowerShell.LocalAccounts module must be imported with the WindowsCompatibility module to gain access to the New-LocalUser cmdlet used in the Create a user account section:

Install-Module WindowsCompatibility -Scope CurrentUser
Import-WinModule Microsoft.PowerShell.LocalAccounts

部署類型Deployment type

您可以建立架構相依或自封式 Windows 服務部署。You can create either a framework-dependent or self-contained Windows Service deployment. 如需詳細資訊與部署案例建議,請參閱 .NET Core 應用程式部署For information and advice on deployment scenarios, see .NET Core application deployment.

與 Framework 相依的部署Framework-dependent deployment

架構相依部署 (FDD) 仰賴存在於目標系統上的全系統共用 .NET Core 版本。Framework-dependent deployment (FDD) relies on the presence of a shared system-wide version of .NET Core on the target system. 搭配 ASP.NET Core Windows 服務應用程式使用 FDD 案例時,SDK 會產生可執行檔 (*.exe),稱為架構相依可執行檔。When the FDD scenario is used with an ASP.NET Core Windows Service app, the SDK produces an executable (*.exe), called a framework-dependent executable.

自封式部署Self-contained deployment

自封式部署 (SCD) 不仰賴任何存在於目標系統上的共用元件。Self-contained deployment (SCD) doesn't rely on the presence of shared components on the target system. 執行階段與應用程式的相依性會隨著應用程式部署到裝載系統。The runtime and the app's dependencies are deployed with the app to the hosting system.

將專案轉換成 Windows 服務Convert a project into a Windows Service

對現有的 ASP.NET Core 專進行下列變更,以服務形式執行應用程式:Make the following changes to an existing ASP.NET Core project to run the app as a service:

專案檔更新Project file updates

視您選擇的部署類型而定,更新專案檔:Based on your choice of deployment type, update the project file:

架構相依部署 (FDD)Framework-dependent Deployment (FDD)

將 Windows 執行階段識別碼 (RID) 新增至包含目標 Framework 的 <PropertyGroup>Add a Windows Runtime Identifier (RID) to the <PropertyGroup> that contains the target framework. 在下列範例中,RID 已設定為 win7-x64In the following example, the RID is set to win7-x64. 新增 <SelfContained> 屬性集到 falseAdd the <SelfContained> property set to false. 這些屬性會指示 SDK 產生適用於 Windows 的可執行檔 (.exe)。These properties instruct the SDK to generate an executable (.exe) file for Windows.

針對 Windows Services 應用程式,不需要 web.config 檔案 (發行 ASP.NET Core 應用程式時通常會產生此檔案)。A web.config file, which is normally produced when publishing an ASP.NET Core app, is unnecessary for a Windows Services app. 若要停用 web.config 檔案的建立,請新增 <IsTransformWebConfigDisabled> 屬性集到 trueTo disable the creation of the web.config file, add the <IsTransformWebConfigDisabled> property set to true.

<PropertyGroup>
  <TargetFramework>netcoreapp2.2</TargetFramework>
  <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
  <SelfContained>false</SelfContained>
  <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>

新增 <UseAppHost> 屬性集到 trueAdd the <UseAppHost> property set to true. 此屬性為服務提供 FDD 的啟用路徑 (可執行檔 .exe)。This property provides the service with an activation path (an executable, .exe) for an FDD.

<PropertyGroup>
  <TargetFramework>netcoreapp2.1</TargetFramework>
  <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
  <UseAppHost>true</UseAppHost>
  <SelfContained>false</SelfContained>
  <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>

自封式部署 (SCD)Self-contained Deployment (SCD)

確認有 Windows 執行階段識別碼 (RID),或將 RID 新增至包含目標 Framework 的 <PropertyGroup>Confirm the presence of a Windows Runtime Identifier (RID) or add a RID to the <PropertyGroup> that contains the target framework. 透過新增 <IsTransformWebConfigDisabled> 屬性集到 true,以停用 web.config 檔案的建立。Disable the creation of a web.config file by adding the <IsTransformWebConfigDisabled> property set to true.

<PropertyGroup>
  <TargetFramework>netcoreapp2.2</TargetFramework>
  <RuntimeIdentifier>win7-x64</RuntimeIdentifier>
  <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
</PropertyGroup>

發行多個 RID:To publish for multiple RIDs:

  • 以分號分隔的清單提供 RID。Provide the RIDs in a semicolon-delimited list.

  • 使用屬性名稱 <RuntimeIdentifiers> (複數)。Use the property name <RuntimeIdentifiers> (plural).

    如需詳細資訊,請參閱 .NET Core RID 目錄For more information, see .NET Core RID Catalog.

新增 Microsoft.AspNetCore.Hosting.WindowsServices 的套件參考。Add a package reference for Microsoft.AspNetCore.Hosting.WindowsServices.

若要啟用 Windows 事件記錄的記錄功能,請加入對 Microsoft.Extensions.Logging.EventLog 的套件參考。To enable Windows Event Log logging, add a package reference for Microsoft.Extensions.Logging.EventLog.

如需詳細資訊,請參閱處理開始與停止事件一節。For more information, see the Handle starting and stopping events section.

Program.Main 更新Program.Main updates

Program.Main 中進行下列變更:Make the following changes in Program.Main:

  • 若要在於服務外執行時測試及偵錯,請新增程式碼以判斷應用程式是以服務形式執行或以主控台應用程式形式執行。To test and debug when running outside of a service, add code to determine if the app is running as a service or a console app. 檢查偵錯工具是否已附加或 --console 命令列引數是否存在。Inspect if the debugger is attached or a --console command-line argument is present.

    若任一條件為真 (應用程式不是以服務形式執行),請呼叫 Web 主機上的 RunIf either condition is true (the app isn't run as a service), call Run on the Web Host.

    若條件為偽 (應用程式是以服務形式執行):If the conditions are false (the app is run as a service):

    因為命令列設定提供者 需要命令列引數的名稱值組,在 CreateDefaultBuilder 收到引數之前,--console 切換參數就會從引數移除。Because the Command-line Configuration Provider requires name-value pairs for command-line arguments, the --console switch is removed from the arguments before CreateDefaultBuilder receives them.

  • 若要寫入 Windows 事件記錄檔,請新增 EventLog 提供者到 ConfigureLoggingTo write to the Windows Event Log, add the EventLog provider to ConfigureLogging. 使用 appsettings.Production.json 檔案中的 Logging:LogLevel:Default 機碼設定記錄層級。Set the logging level with the Logging:LogLevel:Default key in the appsettings.Production.json file. 針對示範與測試用途,範例應用程式的生產設定檔案會將記錄層級設定為 InformationFor demonstration and testing purposes, the sample app's Production settings file sets the logging level to Information. 在生產環境中,該值一般會設定為 ErrorIn production, the value is typically set to Error. 如需詳細資訊,請參閱ASP.NET Core 中的記錄For more information, see ASP.NET Core 中的記錄.

public class Program
{
    public static void Main(string[] args)
    {
        var isService = !(Debugger.IsAttached || args.Contains("--console"));
        
        if (isService)
        {
            var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
            var pathToContentRoot = Path.GetDirectoryName(pathToExe);
            Directory.SetCurrentDirectory(pathToContentRoot);
        }

        var builder = CreateWebHostBuilder(
            args.Where(arg => arg != "--console").ToArray());

        var host = builder.Build();

        if (isService)
        {
            // To run the app without the CustomWebHostService change the
            // next line to host.RunAsService();
            host.RunAsCustomService();
        }
        else
        {
            host.Run();
        }
    }

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

發行應用程式Publish the app

使用 dotnet publish (這是一個 Visual Studio 發行設定檔) 或 Visual Studio Code 來發行應用程式。Publish the app using dotnet publish, a Visual Studio publish profile, or Visual Studio Code. 使用 Visual Studio 時,請選取 [FolderProfile] 並設定 [目標位置],再選取 [發行] 按鈕。When using Visual Studio, select the FolderProfile and configure the Target Location before selecting the Publish button.

若要使用命令列介面 (CLI) 工具來發佈範例應用程式,請從專案資料夾在 Windows 命令殼層中執行 dotnet publish 命令,同時搭配將發行設定傳遞至 -c|--configuration 選項。To publish the sample app using command-line interface (CLI) tools, run the dotnet publish command in a Windows command shell from the project folder with a Release configuration passed to the -c|--configuration option. 搭配路徑使用 -o|--output 選項以發行到應用程式以外的資料夾。Use the -o|--output option with a path to publish to a folder outside of the app.

發行架構相依部署 (FDD)Publish a Framework-dependent Deployment (FDD)

在下列範例中,應用程式是發行到 c:\svc 資料夾:In the following example, the app is published to the c:\svc folder:

dotnet publish --configuration Release --output c:\svc

發行自封式部署 (SCD)Publish a Self-contained Deployment (SCD)

必須在 <RuntimeIdenfifier> (或 <RuntimeIdentifiers>) 中指定 RID 專案檔的屬性。The RID must be specified in the <RuntimeIdenfifier> (or <RuntimeIdentifiers>) property of the project file. 提供執行階段給 dotnet publish 命令的 -r|--runtime 選項。Supply the runtime to the -r|--runtime option of the dotnet publish command.

在下列範例中,應用程式是針對 win7-x64 執行階段發行到 c:\svc 資料夾:In the following example, the app is published for the win7-x64 runtime to the c:\svc folder:

dotnet publish --configuration Release --runtime win7-x64 --output c:\svc

建立使用者帳戶Create a user account

從系統管理 PowerShell 6 命令殼層使用 New-LocalUser Cmdlet,為服務建立使用者帳戶:Create a user account for the service using the New-LocalUser cmdlet from an administrative PowerShell 6 command shell:

New-LocalUser -Name {NAME}

在系統提示時提供強式密碼Provide a strong password when prompted.

針對範例應用程式,建立名為 ServiceUser 的使用者帳戶。For the sample app, create a user account with the name ServiceUser.

New-LocalUser -Name ServiceUser

除非搭配過期 DateTime-AccountExpires 參數提供給 New-LocalUser Cmdlet,否則該帳戶將不會過期。Unless the -AccountExpires parameter is supplied to the New-LocalUser cmdlet with an expiration DateTime, the account doesn't expire.

如需詳細資訊,請參閱 Microsoft.PowerShell.LocalAccounts服務使用者帳戶For more information, see Microsoft.PowerShell.LocalAccounts and Service User Accounts.

使用 Active Directory 時有一個管理使用者的替代方法,就是使用「受控服務帳戶」。An alternative approach to managing users when using Active Directory is to use Managed Service Accounts. 如需詳細資訊,請參閱群組受控服務帳戶概觀For more information, see Group Managed Service Accounts Overview.

設定權限:以服務方式登入Set permission: Log on as a service

在系統管理 PowerShell 6 命令殼層中使用 icacls 命令,授與對應用程式資料夾的寫入/讀取/執行存取權。Grant write/read/execute access to the app's folder using the icacls command an administrative PowerShell 6 command shell.

icacls "{PATH}" /grant "{USER ACCOUNT}:(OI)(CI){PERMISSION FLAGS}" /t
  • {PATH} – 應用程式資料夾的路徑。– Path to the app's folder.
  • {USER ACCOUNT} – 使用者帳戶 (SID)。– The user account (SID).
  • (OI) – 物件繼承旗標會將權限傳播到次級檔案。– The Object Inherit flag propagates permissions to subordinate files.
  • (CI) – 容器繼承旗標會將權限傳播到次級資料夾。– The Container Inherit flag propagates permissions to subordinate folders.
  • {PERMISSION FLAGS} – 設定應用程式的存取權限。– Sets the app's access permissions.
    • 寫入 (W)Write (W)
    • 讀取 (R)Read (R)
    • 執行 (X)Execute (X)
    • 完整 (F)Full (F)
    • 修改 (M)Modify (M)
  • /t – 套用遞迴到現有的次級資料夾與檔案。– Apply recursively to existing subordinate folders and files.

針對發行到 c:\svc 資料夾的範例應用程式,以及具有寫入/讀取/執行權限的 ServiceUser 帳戶,請在系統管理 PowerShell 6 命令殼層中使用下列命令。For the sample app published to the c:\svc folder and the ServiceUser account with write/read/execute permissions, use the following command an administrative PowerShell 6 command shell.

icacls "c:\svc" /grant "ServiceUser:(OI)(CI)WRX" /t

如需詳細資訊,請參閱 icaclsFor more information, see icacls.

建立服務Create the service

使用 RegisterService.ps1 的 PowerShell 指令碼註冊服務。Use the RegisterService.ps1 PowerShell script to register the service. 從系統管理 PowerShell 6 命令殼層,搭配下列命令執行指令碼:From an administrative PowerShell 6 command shell, execute the script with the following command:

.\RegisterService.ps1 
    -Name {NAME} 
    -DisplayName "{DISPLAY NAME}" 
    -Description "{DESCRIPTION}" 
    -Exe "{PATH TO EXE}\{ASSEMBLY NAME}.exe" 
    -User {DOMAIN\USER}

在範例應用程式的下列範例中:In the following example for the sample app:

  • 服務的名稱是 MyServiceThe service is named MyService.
  • 已發行的服務位於 c:\svc 資料夾。The published service resides in the c:\svc folder. 應用程式可執行檔名稱是 SampleApp.exeThe app executable is named SampleApp.exe.
  • 服務是以 ServiceUser 帳戶執行。The service runs under the ServiceUser account. 下列範例命令中,本機電腦名稱為 Desktop-PCIn the following example command, the local machine name is Desktop-PC. Desktop-PC 取代為您系統的電腦名稱或網域。Replace Desktop-PC with the computer name or domain for your system.
.\RegisterService.ps1 
    -Name MyService 
    -DisplayName "My Cool Service" 
    -Description "This is the Sample App service." 
    -Exe "c:\svc\SampleApp.exe" 
    -User Desktop-PC\ServiceUser

管理服務Manage the service

啟動服務Start the service

Start-Service -Name {NAME} 的 PowerShell 6 命令啟動服務。Start the service with the Start-Service -Name {NAME} PowerShell 6 command.

請使用下列命令啟動範例應用程式服務:To start the sample app service, use the following command:

Start-Service -Name MyService

此命令需要幾秒鐘啓動服務。The command takes a few seconds to start the service.

判斷服務狀態Determine the service status

若要檢查服務狀態,請使用 Get-Service -Name {NAME} 的 PowerShell 6 命令。To check the status of the service, use the Get-Service -Name {NAME} PowerShell 6 command. 狀態會回報為下列值之一:The status is reported as one of the following values:

  • Starting
  • Running
  • Stopping
  • Stopped

使用下列命令來檢查範例應用程式服務的狀態:Use the following command to check the status of the sample app service:

Get-Service -Name MyService

瀏覽 Web 應用程式服務Browse a web app service

當服務處於 RUNNING 狀態,且若服務是 Web 應用程式時,請在其路徑瀏覽應用程式 (預設為 http://localhost:5000,使用 HTTPS 重新導向中介軟體時會重新導向至 https://localhost:5001)。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).

範例應用程式服務請瀏覽位於 http://localhost:5000 的應用程式。For the sample app service, browse the app at http://localhost:5000.

停止服務Stop the service

Stop-Service -Name {NAME} 的 PowerShell 6 命令停止服務。Stop the service with the Stop-Service -Name {NAME} Powershell 6 command.

下列命令會停止範例應用程式服務:The following command stops the sample app service:

Stop-Service -Name MyService

移除服務Remove the service

在停止服務的短暫延遲之後,請以 Remove-Service -Name {NAME} 的 Powershell 6 命令移除服務。After a short delay to stop a service, remove the service with the Remove-Service -Name {NAME} Powershell 6 command.

檢查範例應用程式服務的狀態:Check the status of the sample app service:

Remove-Service -Name MyService

處理開始與停止事件Handle starting and stopping events

若要處理 OnStartingOnStartedOnStopping 事件,請進行下列額外變更:To handle OnStarting, OnStarted, and OnStopping events, perform the following additional changes:

  1. 使用 OnStartingOnStartedOnStopping 方法建立衍生自 WebHostService 的類別:Create a class that derives from WebHostService with the OnStarting, OnStarted, and OnStopping methods:

    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.LogInformation("OnStarting method called.");
            base.OnStarting(args);
        }
    
        protected override void OnStarted()
        {
            _logger.LogInformation("OnStarted method called.");
            base.OnStarted();
        }
    
        protected override void OnStopping()
        {
            _logger.LogInformation("OnStopping method called.");
            base.OnStopping();
        }
    }
    
  2. IWebHost 建立一個會將 CustomWebHostService 傳遞給 Run 的擴充方法:Create an extension method for IWebHost that passes the CustomWebHostService to Run:

    public static class WebHostServiceExtensions
    {
        public static void RunAsCustomService(this IWebHost host)
        {
            var webHostService = new CustomWebHostService(host);
            ServiceBase.Run(webHostService);
        }
    }
    
  3. Program.Main 中,呼叫 RunAsCustomService 擴充方法,而不是呼叫 RunAsServiceIn Program.Main, call the RunAsCustomService extension method instead of RunAsService:

    host.RunAsCustomService();
    

    若要查看 Program.Main 中的 RunAsService 位置,請參閱將專案轉換為 Windows 服務一節中的範例程式碼。To see the location of RunAsService in Program.Main, refer to the code sample shown in the Convert a project into a Windows Service section.

Proxy 伺服器和負載平衡器案例Proxy server and load balancer scenarios

服務如果會與來自網際網路或公司網路的要求進行互動,並且位於 Proxy 或負載平衡器後方,可能會需要額外的設定。Services that interact with requests from the Internet or a corporate network and are behind a proxy or load balancer might require additional configuration. 如需詳細資訊,請參閱設定 ASP.NET Core 以與 Proxy 伺服器和負載平衡器搭配運作For more information, see 設定 ASP.NET Core 以與 Proxy 伺服器和負載平衡器搭配運作.

設定 HTTPSConfigure HTTPS

使用安全端點來設定服務:To configure the service with a secure endpoint:

  1. 使用您平台的憑證取得與部署機制來建立將用於裝載系統的 X.509 憑證。Create an X.509 certificate for the hosting system using your platform's certificate acquisition and deployment mechanisms.

  2. 指定 Kestrel 伺服器 HTTPS 端點設定以使用憑證。Specify a Kestrel server HTTPS endpoint configuration to use the certificate.

不支援使用 ASP.NET Core HTTPS 開發憑證來保護服務端點。Use of the ASP.NET Core HTTPS development certificate to secure a service endpoint isn't supported.

目前目錄和內容根目錄Current directory and content root

針對 Windows 服務呼叫 GetCurrentDirectory 所傳回的目前工作目錄為 C:\WINDOWS\system32 資料夾。The current working directory returned by calling GetCurrentDirectory for a Windows Service is the C:\WINDOWS\system32 folder. System32 資料夾不是儲存服務檔案 (例如,設定檔) 的合適位置。The system32 folder isn't a suitable location to store a service's files (for example, settings files). 使用下列其中一個方式來維護及存取服務的資產與設定檔。Use one of the following approaches to maintain and access a service's assets and settings files.

將內容根目錄路徑設定到應用程式的資料夾Set the content root path to the app's folder

ContentRootPath 與建立服務時提供給 binPath 引數的路徑相同。The ContentRootPath is the same path provided to the binPath argument when the service is created. 請搭配應用程式內容根目錄的路徑呼叫 SetCurrentDirectory,而不要呼叫 GetCurrentDirectory 來建立設定檔的路徑。Instead of calling GetCurrentDirectory to create paths to settings files, call SetCurrentDirectory with the path to the app's content root.

Program.Main 中,判斷服務可執行檔資料夾的路徑,然後使用該路徑來建立應用程式的內容根目錄:In Program.Main, determine the path to the folder of the service's executable and use the path to establish the app's content root:

var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
var pathToContentRoot = Path.GetDirectoryName(pathToExe);
Directory.SetCurrentDirectory(pathToContentRoot);

CreateWebHostBuilder(args)
    .Build()
    .RunAsService();

將服務的檔案儲存在磁碟上的適當位置Store the service's files in a suitable location on disk

使用包含檔案的 IConfigurationBuilder 資料夾,使用 SetBasePath 來指定絕對路徑。Specify an absolute path with SetBasePath when using an IConfigurationBuilder to the folder containing the files.

其他資源Additional resources