1+ using System . Net ;
2+ using System . Reflection ;
3+ using GameFrameX . Foundation . Logger ;
4+ using GameFrameX . NetWork ;
5+ using GameFrameX . NetWork . Abstractions ;
6+ using GameFrameX . NetWork . HTTP ;
7+ using GameFrameX . NetWork . Message ;
8+ using GameFrameX . StartUp . Options ;
9+ using GameFrameX . SuperSocket . Connection ;
10+ using GameFrameX . SuperSocket . Primitives ;
11+ using GameFrameX . SuperSocket . ProtoBase ;
12+ using GameFrameX . SuperSocket . Server ;
13+ using GameFrameX . SuperSocket . Server . Abstractions ;
14+ using GameFrameX . SuperSocket . Server . Abstractions . Session ;
15+ using GameFrameX . SuperSocket . Server . Host ;
16+ using GameFrameX . SuperSocket . WebSocket ;
17+ using GameFrameX . SuperSocket . WebSocket . Server ;
18+ using GameFrameX . Utility ;
19+ using GameFrameX . Utility . Extensions ;
20+ using GameFrameX . Utility . Setting ;
21+ using Microsoft . AspNetCore . Builder ;
22+ using Microsoft . AspNetCore . Diagnostics ;
23+ using Microsoft . AspNetCore . Hosting ;
24+ using Microsoft . AspNetCore . Http ;
25+ using Microsoft . Extensions . Configuration ;
26+ using Microsoft . Extensions . DependencyInjection ;
27+ using Microsoft . Extensions . Hosting ;
28+ using Microsoft . Extensions . Logging ;
29+ using Microsoft . OpenApi . Models ;
30+ using OpenTelemetry . Resources ;
31+ using OpenTelemetry . Trace ;
32+ using Serilog ;
33+ using CloseReason = GameFrameX . SuperSocket . WebSocket . CloseReason ;
34+
35+ namespace GameFrameX . StartUp ;
36+
37+ /// <summary>
38+ /// 程序启动器基类 - 提供TCP和WebSocket服务器的基础功能实现
39+ /// </summary>
40+ public abstract partial class AppStartUpBase
41+ {
42+ /// <summary>
43+ /// 启动 HTTP 服务器
44+ /// </summary>
45+ /// <param name="baseHandler">HTTP处理器列表,用于处理不同的HTTP请求</param>
46+ /// <param name="httpFactory">HTTP处理器工厂,根据命令标识符创建对应的处理器实例</param>
47+ /// <param name="aopHandlerTypes">AOP处理器列表,用于在HTTP请求处理前后执行额外的逻辑</param>
48+ /// <param name="minimumLevelLogLevel">日志记录的最小级别,用于控制日志输出</param>
49+ private async Task StartHttpServer ( List < BaseHttpHandler > baseHandler , Func < string , BaseHttpHandler > httpFactory , List < IHttpAopHandler > aopHandlerTypes = null , LogLevel minimumLevelLogLevel = LogLevel . Debug )
50+ {
51+ var apiRootPath = Setting . HttpUrl ;
52+ // 根路径必须以/开头和以/结尾
53+ if ( ! Setting . HttpUrl . StartsWith ( '/' ) )
54+ {
55+ apiRootPath = "/" + Setting . HttpUrl ;
56+ }
57+
58+ if ( ! Setting . HttpUrl . EndsWith ( '/' ) )
59+ {
60+ apiRootPath += "/" ;
61+ }
62+
63+ GlobalSettings . ApiRootPath = apiRootPath ;
64+
65+ LogHelper . InfoConsole ( "启动 [HTTP] 服务器..." ) ;
66+ if ( Setting . HttpPort is > 0 and < ushort . MaxValue && NetHelper . PortIsAvailable ( Setting . HttpPort ) )
67+ {
68+ var builder = WebApplication . CreateBuilder ( ) ;
69+
70+ var development = Setting . HttpIsDevelopment || builder . Environment . IsDevelopment ( ) ;
71+
72+ var openApiInfo = GetOpenApiInfo ( ) ;
73+ if ( development )
74+ {
75+ // 添加 Swagger 服务
76+ builder . Services . AddEndpointsApiExplorer ( ) ;
77+ builder . Services . AddSwaggerGen ( options =>
78+ {
79+ options . SwaggerDoc ( openApiInfo . Version , openApiInfo ) ;
80+
81+ // 使用自定义的 SchemaFilter 来保持属性名称大小写
82+ options . SchemaFilter < PreservePropertyCasingSchemaFilter > ( ) ;
83+
84+ // 添加自定义操作过滤器来处理动态路由
85+ options . OperationFilter < SwaggerOperationFilter > ( baseHandler ) ;
86+ // 使用完整的类型名称
87+ options . CustomSchemaIds ( type => type . Name ) ;
88+ } ) ;
89+ }
90+
91+
92+ builder . WebHost . UseKestrel ( options =>
93+ {
94+ options . ListenAnyIP ( Setting . HttpPort ) ;
95+
96+ // HTTPS
97+ if ( Setting . HttpsPort > 0 && NetHelper . PortIsAvailable ( Setting . HttpsPort ) )
98+ {
99+ throw new NotImplementedException ( "HTTPS 未实现,请取消HTTPS端口配置" ) ;
100+
101+ // options.ListenAnyIP(Setting.HttpsPort, listenOptions => { listenOptions.UseHttps(); });
102+ }
103+ } ) . ConfigureLogging ( logging =>
104+ {
105+ logging . ClearProviders ( ) ;
106+ logging . AddSerilog ( Log . Logger ) ;
107+ logging . SetMinimumLevel ( minimumLevelLogLevel ) ;
108+ } ) ;
109+
110+ var app = builder . Build ( ) ;
111+ if ( development )
112+ {
113+ // 添加 Swagger 中间件
114+ app . UseSwagger ( ) ;
115+ app . UseSwaggerUI ( options =>
116+ {
117+ options . SwaggerEndpoint ( $ "/swagger/{ openApiInfo . Version } /swagger.json", openApiInfo . Title ) ;
118+ options . RoutePrefix = "swagger" ;
119+ } ) ;
120+ var ipList = NetHelper . GetLocalIpList ( ) ;
121+ foreach ( var ip in ipList )
122+ {
123+ LogHelper . DebugConsole ( $ "Swagger UI 可通过 http://{ ip } :{ Setting . HttpPort } /swagger 访问") ;
124+ }
125+ }
126+
127+ app . UseExceptionHandler ( ExceptionHandler ) ;
128+
129+ // 每个http处理器,注册到路由中
130+ foreach ( var handler in baseHandler )
131+ {
132+ var handlerType = handler . GetType ( ) ;
133+ var mappingAttribute = handlerType . GetCustomAttribute < HttpMessageMappingAttribute > ( ) ;
134+ if ( mappingAttribute == null )
135+ {
136+ continue ;
137+ }
138+
139+ // 只支持POST请求
140+ var route = app . MapPost ( $ "{ apiRootPath } { mappingAttribute . StandardCmd } ", async ( HttpContext context , string text ) => { await HttpHandler . HandleRequest ( context , httpFactory , aopHandlerTypes ) ; } ) ;
141+ if ( development )
142+ {
143+ // 开发模式,启用 Swagger
144+ route . WithOpenApi ( operation =>
145+ {
146+ operation . Summary = "处理 POST 请求" ;
147+ operation . Description = "处理来自游戏客户端的 POST 请求" ;
148+ return operation ;
149+ } ) ;
150+ }
151+ }
152+
153+ await app . StartAsync ( ) ;
154+ LogHelper . InfoConsole ( $ "启动 [HTTP] 服务器启动完成 - 端口: { Setting . HttpPort } ") ;
155+ }
156+ else
157+ {
158+ LogHelper . Error ( $ "启动 [HTTP] 服务器 端口 [{ Setting . HttpPort } ] 被占用,无法启动HTTP服务") ;
159+ }
160+ }
161+
162+ /// <summary>
163+ /// 配置启动,当InnerIP为空时.将使用Any
164+ /// </summary>
165+ /// <param name="options"></param>
166+ protected virtual void ConfigureHttp ( ServerOptions options )
167+ {
168+ // configApp.AddInMemoryCollection(new Dictionary<string, string>
169+ // {
170+ // { "serverOptions:name", "TestServer" },
171+ // { "serverOptions:listeners:0:ip", "Any" },
172+ // { "serverOptions:listeners:0:port", "4040" }
173+ // });
174+
175+ var listenOptions = new ListenOptions
176+ {
177+ Ip = IPAddress . Any . ToString ( ) ,
178+ Port = Setting . HttpPort ,
179+ } ;
180+ options . AddListener ( listenOptions ) ;
181+ }
182+
183+ /// <summary>
184+ /// 获取或创建 Swagger信息
185+ /// </summary>
186+ /// <returns></returns>
187+ private OpenApiInfo GetOpenApiInfo ( )
188+ {
189+ // 添加 Swagger 服务
190+ var version = Assembly . GetExecutingAssembly ( ) . GetName ( ) . Version ;
191+ if ( version == null )
192+ {
193+ version = new Version ( 1 , 0 , 0 ) ;
194+ }
195+
196+ var openApiInfo = new OpenApiInfo
197+ {
198+ Title = "GameFrameX API" ,
199+ Version = $ "v{ version . Major } .{ version . Minor } ",
200+ TermsOfService = new Uri ( "https://gameframex.doc.alianblank.com" ) ,
201+ Contact = new OpenApiContact ( ) { Url = new Uri ( "https://gameframex.doc.alianblank.com" ) , Name = "Blank" , Email = "[email protected] " , } , 202+ License = new OpenApiLicense ( ) { Name = "GameFrameX" , Url = new Uri ( "https://github.com/GameFrameX/GameFrameX" ) , } ,
203+ Description = "GameFrameX HTTP API documentation" ,
204+ } ;
205+ return openApiInfo ;
206+ }
207+
208+ /// <summary>
209+ /// 异常处理
210+ /// </summary>
211+ /// <param name="errorContext"></param>
212+ private static void ExceptionHandler ( IApplicationBuilder errorContext )
213+ {
214+ errorContext . Run ( async context =>
215+ {
216+ // 获取异常信息
217+ var exceptionHandlerPathFeature = context . Features . Get < IExceptionHandlerPathFeature > ( ) ;
218+
219+ // 自定义返回Json信息;
220+ await context . Response . WriteAsync ( exceptionHandlerPathFeature . Error . Message ) ;
221+ } ) ;
222+ }
223+ }
0 commit comments