# 主机 (00_Host)
最后更新: 2024-09-20
# 📚 概述
DarkM 主机模块提供了完整的应用程序托管解决方案,支持 Web、API、Electron、Generic 四种主机类型。核心功能包括服务注册、中间件管道、异常处理、CORS、IP 限流等。
源代码位置: DarkM/src/Framework/Host
# 🏗️ 模块架构
# 完整目录结构
Host/
├── Host.Web/ # Web 主机(核心)
│ ├── Options/
│ │ ├── HostOptions.cs # 主机配置项
│ │ └── IpRateLimitConfig.cs # IP 限流配置
│ ├── Middleware/
│ │ ├── ExceptionHandleMiddleware.cs # 全局异常处理中间件
│ │ ├── IPLimitMiddleware.cs # IP 限流中间件
│ │ └── ExceptionHandleMiddlewareExtensions.cs # 异常处理扩展
│ ├── Rewrite/
│ │ ├── RewriteItems.cs # URL 重写规则
│ │ └── ServiceCollectionExtensions.cs # 重写服务注册
│ ├── HostBuilder.cs # 主机构建器
│ ├── StartupAbstract.cs # 启动抽象基类
│ ├── ServiceCollectionExtensions.cs # 服务注册扩展(核心)
│ ├── ApplicationBuilderExtensions.cs # 应用构建扩展(核心)
│ ├── LimitServiceCollectionExtensions.cs# 限流服务扩展
│ ├── IStartLogoProvider.cs # 启动 Logo 接口
│ └── DefaultStartLogoProvider.cs # 默认启动 Logo 实现
│
├── Host.Api/ # API 主机
│ └── Class1.cs
│
├── Host.Electron/ # Electron 桌面主机
│ └── HostBuilder.cs
│
└── Host.Generic/ # 通用主机
├── HostBuilder.cs
└── ServiceCollectionExtensions.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 🔧 核心类详解
# 1. HostOptions(主机配置项)
文件位置: Host.Web/Options/HostOptions.cs
namespace DarkM.Lib.Host.Web.Options
{
/// <summary>
/// 主机配置项
/// </summary>
public class HostOptions
{
/// <summary>
/// 绑定的地址 (默认:http://*:5000)
/// </summary>
public string Urls { get; set; }
/// <summary>
/// 基础地址 (默认:/)
/// </summary>
public string BaseUrl { get; set; }
/// <summary>
/// 开启 Swagger
/// </summary>
public bool Swagger { get; set; }
/// <summary>
/// 启用代理
/// </summary>
public bool Proxy { get; set; }
/// <summary>
/// CORS 预检请求有效期(秒,默认 30 分钟)
/// </summary>
public int PreflightMaxAge { get; set; }
/// <summary>
/// 隐藏启动 Logo
/// </summary>
public bool HideStartLogo { get; set; }
/// <summary>
/// 是否启用 IP 限流
/// </summary>
public bool EnableIPLimit { get; set; }
/// <summary>
/// 是否开启微信
/// </summary>
public bool EnableWechat { get; set; }
/// <summary>
/// 跨域策略 Default/Any
/// </summary>
public string CorsPolicy { get; set; } = "Default";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
配置项说明:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| Urls | string | http://*:5000 | 绑定地址 |
| BaseUrl | string | / | 基础路径 |
| Swagger | bool | false | 启用 Swagger |
| Proxy | bool | false | 启用反向代理 |
| PreflightMaxAge | int | 1800 | CORS 预检有效期 |
| HideStartLogo | bool | false | 隐藏启动 Logo |
| EnableIPLimit | bool | false | 启用 IP 限流 |
| EnableWechat | bool | false | 启用微信 |
| CorsPolicy | string | Default | 跨域策略 |
# 2. IpRateLimitConfig(IP 限流配置)
文件位置: Host.Web/Options/IpRateLimitConfig.cs
public class IpRateLimitConfig
{
/// <summary>
/// 是否启用端点限流
/// </summary>
public bool EnableEndpointRateLimiting { get; set; }
/// <summary>
/// 是否堆叠被阻止的请求
/// </summary>
public bool StackBlockedRequests { get; set; }
/// <summary>
/// 真实 IP 头
/// </summary>
public string RealIpHeader { get; set; }
/// <summary>
/// 客户端 ID 头
/// </summary>
public string ClientIdHeader { get; set; }
/// <summary>
/// HTTP 状态码
/// </summary>
public int HttpStatusCode { get; set; }
/// <summary>
/// 通用规则
/// </summary>
public List<IpRateLimitRule> GeneralRules { get; set; }
}
public class IpRateLimitRule
{
/// <summary>
/// 端点
/// </summary>
public string Endpoint { get; set; }
/// <summary>
/// 周期(如:1m, 1h, 1d)
/// </summary>
public string Period { get; set; }
/// <summary>
/// 限制次数
/// </summary>
public int Limit { get; set; }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 💡 配置说明
# appsettings.json 配置
{
"Host": {
// 绑定的地址 (默认:http://*:5000)
"Urls": "http://*:6220",
// 基础地址 (默认:/)
"BaseUrl": "/",
// 开启 Swagger
"Swagger": false,
// 启用代理
"Proxy": false,
// 指定跨域访问时预检请求的有效期,单位秒,默认 30 分钟
"PreflightMaxAge": 1800,
// 隐藏启动 Logo
"HideStartLogo": false,
// 是否启用 IP 限流
"EnableIPLimit": false,
// 是否启用微信
"EnableWechat": false,
// 跨域策略 Default:不能跨域,Any:容许所有跨域
"CorsPolicy": "Default"
},
// IP 限流配置(当 EnableIPLimit=true 时生效)
"IpRateLimit": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"GeneralRules": [
{
"Endpoint": "*",
"Period": "1m",
"Limit": 100
},
{
"Endpoint": "*/api/auth/*",
"Period": "1m",
"Limit": 10
}
]
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 配置项详解
# Urls
指定主机绑定的地址和端口:
"Urls": "http://*:6220" // 监听所有网络接口,端口 6220
1
支持多个地址:
"Urls": "http://*:6220;https://*:6221"
1
# BaseUrl
基础路径,用于部署在子目录的情况:
"BaseUrl": "/myapp" // 访问地址:http://localhost:6220/myapp
1
# Swagger
是否启用 Swagger 接口文档:
"Swagger": true // 启用后访问 http://localhost:6220/swagger
1
说明: 开发模式下会自动启用,生产环境需手动开启。
# PreflightMaxAge
CORS 预检请求的缓存时间(秒):
"PreflightMaxAge": 1800 // 30 分钟
1
说明: 浏览器在此期间内不用发出另一条预检请求。
# CorsPolicy
跨域策略:
Default- 不能跨域Any- 允许所有跨域
"CorsPolicy": "Any"
1
# 🏗️ 依赖注入
# ServiceCollectionExtensions
文件位置: Host.Web/ServiceCollectionExtensions.cs
public static class ServiceCollectionExtensions
{
/// <summary>
/// 添加 WebHost
/// </summary>
public static IServiceCollection AddWebHost(
this IServiceCollection services,
HostOptions hostOptions,
IHostEnvironment env,
IConfiguration cfg)
{
// 注册 HostOptions
services.AddSingleton(hostOptions);
// 添加 DarkM 核心服务
services.AddDarkMServices();
// 添加 OpenTracing 日志
services.AddOpenTracingLog(cfg);
// 加载缓存
services.AddCache(cfg);
// 加载模块
var modules = services.AddModules();
// 添加对象映射
services.AddMappers(modules);
// 主动或者开发模式下开启 Swagger
if (hostOptions.Swagger || env.IsDevelopment())
{
services.AddSwagger(modules);
}
// 添加 MVC 功能
services.AddMvc(c =>
{
if (hostOptions.Swagger || env.IsDevelopment())
{
// API 分组约定
c.Conventions.Add(new ApiExplorerGroupConvention());
}
// 模块中的 MVC 配置
foreach (var module in modules)
{
((ModuleDescriptor)module).Initializer?.ConfigureMvc(c);
}
})
.AddNewtonsoftJson(options =>
{
// 设置日期格式化格式
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
})
.AddValidators(services) // 添加验证器
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
// IP 限流
if (hostOptions.EnableIPLimit)
{
services.AddIpRateLimit(cfg);
}
// CORS
services.AddCors(options =>
{
var preflightMaxAge = hostOptions.PreflightMaxAge < 0
? new TimeSpan(0, 30, 0)
: new TimeSpan(0, 0, hostOptions.PreflightMaxAge);
// 默认,不容许跨域
options.AddPolicy("Default",
builder => builder
.SetPreflightMaxAge(preflightMaxAge)
.AllowAnyMethod()
.AllowAnyHeader()
.WithExposedHeaders("Content-Disposition"));
// 容许所有跨域
options.AddPolicy("Any",
builder => builder.AllowAnyOrigin()
.SetPreflightMaxAge(preflightMaxAge)
.AllowAnyMethod()
.AllowAnyHeader()
.WithExposedHeaders("Content-Disposition"));
});
// 添加数据库
services.AddDb(cfg, modules);
// 解决 Multipart body length limit exceeded
services.Configure<FormOptions>(x =>
{
x.ValueLengthLimit = int.MaxValue;
x.MultipartBodyLengthLimit = int.MaxValue;
});
// 添加 HttpClient
services.AddHttpClient();
// 添加模块的自定义服务
services.AddModuleServices(modules, env, cfg);
// 添加配置管理
services.AddConfig(cfg);
// 添加 Excel 功能
services.AddExcel(cfg);
// 添加 Pdf 功能
services.AddPdf(cfg);
// 添加 OSS 功能
services.AddOSS(cfg);
// 开启 Basic 身份认证
services.AddBasicAuth();
// 开启 Digest 身份认证
services.AddDigestAuth();
// Jwt 身份认证
services.AddJwtAuth();
// 添加模块初始化服务
services.AddModuleInitializerServices(modules, env, cfg);
// 配置转发规则
services.AddRewrite(cfg);
// 添加默认启动 Logo
services.AddSingleton<IStartLogoProvider, DefaultStartLogoProvider>();
return services;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# ApplicationBuilderExtensions
文件位置: Host.Web/ApplicationBuilderExtensions.cs
public static class ApplicationBuilderExtensions
{
/// <summary>
/// 启用 WebHost
/// </summary>
public static IApplicationBuilder UseWebHost(
this IApplicationBuilder app,
HostOptions hostOptions,
IHostEnvironment env)
{
// 异常处理
app.UseExceptionHandle();
// 设置默认文档
app.UseDefaultFiles();
// 启用默认页
app.UseDefaultPage();
// 启用文档页
app.UseDocs();
// 反向代理
if (hostOptions.Proxy)
{
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto
});
}
// 启用 Rewrite 规则
app.UseRewriter();
// 路由
app.UseRouting();
// 限流
if (hostOptions.EnableIPLimit)
{
app.UseMiddleware<IPLimitMiddleware>();
}
// CORS
if (hostOptions.CorsPolicy.NotNull())
{
app.UseCors(hostOptions.CorsPolicy);
}
else
{
app.UseCors("Default");
}
// 认证
app.UseAuthentication();
// 授权
app.UseAuthorization();
// 配置端点
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
// 开启 Swagger
if (env.IsDevelopment() || hostOptions.Swagger)
{
app.UseCustomSwagger();
}
else
{
app.UseDeveloperExceptionPage();
}
// 加载模块
app.UseModules(env);
return app;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# 💡 使用示例
# 1. 基本 Web 应用
Program.cs:
using DarkM.Lib.Host.Web;
var host = HostBuilder.CreateWebHost(args);
await host.RunAsync();
1
2
3
4
2
3
4
# 2. 自定义启动类
Startup.cs:
using DarkM.Lib.Host.Web;
public class Startup : StartupAbstract
{
public Startup(IHostEnvironment env, IConfiguration cfg)
: base(env, cfg)
{
}
public override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
// 添加自定义服务
services.AddScoped<IMyService, MyService>();
}
public override void Configure(IApplicationBuilder app, IHostApplicationLifetime appLifetime)
{
base.Configure(app, appLifetime);
// 添加自定义中间件
app.UseMiddleware<RequestLogMiddleware>();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 3. 配置 IP 限流
appsettings.json:
{
"Host": {
"EnableIPLimit": true
},
"IpRateLimit": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"GeneralRules": [
{
"Endpoint": "*",
"Period": "1m",
"Limit": 100
},
{
"Endpoint": "*/api/auth/*",
"Period": "1m",
"Limit": 10
}
]
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
说明:
- 所有接口:每分钟最多 100 次请求
- 认证接口:每分钟最多 10 次请求(防暴力破解)
# 🔍 常见问题
# Q1: 如何修改默认端口?
解决方案 1: 修改 appsettings.json
{
"Host": {
"Urls": "http://*:8080"
}
}
1
2
3
4
5
2
3
4
5
解决方案 2: 命令行启动时指定
dotnet run --urls=http://*:8080
1
解决方案 3: 环境变量
export ASPNETCORE_URLS=http://*:8080
dotnet run
1
2
2
# Q2: 如何启用 Swagger?
开发模式(自动启用):
{
"Host": {
"Swagger": false // 开发模式下会自动启用
}
}
1
2
3
4
5
2
3
4
5
生产模式(手动启用):
{
"Host": {
"Swagger": true
}
}
1
2
3
4
5
2
3
4
5
访问地址: http://localhost:6220/swagger
# Q3: 如何配置跨域?
允许所有跨域:
{
"Host": {
"CorsPolicy": "Any"
}
}
1
2
3
4
5
2
3
4
5
默认跨域(带限制):
{
"Host": {
"CorsPolicy": "Default"
}
}
1
2
3
4
5
2
3
4
5
自定义跨域:
services.AddCors(options =>
{
options.AddPolicy("AllowSpecificOrigin",
builder => builder
.WithOrigins("https://example.com")
.AllowAnyHeader()
.AllowAnyMethod());
});
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# Q4: 如何配置反向代理?
启用反向代理:
{
"Host": {
"Proxy": true
}
}
1
2
3
4
5
2
3
4
5
Nginx 配置示例:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:6220;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# Q5: 如何隐藏启动 Logo?
解决方案:
{
"Host": {
"HideStartLogo": true
}
}
1
2
3
4
5
2
3
4
5
# 📚 相关文档
# 🔗 参考链接
- 源代码 (opens new window) -
src/Framework/Host - ASP.NET Core 主机 (opens new window)
- 中间件 (opens new window)
最后更新: 2024-09-20
← 配置项 通用库 (01_Utils) →