Skip to content

Commit bce270b

Browse files
committed
[修改]1. 修改HTTP 实现
1 parent 46f9c0a commit bce270b

File tree

3 files changed

+227
-159
lines changed

3 files changed

+227
-159
lines changed

GameFrameX.StartUp/AppStartUpBase.cs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,19 +81,7 @@ protected virtual void Init()
8181
{
8282
}
8383

84-
/// <summary>
85-
/// 配置启动,当InnerIP为空时.将使用Any
86-
/// </summary>
87-
/// <param name="options"></param>
88-
protected virtual void ConfigureHttp(ServerOptions options)
89-
{
90-
var listenOptions = new ListenOptions
91-
{
92-
Ip = IPAddress.Any.ToString(),
93-
Port = Setting.HttpPort,
94-
};
95-
options.AddListener(listenOptions);
96-
}
84+
9785

9886
/// <summary>
9987
/// 配置启动,当InnerIP为空时.将使用Any
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
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

Comments
 (0)