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