建立3个项目,分别是类库项目ConfigTool.WebSite、webapi项目ConfigTool.TestWebSite、webapi项目ConfigTool.WinService,目标框架均为.NET8。

  其中控制台ConfigTool.TestWebSite方便开发调试,win服务ConfigTool.WinService作为宿主服务,类库ConfigTool.WebSite为自定义web服务器的主体代码。

一、项目框架

ConfigTool.WebSite引用nuget包:

Microsoft.Extensions.Hosting 8.0.0
Microsoft.Extensions.Hosting.Abstractions 8.0.0
Microsoft.NETCore.DotNetHost 8.0.6
Swashbuckle.AspNetCore 6.6.2

ConfigTool.WebSite的项目文件内容如下:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
    <PackageReference Include="Microsoft.NETCore.DotNetHost" Version="8.0.6" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
  </ItemGroup>
	
  <ItemGroup>
    <None Remove="wwwroot\index.html" />
  </ItemGroup>

  <ItemGroup>
    <Content Include="wwwroot\index.html">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>

  <ItemGroup>
    <None Update="wwwroot\images\1.jpg">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>

</Project>

ConfigTool.TestWebSite的项目文件内容如下:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <SupportedOSPlatformVersion>10.0.17763.0</SupportedOSPlatformVersion>
  </PropertyGroup>
  
  <ItemGroup>
    <ProjectReference Include="..\ConfigTool.WebSite\ConfigTool.WebSite.csproj" />
  </ItemGroup>

</Project>

ConfigTool.WinService引用nuget包:

Microsoft.Extensions.Hosting.WindowsServices 8.0.0

ConfigTool.WinService的项目文件内容如下:

<Project Sdk="Microsoft.NET.Sdk.Web">

	<PropertyGroup>
		<TargetFramework>net8.0-windows</TargetFramework>
		<Nullable>enable</Nullable>
		<ImplicitUsings>enable</ImplicitUsings>
		<RootNamespace>App.WindowsService</RootNamespace>
		<OutputType>exe</OutputType>
		<PublishSingleFile Condition="'$(Configuration)' == 'Release'">true</PublishSingleFile>
		<RuntimeIdentifier>win-x64</RuntimeIdentifier>
		<PlatformTarget>x64</PlatformTarget>
		<UserSecretsId>dotnet-smart_sign-ef6ac77d-c996-482b-8f81-5d63d0c287a5</UserSecretsId>
	</PropertyGroup>

	<ItemGroup>
	  <PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
	</ItemGroup>

	<ItemGroup>
	  <ProjectReference Include="..\ConfigTool.WebSite\ConfigTool.WebSite.csproj" />
	</ItemGroup>

	<ItemGroup>
	  <None Update="install.bat">
	    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
	  </None>
	  <None Update="uninstall.bat">
	    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
	  </None>
	</ItemGroup>

</Project>

二、代码编写

2.1、ConfigTool.WebSite

在ConfigTool.WebSite中增加目录Controllers和wwwroot

using Microsoft.AspNetCore.Mvc;

namespace ConfigTool.WebSite.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        [HttpGet]
        [Route("Time")]
        public string Time()
        {
            return DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
        }

    }
}

在wwwroot下放入index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
</head>
<body>
    <p>
        <input type="text" id="curtime" value="1"/>
        <button id="submit">从api接口获取当前服务器时间</button>
    </p>
    <p>
        <image src="images/1.jpg" alt="验证静态资源" width="300" height="300"/>
    </p>
</body>

<script>
    $("#submit").click(function(){
        $.get("api/Values/Time",function(data,status){    
            $("#curtime").val(data);
        });
    });
</script>
</html>

新增WebServer类文件

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;

namespace ConfigTool.WebSite
{
    public class WebServer
    {
        readonly WebApplication app;
        int? port = 8080;
        string? url;
        public WebServer()
        {
            var webBuilder = WebApplication.CreateBuilder();
            webBuilder.Services.AddControllers();
            app = webBuilder.Build();
            app.MapControllers();
        }

        public void Init(int port)
        {
            this.port = port;
            app.UseStaticFiles(new StaticFileOptions
            {
                FileProvider = new PhysicalFileProvider(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot")),
                RequestPath = ""
            });
            var host = "*";
            url = $"http://{host}:{port}";
            app.Urls.Add(url);
        }

        /// <summary>
        /// 启动web服务,并阻塞调用进程直到程序退出,这里提供给控制台程序调用
        /// </summary>
        public void Run()
        {
            app.Run();
        }

        /// <summary>
        /// 启动web服务,这里提供给服务程序调用
        /// </summary>
        public void Start()
        {
            app.Start();
        }

        /// <summary>
        /// 停止web服务,这里提供给服务程序调用
        /// </summary>
        public void Stop()
        {
            app.StopAsync();
        }


    }
}

2.2、ConfigTool.TestWebSite

ConfigTool.TestWebSite的Program.cs代码如下:

using ConfigTool.WebSite;

namespace ConfigTool.TestWebSite
{
    internal class Program
    {
        static void Main(string[] args)
        {
            WebServer server = new();
            server.Init(8080);
            server.Run();

            Console.ReadLine();
        }
    }
}

可以用来调试业务代码。

2.3、ConfigTool.WinService

ConfigTool.WinService的的Program.cs代码如下:

using App.WindowsService;
using ConfigTool.WebSite;

namespace ConfigTool.WinService
{
    public class Program
    {
        public static void Main(string[] args)
        {
            HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
            builder.Services.AddWindowsService(options =>
            {
                options.ServiceName = "ConfigTool.WinService";
            });

            builder.Services.AddSingleton<WebServer>();
            builder.Services.AddHostedService<WindowsBackgroundService>();

            IHost host = builder.Build();
            host.Run();
        }
    }
}

WindowsBackgroundService.cs代码如下:

using ConfigTool.WebSite;

namespace App.WindowsService;

public sealed class WindowsBackgroundService : BackgroundService
{
    private readonly WebServer webServer;
    private readonly ILogger<WindowsBackgroundService> logger;

    private bool isRunning = false;
    private static readonly object locker = new();

    public WindowsBackgroundService(WebServer webServer, ILogger<WindowsBackgroundService> logger)
    {
        this.webServer = webServer;
        this.logger = logger;
        this.webServer.Init(5000);
    }

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        return Task.CompletedTask;
    }

    public override Task StartAsync(CancellationToken cancellationToken)
    {
        if (isRunning)
        {
            return Task.CompletedTask;
        }
        if (!isRunning)
        {
            lock (locker)
            {
                if (isRunning)
                {
                    return Task.CompletedTask;
                }
                isRunning = true;
            }
        }
        this.webServer.Start();

        return base.StartAsync(cancellationToken);
    }

    public override Task StopAsync(CancellationToken cancellationToken)
    {
        if (!isRunning)
        {
            return Task.CompletedTask;
        }
        if (isRunning)
        {
            lock (locker)
            {
                if (!isRunning)
                {
                    return Task.CompletedTask;
                }
                isRunning = false;
            }
        }
        this.webServer.Stop();
        return base.StopAsync(cancellationToken);
    }

}

install.bat的内容如下:

echo "service installing......" 
echo off  
@sc create ConfigTool.WinService binpath="%~dp0ConfigTool.WinService.exe"
@sc config ConfigTool.WinService start=auto
@net start ConfigTool.WinService
echo on 
echo "service started"
timeout /t 5

其中%~dp0表示bat脚本的当前目录的意思。

uninstall.bat的内容如下:

echo "uninstall service......" 
echo off  
@net stop ConfigTool.WinService
@sc delete ConfigTool.WinService
echo on
echo "service uninstalled"
timeout /t 5

 

 posted on 2024-06-06 18:27  Lucien.Bao  阅读(39)  评论(0编辑  收藏  举报