# 主机 (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

# 🔧 核心类详解

# 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

配置项说明:

属性 类型 默认值 说明
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

# 💡 配置说明

# 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

# 配置项详解

# 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

# 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

# 💡 使用示例

# 1. 基本 Web 应用

Program.cs:

using DarkM.Lib.Host.Web;

var host = HostBuilder.CreateWebHost(args);
await host.RunAsync();
1
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

# 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

说明:

  • 所有接口:每分钟最多 100 次请求
  • 认证接口:每分钟最多 10 次请求(防暴力破解)

# 🔍 常见问题

# Q1: 如何修改默认端口?

解决方案 1: 修改 appsettings.json

{
  "Host": {
    "Urls": "http://*:8080"
  }
}
1
2
3
4
5

解决方案 2: 命令行启动时指定

dotnet run --urls=http://*:8080
1

解决方案 3: 环境变量

export ASPNETCORE_URLS=http://*:8080
dotnet run
1
2

# Q2: 如何启用 Swagger?

开发模式(自动启用):

{
  "Host": {
    "Swagger": false  // 开发模式下会自动启用
  }
}
1
2
3
4
5

生产模式(手动启用):

{
  "Host": {
    "Swagger": true
  }
}
1
2
3
4
5

访问地址: http://localhost:6220/swagger


# Q3: 如何配置跨域?

允许所有跨域:

{
  "Host": {
    "CorsPolicy": "Any"
  }
}
1
2
3
4
5

默认跨域(带限制):

{
  "Host": {
    "CorsPolicy": "Default"
  }
}
1
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

# Q4: 如何配置反向代理?

启用反向代理:

{
  "Host": {
    "Proxy": true
  }
}
1
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

解决方案:

{
  "Host": {
    "HideStartLogo": true
  }
}
1
2
3
4
5

# 📚 相关文档


# 🔗 参考链接


最后更新: 2024-09-20