using Flyshot.ControllerClientCompat; using Flyshot.Server.Host; using Flyshot.Server.Host.Middleware; using Flyshot.Server.Host.Services; using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using NLog.Web; using Swashbuckle.AspNetCore.SwaggerGen; // NLog: 在 ASP.NET Core 启动前完成配置加载,确保最早期的日志也能被捕获。 NLog.LogManager.Setup().LoadConfigurationFromAppSettings(); var logger = NLog.LogManager.GetCurrentClassLogger(); try { logger.Info("Flyshot Server Host 启动中..."); var builder = WebApplication.CreateBuilder(args); // NLog: 替换默认日志提供者为 NLog,清除其他 Provider 避免重复输出。 builder.Logging.ClearProviders(); builder.Host.UseNLog(); builder.Services.Configure(builder.Configuration.GetSection("Swagger")); builder.Services.Configure(builder.Configuration.GetSection("Cors")); builder.Services.AddControllerClientCompat(builder.Configuration); // 注册服务启动时自动初始化机器人的托管服务;配置缺失时自动跳过。 builder.Services.AddHostedService(); builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); var swaggerOptions = builder.Configuration.GetSection("Swagger").Get() ?? new HostSwaggerOptions(); builder.Services.AddSwaggerGen(options => { options.SwaggerDoc(swaggerOptions.DocumentName, new OpenApiInfo { Title = swaggerOptions.Title, Version = swaggerOptions.Version }); // 把控制器与 DTO 上的 /// summary 注释纳入 OpenAPI 文档;当前主要服务 Swagger UI, // 调试页的人类可读说明已经静态内嵌在 debug.html 中,不再依赖发布目录中的 XML 文档。 var xmlDocumentationPath = Path.Combine(AppContext.BaseDirectory, $"{typeof(Program).Assembly.GetName().Name}.xml"); if (File.Exists(xmlDocumentationPath)) { options.IncludeXmlComments(xmlDocumentationPath, includeControllerXmlComments: true); } }); var corsOptions = builder.Configuration.GetSection("Cors").Get() ?? new HostCorsOptions(); builder.Services.AddCors(options => { options.AddPolicy(corsOptions.PolicyName, policyBuilder => { // 兼容本地调试时最常见的任意源配置,同时保留显式白名单模式。 if (corsOptions.AllowedOrigins.Length == 1 && string.Equals(corsOptions.AllowedOrigins[0], "*", StringComparison.Ordinal)) { policyBuilder.AllowAnyOrigin(); } else { policyBuilder.WithOrigins(corsOptions.AllowedOrigins); } if (corsOptions.AllowedMethods.Length == 1 && string.Equals(corsOptions.AllowedMethods[0], "*", StringComparison.Ordinal)) { policyBuilder.AllowAnyMethod(); } else { policyBuilder.WithMethods(corsOptions.AllowedMethods); } if (corsOptions.AllowedHeaders.Length == 1 && string.Equals(corsOptions.AllowedHeaders[0], "*", StringComparison.Ordinal)) { policyBuilder.AllowAnyHeader(); } else { policyBuilder.WithHeaders(corsOptions.AllowedHeaders); } }); }); var app = builder.Build(); var resolvedSwaggerOptions = app.Services.GetRequiredService>().Value; var resolvedCorsOptions = app.Services.GetRequiredService>().Value; if (resolvedSwaggerOptions.Enabled) { app.UseSwagger(options => { options.RouteTemplate = resolvedSwaggerOptions.JsonRouteTemplate; }); app.UseSwaggerUI(options => { options.RoutePrefix = resolvedSwaggerOptions.RoutePrefix; options.SwaggerEndpoint( $"/swagger/{resolvedSwaggerOptions.DocumentName}/swagger.json", $"{resolvedSwaggerOptions.Title} {resolvedSwaggerOptions.Version}"); options.DocumentTitle = resolvedSwaggerOptions.Title; }); } app.UseCors(resolvedCorsOptions.PolicyName); app.UseStaticFiles(); // 注册 HTTP 请求/响应日志中间件,记录所有 API 调用的输入输出。 app.UseMiddleware(); app.MapControllers(); logger.Info("Flyshot Server Host 已就绪,开始监听请求。"); app.Run(); } catch (Exception exception) { logger.Error(exception, "Flyshot Server Host 启动失败。"); throw; } finally { NLog.LogManager.Shutdown(); } public partial class Program;