.NET 6如何为现有Windows Service应用添加Web API支持
方案结论
完全不需要重构迁移现有项目,.NET 6 原生支持在 Windows Service 进程内直接自托管 ASP.NET Core Web API,不需要依赖IIS,服务启动时自动拉起HTTP监听,服务停止时同步释放端口资源,是成本最低、稳定性最高的方案。
你提到的 Worker Service 迁移和集成 Web API 两个步骤可以合并完成,不需要分开操作——.NET 6 下 Windows Service、Worker Service、ASP.NET Core 都是基于同一个通用宿主(Generic Host)构建的,天然可以无缝整合。
具体实现步骤
1. 调整项目配置
编辑项目csproj文件,做两处修改:
- 引入ASP.NET Core共享框架,获得Web相关能力
- 引入官方包对接Windows Service生命周期
示例配置片段:
<Project Sdk="Microsoft.NET.Sdk.Worker"> <PropertyGroup> <TargetFramework>net6.0</TargetFramework> <OutputType>Exe</OutputType> </PropertyGroup> <ItemGroup> <FrameworkReference Include="Microsoft.AspNetCore.App" /> <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.*" /> </ItemGroup> </Project>
如果项目原来用的是Microsoft.NET.Sdk SDK,直接替换成上面的Microsoft.NET.Sdk.Worker即可,不会影响原有业务逻辑。
2. 替换启动入口代码
把原来的ServiceBase.Run启动逻辑替换为通用宿主启动逻辑,同时注册原有后台服务和Web API能力,示例代码如下:
// Program.cs using WinService1; var builder = Host.CreateDefaultBuilder(args); // 将宿主生命周期绑定到Windows Service,原生支持服务的启动、停止、重启事件 builder.UseWindowsService(); // 注册原有Windows Service业务逻辑 builder.ConfigureServices(services => { // 把原来继承ServiceBase的WinService1迁移为继承BackgroundService // 原OnStart中的初始化逻辑迁移到StartAsync/ExecuteAsync,原OnStop中的释放逻辑迁移到StopAsync services.AddHostedService<WinService1>(); // 原有项目的依赖注入、配置、日志注册逻辑保持不变 }); // 集成Web API能力 builder.ConfigureWebHostDefaults(webBuilder => { // 配置HTTP监听端口,*代表监听所有网卡地址,可按需调整为127.0.0.1(仅本地访问)或指定内网IP webBuilder.UseUrls("http://*:5080"); webBuilder.ConfigureServices(services => { // 注册Web API控制器,和普通Web API项目配置完全一致 services.AddControllers(); // 按需添加Swagger、认证授权、跨域等能力即可 }); webBuilder.Configure(app => { // 按需配置中间件 app.UseRouting(); app.UseEndpoints(endpoints => { // 映射控制器路由 endpoints.MapControllers(); }); }); }); var host = builder.Build(); await host.RunAsync();
3. 添加Web API业务代码
在项目里新建Controllers文件夹,按照普通ASP.NET Core Web API的写法编写控制器即可,控制器里可以直接通过依赖注入拿到原有Windows Service里的业务服务实例,同进程调用没有跨进程开销。
示例控制器:
[ApiController] [Route("api/[controller]")] public class ServiceStatusController : ControllerBase { private readonly WinService1 _service; public ServiceStatusController(WinService1 service) { _service = service; } [HttpGet] public IActionResult GetStatus() { return Ok(new { IsRunning = true, ServiceName = _service.ServiceName }); } }
注意事项
- 选择监听端口时避开系统保留端口范围,部署后记得在Windows防火墙中放开对应端口的入站规则,否则外部设备无法访问
- 原有Windows Service的业务逻辑迁移到BackgroundService的成本极低,不需要重写核心业务代码,只需要把生命周期钩子对应到新的方法即可
- 部署方式和原有Windows Service完全一致,仍然可以通过
sc create命令创建服务,不需要额外部署Web服务器 - 如果需要HTTPS支持,直接在UseUrls中添加HTTPS地址并配置证书即可,和普通Web API项目配置逻辑没有区别
- 如果暂时不想改动原有继承ServiceBase的WinService1代码,也可以通过IHostApplicationLifetime的应用启动/停止事件手动触发ServiceBase的运行逻辑,但这种方式需要手动对齐两个生命周期,容易出现端口残留、状态不一致问题,推荐花少量时间把原有逻辑迁移到BackgroundService,长期维护成本更低
内容的提问来源于stack exchange,提问作者Aydzan Ahmedov
相关产品推荐
相关产品推荐

