#if WINDOWS using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Hosting; using System.Text.Json; using Hua.Todo.Application; using Hua.Todo.Application.DynamicApi; using Hua.Todo.Maui.Models; using AppSettings = Hua.Todo.Maui.Models.AppSettings; namespace Hua.Todo.Maui.Services; /// /// Windows 平台嵌入式 Web 服务器实现 /// 使用 ASP.NET Core 运行 /// public class EmbeddedWebServerService : IEmbeddedWebServerService { private WebApplication? _webApp; private readonly AppSettings _appSettings; /// /// 服务器是否正在运行 /// public bool IsRunning => _webApp != null; /// /// 服务器基础 URL /// public string BaseUrl => _appSettings.WebServer.HostUrl; /// /// 初始化嵌入式 Web 服务器服务 /// /// 应用程序配置 public EmbeddedWebServerService(AppSettings appSettings) { _appSettings = appSettings; } /// /// 异步启动服务器。 /// 启动时机:通常在应用启动或用户手动开启服务时调用。 /// 错误处理:启动失败会抛出 ASP.NET Core 相关异常,建议在调用方进行 catch 处理。 /// 线程安全:非 UI 线程相关,可在后台线程调用;方法内部已处理重入(若已运行则直接返回)。 /// /// 表示启动操作的任务 public async Task StartAsync() { if (_webApp != null) return; var builder = WebApplication.CreateBuilder(); builder.WebHost.UseUrls(_appSettings.WebServer.HostUrl); // 配置控制器和 JSON 选项 builder.Services.AddControllers() .AddApplicationPart(typeof(Hua.Todo.Application.ServiceCollectionExtensions).Assembly) .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; }); builder.Services.AddEndpointsApiExplorer(); // 注册应用逻辑服务 builder.Services.AddApplicationServices(_appSettings.WebServer.ConnectionString); // 配置跨域策略 builder.Services.AddCors(options => { options.AddPolicy("AllowAll", policy => { policy.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); }); }); var app = builder.Build(); // 如果配置为使用静态文件(前端托管),则配置静态文件服务 if (_appSettings.WebServer.IsUsingStatic) { ServeStaticFiles(app); } app.UseCors("AllowAll"); app.UseAuthorization(); app.UseDynamicApi(); app.MapControllers(); _webApp = app; await _webApp.StartAsync(); } /// /// 配置静态文件服务(用于托管 Vue 前端) /// /// Web 应用程序实例 private void ServeStaticFiles(WebApplication app) { try { var wwwrootPath = Path.Combine(AppContext.BaseDirectory, "wwwroot"); if (!Directory.Exists(wwwrootPath)) { Console.WriteLine("[EmbeddedWebServer] wwwroot directory not found. Static file serving disabled."); return; } var fileProvider = new PhysicalFileProvider(wwwrootPath); var defaultFilesOptions = new DefaultFilesOptions { FileProvider = fileProvider, RequestPath = "" }; app.UseDefaultFiles(defaultFilesOptions); var staticFileOptions = new StaticFileOptions { FileProvider = fileProvider, RequestPath = "", OnPrepareResponse = ctx => { ctx.Context.Response.Headers["Cache-Control"] = "no-cache, no-store, must-revalidate"; ctx.Context.Response.Headers["Pragma"] = "no-cache"; ctx.Context.Response.Headers["Expires"] = "0"; } }; app.UseStaticFiles(staticFileOptions); // 处理 SPA 路由 app.Use(async (context, next) => { if (context.Request.Path.HasValue) { var path = context.Request.Path.Value; if (path != "/" && !path.StartsWith("/assets", StringComparison.OrdinalIgnoreCase) && !path.StartsWith("/api", StringComparison.OrdinalIgnoreCase)) { var ext = Path.GetExtension(path); if (string.IsNullOrEmpty(ext)) { context.Request.Path = "/index.html"; } } } await next(); }); Console.WriteLine($"[EmbeddedWebServer] Serving static files from: {wwwrootPath}"); } catch (Exception ex) { Console.WriteLine($"[EmbeddedWebServer] Failed to serve static files: {ex.Message}"); } } /// /// 异步停止服务器。 /// 释放服务器资源并停止监听。 /// /// 表示停止操作的任务 public async Task StopAsync() { if (_webApp == null) return; await _webApp.StopAsync(); await _webApp.DisposeAsync(); _webApp = null; } } #endif