1. 项目结构与启动流程
一句话总结: ASP.NET Core 应用从 Program.cs 的 WebApplication 构建器开始,Host 负责组装配置、日志、服务容器与中间件管线,是一切 Web 功能的宿主。
现代 ASP.NET Core 采用「最小宿主模型」:WebApplication.CreateBuilder 构建宿主,app 对象同时代表配置、服务容器和中间件管线。理解启动流程,等于拿到整个框架的地图。
// Program.cs 最小宿主
var builder = WebApplication.CreateBuilder(args);
// 1. 注册服务(依赖注入容器)
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// 2. 组装中间件管线(顺序即执行顺序)
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
// 3. 启动监听
app.Run();
| 环节 | 职责 |
|---|---|
CreateBuilder | 加载配置、日志、DI 容器骨架 |
AddControllers | 注册 MVC/API 服务 |
Build | 固化配置与服务容器 |
UseXxx | 往管线里加中间件 |
MapControllers | 把控制器挂进路由表 |
Run | 启动 Kestrel 开始监听 |
一句话: 启动流程可以概括为「注册服务 + 组装管线」。services 管「能注入什么」,中间件管「请求怎么流」,两者分开理解,后续所有 Web 特性都能对号入座。
2. Kestrel 服务器
一句话总结: Kestrel 是 ASP.NET Core 内置的跨平台 Web 服务器,直接承载 HTTP 请求处理,配置与性能调优都围绕它展开。
Kestrel 是运行在进程内的 HTTP 服务器,替代了传统 IIS 的监听角色。它支持 HTTP/1.1、HTTP/2、HTTPS、Unix socket,还能和 IIS、Nginx 组成反向代理架构。
{
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://*:8080",
"Protocols": "Http1AndHttp2"
},
"Https": {
"Url": "https://*:8443",
"Certificate": {
"Path": "/etc/certs/myapp.pfx",
"Password": "secret"
}
}
}
}
}
| Kestrel 配置 | 说明 |
|---|---|
| Endpoints.Url | 监听地址与端口 |
| Protocols | HTTP/1.1、HTTP/2 |
| MaxRequestBodySize | 请求体上限 |
| AllowSynchronousIO | 是否允许同步 I/O |
| Limits.KeepAliveTimeout | 保活超时 |
避坑: 生产环境常见架构是 Nginx / IIS 反代 → Kestrel。Kestrel 默认不处理
X-Forwarded-*头,启用 HTTPS 重定向与真实 IP 记录时,必须调用ForwardedHeaders中间件,否则拿到的一律是代理地址。
3. 配置系统
一句话总结: 配置是「多源合并 + 环境覆盖」的体系,JSON、环境变量、命令行按优先级叠加,Options 模式把配置强类型化注入业务代码。
ASP.NET Core 配置由 IConfiguration 统一读取,数据来自多个 Provider,后面的覆盖前面的。默认优先级:命令行 > 环境变量 > appsettings.{Environment}.json > appsettings.json。
// appsettings.json
{
"ConnectionStrings": {
"Default": "Server=db;Database=Shop;User Id=sa;Password=xxx"
},
"Jwt": {
"Issuer": "plume",
"ExpireHours": 24
}
}
// Options 模式:强类型绑定
public class JwtOptions
{
public string Issuer { get; set; }
public int ExpireHours { get; set; }
}
builder.Services.Configure<JwtOptions>(builder.Configuration.GetSection("Jwt"));
| 配置源 | 优先级 | 用途 |
|---|---|---|
| appsettings.json | 低 | 默认值 |
| appsettings.{Env}.json | 中 | 按环境覆盖 |
| 环境变量 | 高 | 部署注入密钥 |
| 命令行 | 最高 | 启动参数 |
避坑: 密钥(数据库密码、JWT 私钥)绝不能写进 appsettings.json 提交仓库。生产用环境变量或密钥管理服务注入,并在启动时校验缺失即快速失败。Options 模式让配置从「散落字符串」变成「可测试的强类型对象」。
4. 日志体系
一句话总结: 日志由 LoggerFactory + Provider 构成,用结构化和日志级别过滤生产噪音,ILogger 注入让业务代码与日志实现解耦。
ASP.NET Core 日志走 ILogger<T> 注入,Provider 决定日志去哪(控制台、文件、ES 等)。日志级别从 Trace 到 Critical,按需过滤;结构化日志把占位符变成字段,便于检索。
// 注入并使用结构化日志
public class WeatherService(ILogger<WeatherService> logger)
{
public async Task<Weather> GetAsync(string city)
{
logger.LogInformation("查询天气 {City} 于 {Time}",
city, DateTimeOffset.UtcNow);
return await _api.GetAsync(city);
}
}
// appsettings.json 中的级别过滤
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
| 日志级别 | 含义 | 典型场景 |
|---|---|---|
| Trace / Debug | 最详细 | 本地调试 |
| Information | 常规信息 | 业务埋点 |
| Warning | 异常但不致命 | 重试、降级 |
| Error | 已处理错误 | 异常记录 |
| Critical | 致命 | 进程级故障 |
避坑: 不要用字符串拼接拼日志,
LogInformation($"...{x}")即使日志级别被过滤,拼接也会执行。用占位符模板,过滤掉时零开销。另外结构化日志的字段名用{CamelCase}模板,查询时才能稳定索引。
5. 路由与控制器
一句话总结: 路由把 URL 映射到处理端点,控制器或 Minimal API 都依赖路由表;属性路由、约束与绑定是 API 设计的核心语法。
路由决定「哪个 URL 进哪个 Action」。ASP.NET Core 支持控制器 + 属性路由,也支持 Minimal API 的函数式端点。请求数据通过模型绑定注入参数,返回值经格式化器序列化。
[ApiController]
[Route("api/orders")]
public class OrdersController(OrderService service) : ControllerBase
{
[HttpGet("{id:int}")]
public async Task<ActionResult<Order>> GetById(int id)
{
var order = await service.GetAsync(id);
return order is null ? NotFound() : Ok(order);
}
[HttpPost]
public async Task<ActionResult<Order>> Create(
[FromBody] CreateOrderRequest request)
{
var created = await service.CreateAsync(request);
return CreatedAtAction(nameof(GetById), new { id = created.Id }, created);
}
}
// Minimal API 等价形式
app.MapGet("/api/orders/{id:int}", async (int id, OrderService svc) =>
{
var order = await svc.GetAsync(id);
return order is null ? Results.NotFound() : Results.Ok(order);
});
| 路由要素 | 说明 |
|---|---|
[Route] / [HttpGet] | 控制器级与 Action 级路由 |
{id:int} 约束 | 类型约束,匹配失败自动 404 |
[FromBody] | JSON 请求体绑定 |
[FromQuery] | 查询字符串绑定 |
[ApiController] | 自动模型校验、自动 400 |
一句话: 路由与绑定决定了 API 的「外表」。约束、校验、REST 语义(CreatedAtAction、NotFound、Ok)统一后,接口契约会非常清晰,错误返回也不再散落各处。
6. 静态文件与会话
一句话总结: 静态文件中间件服务前端资源,会话与缓存中间件处理无状态 HTTP 的附加状态,按需开启并注意顺序。
Web 应用不只有 API,还有静态资源、会话、缓存、压缩等横切能力。它们都是中间件,顺序决定行为。
var app = builder.Build();
// 静态文件:wwwroot 下的资源
app.UseStaticFiles();
// 会话:需要先启用才能注入 IHttpContextAccessor / ISession
app.UseSession();
// 响应压缩:按 Accept-Encoding 压缩
app.UseResponseCompression();
// 请求限流:保护端点
app.UseRateLimiter();
| 中间件 | 作用 | 顺序要求 |
|---|---|---|
UseStaticFiles | 服务 wwwroot | 尽早 |
UseSession | 会话状态 | 在路由前 |
UseResponseCompression | gzip/brotli | 靠前 |
UseRateLimiter | 限流 | 在受保护端点前 |
避坑: 中间件顺序即执行顺序,写错会出隐蔽问题——例如静态文件中间件放路由之后,静态资源可能永远匹配不到;会话中间件放授权之后,控制器里读不到 Session。把通用中间件尽量前置,业务中间件靠后。
7. 部署与生产实践
一句话总结: 生产部署围绕环境切换、反向代理、健康检查与监控展开,进程外托管让 Nginx/IIS 与 Kestrel 各司其职。
生产环境的关键词是「托管、观测、韧性」:发布产物用 dotnet publish 输出自包含或框架依赖包;进程外托管由 Nginx/IIS 反代;健康检查端点暴露给负载均衡器;日志与指标接进集中平台。
# 发布为框架依赖,配合 Nginx 反代
dotnet publish -c Release -o /app/publish
# Nginx 反代片段
# location / {
# proxy_pass http://127.0.0.1:8080;
# proxy_set_header Host $host;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# }
// 健康检查端点
builder.Services.AddHealthChecks();
var app = builder.Build();
app.MapHealthChecks("/healthz");
// 生产开关:HTTPS 重定向 + 转发头
if (app.Environment.IsProduction())
{
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor
| ForwardedHeaders.XForwardedProto
});
}
| 生产实践 | 要点 |
|---|---|
| 反向代理 | Nginx/IIS 前置,Kestrel 只监听内网 |
| 健康检查 | /healthz 暴露给 LB 探活 |
| 日志汇聚 | Serilog + 文件/ES 落盘 |
| 环境配置 | appsettings.Production.json 覆盖 |
| 发布 | dotnet publish -c Release |
一句话: 部署的本质是「把开发环境的行为搬到生产环境的约束里」。用环境配置文件切换密钥、用反代终止 TLS、用健康检查做自动扩缩的依据,Web 服务才算真正上了生产。
8. 总结
| 环节 | 要点 |
|---|---|
| 启动流程 | CreateBuilder + 注册服务 + 组装管线 + Run |
| Kestrel | 内置服务器,反代 + ForwardedHeaders |
| 配置 | 多源合并,Options 强类型注入 |
| 日志 | ILogger 注入 + 结构化模板 + 级别过滤 |
| 路由 | 控制器/Minimal API + 约束 + 绑定 |
| 静态与会话 | 中间件顺序即行为 |
| 部署 | publish + 反代 + 健康检查 + 日志汇聚 |
ASP.NET Core 把 Web 开发拆成清晰的层次:宿主、服务器、配置、日志、路由、中间件。这一篇把骨架立起来,依赖注入与中间件管线的深层机制在下一篇展开,两者合起来就是完整的请求处理世界观。
延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。