ASP.NET Core 学习笔记(五):进阶与生产实践

写在前面

本文是 ASP.NET Core 系列收官篇,讲生产环境用得多的进阶能力:过滤器(AOP)、后台服务、健康检查、性能优化、部署


一、过滤器(AOP 切面)

过滤器让你在请求管道的特定阶段插入逻辑,实现日志、缓存、权限、事务等横切关注点。

1.1 六种过滤器(执行顺序)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
请求进来
1. Authorization Filter   授权(最外层)
2. Resource Filter        资源(缓存、短路)
3. Action Filter          方法(前后)
4. Exception Filter       异常
5. Result Filter          结果(前后)
响应出去

6. Always Run Filter      始终执行(Result 的变体,不被短路影响)

1.2 实现自定义过滤器

 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
// Action 过滤器(方法执行前后)
public class LoggingActionFilter : IAsyncActionFilter
{
    private readonly ILogger<LoggingActionFilter> _logger;
    public LoggingActionFilter(ILogger<LoggingActionFilter> logger) => _logger = logger;

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        _logger.LogInformation("执行 {Action}", context.ActionDescriptor.DisplayName);
        await next();   // 执行 Action
        _logger.LogInformation("执行完成");
    }
}

// 异常过滤器(捕获未处理异常)
public class ApiExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        context.Result = new ObjectResult(new { message = context.Exception.Message })
        { StatusCode = 500 };
        context.ExceptionHandled = true;
    }
}

// 注册(全局)
builder.Services.AddControllers(o =>
{
    o.Filters.Add<LoggingActionFilter>();
    o.Filters.Add<ApiExceptionFilter>();
});

1.3 特性形式

1
2
3
4
5
6
// 用特性方式挂到控制器/方法
[ServiceFilter(typeof(LoggingActionFilter))]   // 通过 DI 注入
public class UsersController : ControllerBase { ... }

// 或写一个既是特性又是过滤器的类(用于无需 DI 的简单场景)
public class ValidateAttribute : Attribute, IActionFilter { ... }
1
2
3
何时用过滤器 vs 中间件:
  中间件 — 管道层,对每个请求都跑(日志、异常、CORS)
  过滤器 — MVC 层,能感知控制器/Action(缓存、模型验证、特定授权)

二、后台服务(IHostedService)

需要在后台持续运行的任务(定时任务、消息消费、监控)。

2.1 BackgroundService(推荐基类)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class OrderTimeoutService : BackgroundService   // 继承 BackgroundService
{
    private readonly ILogger<OrderTimeoutService> _logger;

    public OrderTimeoutService(ILogger<OrderTimeoutService> logger) => _logger = logger;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("订单超时服务启动");
        while (!stoppingToken.IsCancellationRequested)
        {
            try { await CancelExpiredOrders(); }
            catch (Exception ex) { _logger.LogError(ex, "处理异常"); }
            await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);   // 每 1 分钟
        }
    }

    private async Task CancelExpiredOrders() { /* 业务 */ }
}

// 注册
builder.Services.AddHostedService<OrderTimeoutService>();

2.2 托管队列(IHostedService + Channel)

后台任务排队执行,结合 Channel:

 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
public class EmailQueueService : BackgroundService
{
    private readonly Channel<EmailMessage> _channel;
    public EmailQueueService(Channel<EmailMessage> channel) => _channel = channel;

    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        await foreach (var email in _channel.Reader.ReadAllAsync(ct))
        {
            await SendEmailAsync(email);
        }
    }
}

// 注册 Channel + 服务
builder.Services.AddSingleton(Channel.CreateUnbounded<EmailMessage>());
builder.Services.AddHostedService<EmailQueueService>();

// 业务代码投递
public class OrderService
{
    private readonly Channel<EmailMessage> _channel;
    public OrderService(Channel<EmailMessage> channel) => _channel = channel;

    public async Task CreateAsync(Order order)
    {
        // ... 创建订单
        await _channel.Writer.WriteAsync(new EmailMessage(order.UserEmail, "下单成功"));
    }
}

2.3 Worker Service

独立的后台服务项目(无 Web):

1
dotnet new worker -n MyWorker   # 创建 Worker 项目
1
2
// 和 BackgroundService 一样的模型,适合纯后台任务
// 可部署为 Windows Service / systemd 服务

三、健康检查

生产环境必备——让负载均衡器、K8s 知道应用是否健康。

1
2
3
4
5
6
7
builder.Services.AddHealthChecks()
    .AddSqlServer(connectionString, name: "sql")
    .AddRedis(redisConnection, name: "redis")
    .AddUrlGroup(new Uri("https://api.github.com"), name: "github");

var app = builder.Build();
app.MapHealthChecks("/health");   // GET /health 返回 200(健康)或 503(不健康)
1
2
3
4
5
6
7
8
用途:
  - K8s liveness/readiness 探针
  - 负载均衡健康判断
  - 监控告警

  自定义健康检查:
    public class MyHealthCheck : IHealthCheck { ... }
    AddCheck<MyHealthCheck>("my-check")

四、性能优化

4.1 响应缓存

1
2
3
4
5
6
builder.Services.AddResponseCaching();
app.UseResponseCaching();

[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Any)]
[HttpGet("list")]
public List<Item> GetList() { ... }   // 60 秒内相同请求走缓存

4.2 输出缓存(.NET 7+)

服务端缓存,比 ResponseCache(浏览器侧)更强:

1
2
3
4
5
6
7
8
9
builder.Services.AddOutputCache();
app.UseOutputCache();

[OutputCache(Duration = 60)]
[HttpGet("hot")]
public async Task<List<Article>> Hot() { ... }

// 标记失效(数据变了主动清除缓存)
_cache EvictTagAsync("articles");

4.3 响应压缩

1
2
3
4
5
6
7
builder.Services.AddResponseCompression(opt =>
{
    opt.EnableForHttps = true;
    opt.Providers.Add<BrotliCompressionProvider>();
    opt.Providers.Add<GzipCompressionProvider>();
});
app.UseResponseCompression();   // 自动压缩 JSON/HTML 等

4.4 异步 + 性能要点

1
2
3
4
5
6
7
✓ 全异步(async/await),不阻塞线程
✓ 数据库用 EF Core 异步 + AsNoTracking(只读查询)
✓ 避免 N+1 查询(Include / 拆分查询)
✓ 连接池配置合理
✓ 减少 JSON 分配(System.Text.Json 高效、Utf8JsonWriter 流式)
✓ 大对象用 ArrayPool 复用
✓ 开 Server GC(多核服务器)

五、部署

5.1 发布

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 框架依赖(需装运行时)
dotnet publish -c Release -o ./publish

# 独立部署(含运行时,目标机不用装 .NET)
dotnet publish -c Release -r linux-x64 --self-contained -o ./publish

# 单文件
dotnet publish -c Release -r linux-x64 --self-contained -p:PublishSingleFile=true

# AOT(启动极快、单文件、无运行时)
dotnet publish -c Release -r linux-x64 -p:PublishAot=true

5.2 systemd 服务(Linux)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# /etc/systemd/system/myapp.service
[Unit]
Description=My App
After=network.target

[Service]
WorkingDirectory=/var/www/myapp
ExecStart=/var/www/myapp/MyApp --urls http://0.0.0.0:5000
Restart=always
Environment=ASPNETCORE_ENVIRONMENT=Production
User=www-data

[Install]
WantedBy=multi-user.target

5.3 反向代理架构

1
2
3
4
5
6
客户端 → Nginx(80/443,HTTPS 终端)→ Kestrel(5000,HTTP)

  Nginx 负责:HTTPS、静态文件、限流、负载均衡
  Kestrel 负责:ASP.NET Core 应用

  配合 UseForwardedHeaders 让应用拿到真实 IP/协议

5.4 Docker 部署

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app

FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app .
EXPOSE 80
ENTRYPOINT ["dotnet", "MyApp.dll"]

六、小结

  • 过滤器:六种过滤器实现 AOP(日志、缓存、异常、授权)
  • 后台服务:BackgroundService 做定时/消费任务,Worker Service 独立后台项目
  • 健康检查:AddHealthChecks + MapHealthChecks,供 K8s/负载均衡探活
  • 性能优化:ResponseCache/OutputCache、压缩、全异步、Server GC
  • 部署:dotnet publish、systemd、Nginx 反代、Docker、AOT

系列总结

ASP.NET Core 五篇完结:

  1. 基础架构:中间件管道 + 依赖注入(两大支柱)
  2. 路由与控制器:路由、模型绑定、验证、Minimal API
  3. 认证与授权:JWT/Cookie、Claims、策略授权
  4. API 设计:RESTful、Swagger、错误处理、版本控制、序列化
  5. 进阶与生产:过滤器、后台服务、健康检查、性能、部署

核心心法:理解中间件管道 + 依赖注入 + Claims 身份模型,ASP.NET Core 就彻底通了。 剩下的都是基于这些的组合应用。