ASP.NET Core 基础
如果你要做 .NET 后端开发,那么 ASP.NET Core 几乎就是主线框架。它负责把 HTTP 请求接进来,再通过路由、中间件、控制器、依赖注入、配置系统等一整套机制,把请求最终处理成响应结果。
很多初学者学 ASP.NET Core 时容易陷入一个误区:只记住了几个 API 写法,却没有真正理解它的请求处理模型。这样一旦项目复杂起来,就很容易搞不清楚中间件顺序、依赖注入注册位置、路由匹配方式,甚至连 401、403 或参数绑定错误都不知道从哪里排查。
这篇文章的目标,就是帮你把这条主线完整串起来。
第一章 ASP.NET Core 是做什么的
ASP.NET Core 是 .NET 平台上的现代 Web 框架,常用于:
- 构建 Web API
- 构建 MVC 网站
- 开发后台管理系统
- 开发微服务接口
- 编写后台任务和托管服务
对大多数后端开发者来说,最常接触的是 ASP.NET Core Web API。
第二章 一个 Web 请求是如何被处理的
先建立整体模型,后面学每个模块时就不容易乱。
一个请求的大致流程如下:
- 客户端发起 HTTP 请求
- 请求进入 Kestrel 服务器
- 请求依次经过中间件 pipeline
- 路由系统匹配目标端点
- 执行控制器或 Minimal API 处理逻辑
- 返回响应给客户端
你可以把这个过程想成一条流水线:
HTTP Request
-> Middleware 1
-> Middleware 2
-> Middleware 3
-> Endpoint Routing
-> Controller / Minimal API
-> HTTP Response
这个模型里最重要的几个关键词是:
- 中间件
- 路由
- 端点
- 依赖注入
- 模型绑定
第三章 创建一个最基本的 Web API 项目
3.1 使用 CLI 创建
dotnet new webapi -n DemoApi
启动项目:
dotnet run
3.2 典型的 Program.cs
现代 ASP.NET Core 项目通常采用精简启动模型,最核心的代码通常集中在 Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapControllers();
app.Run();
这段代码虽然不长,但它几乎已经包含了 Web 项目最核心的骨架:
- 创建应用构建器
- 注册服务
- 构建应用
- 配置中间件
- 映射路由
- 启动服务
第四章 中间件是什么
4.1 中间件的基本概念
中间件可以理解成请求处理流水线上的一个节点。请求进入应用后,会按顺序经过这些节点,每个节点都可以:
- 读取请求信息
- 修改请求
- 决定是否继续往后传递
- 在响应返回前做额外处理
常见职责包括:
- 全局异常处理
- 日志记录
- HTTPS 重定向
- 静态资源处理
- CORS
- 认证
- 授权
4.2 一个最基础的中间件顺序示例
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
顺序很重要,因为请求是按你注册的顺序流过去的。
4.3 为什么顺序这么重要
比如:
- 还没认证就先授权,授权一定失败
- 路由还没建立就访问控制器,当然找不到端点
- 异常处理中间件放太靠后,前面的异常可能拦不住
所以学习中间件时,最重要的不是死背所有 UseXxx,而是建立“流水线顺序”的意识。
4.4 自定义中间件示例
public class RequestLogMiddleware
{
private readonly RequestDelegate _next;
public RequestLogMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
Console.WriteLine($"Request: {context.Request.Method} {context.Request.Path}");
await _next(context);
Console.WriteLine($"Response: {context.Response.StatusCode}");
}
}
注册:
app.UseMiddleware<RequestLogMiddleware>();
这个例子非常适合帮助你理解中间件“前后包裹”的执行特点。
第五章 路由与端点
5.1 路由的作用
路由负责把 URL 和 HTTP 方法映射到具体的处理逻辑。
例如:
GET /api/users/1POST /api/usersPUT /api/users/1DELETE /api/users/1
5.2 控制器路由示例
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
return Ok(new { Id = id, Name = "Tom" });
}
}
这段代码表示:
- 控制器路由前缀是
api/users HttpGet("{id}")匹配GET /api/users/{id}
5.3 常见路由写法
[HttpGet]
[HttpGet("{id}")]
[HttpPost]
[HttpPut("{id}")]
[HttpDelete("{id}")]
5.4 路由参数与查询参数
路由参数:
GET /api/users/10
对应:
[HttpGet("{id}")]
public IActionResult GetById(int id)
查询参数:
GET /api/users?page=1&pageSize=10
对应:
[HttpGet]
public IActionResult GetList(int page = 1, int pageSize = 10)
第六章 控制器模式与 Minimal API
6.1 控制器模式
控制器是最经典、最常见的组织方式,适合中大型业务项目。
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult GetList()
{
var data = new[]
{
new { Id = 1, Name = "Keyboard" },
new { Id = 2, Name = "Mouse" }
};
return Ok(data);
}
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
return Ok(new { Id = id, Name = $"Product-{id}" });
}
}
控制器模式的优点:
- 结构清晰
- 和团队协作习惯契合
- 适合分层和权限控制
- 更适合复杂业务
6.2 Minimal API
Minimal API 更轻量,适合简单服务、工具接口和快速原型。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/ping", () => Results.Ok("pong"));
app.MapGet("/users/{id:int}", (int id) => Results.Ok(new { Id = id, Name = $"user-{id}" }));
app.Run();
它的优点是:
- 写法更短
- 启动成本更低
- 很适合轻量服务
但如果业务复杂度上来,仍然建议转向更清晰的分层结构。
第七章 模型绑定与参数接收
ASP.NET Core 很强大的一点是:它可以自动把请求中的数据绑定到方法参数或对象上。
7.1 查询参数
[HttpGet]
public IActionResult Search(string keyword, int page = 1)
{
return Ok(new { keyword, page });
}
对应请求:
GET /api/products?keyword=keyboard&page=2
7.2 路径参数
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
return Ok(id);
}
7.3 请求体绑定
[HttpPost]
public IActionResult Create(CreateUserRequest request)
{
return Ok(request);
}
public class CreateUserRequest
{
public string UserName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
对应 JSON:
{
"userName": "tom",
"email": "tom@example.com"
}
7.4 为什么 [ApiController] 很重要
给控制器加上 [ApiController] 后,很多 Web API 场景会更友好:
- 自动参数来源推断
- 自动模型校验失败响应
- 更适合前后端分离接口开发
第八章 依赖注入是怎么工作的
8.1 为什么要用依赖注入
如果控制器里自己 new 服务对象,会导致:
- 代码耦合
- 不利于测试
- 不利于替换实现
所以 ASP.NET Core 内置了依赖注入容器。
8.2 注册服务
builder.Services.AddScoped<IUserService, UserService>();
8.3 注入服务
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
public UsersController(IUserService userService)
{
_userService = userService;
}
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
return Ok(_userService.GetName(id));
}
}
8.4 生命周期
最常见的三种生命周期:
AddSingleton:整个应用共用一个实例AddScoped:每个请求一个实例AddTransient:每次解析都新建实例
常见理解:
- 配置类、缓存适合
Singleton - 业务服务、数据库上下文常用
Scoped - 轻量无状态对象可考虑
Transient
第九章 配置系统与选项绑定
9.1 常见配置来源
ASP.NET Core 常见配置来源包括:
appsettings.jsonappsettings.Development.json- 环境变量
- 命令行参数
9.2 配置绑定示例
配置文件:
{
"Jwt": {
"Issuer": "demo-api",
"Audience": "demo-client",
"Key": "your-secret-key"
}
}
选项类:
public class JwtOptions
{
public string Issuer { get; set; } = string.Empty;
public string Audience { get; set; } = string.Empty;
public string Key { get; set; } = string.Empty;
}
注册:
builder.Services.Configure<JwtOptions>(
builder.Configuration.GetSection("Jwt"));
这种方式比在业务代码里到处直接读取配置更清晰,也更适合维护。
第十章 Swagger、异常处理与基础工程能力
一个像样的 Web API 项目,通常不会只停留在“能返回 JSON”。
10.1 Swagger
Swagger 能帮助你:
- 浏览接口列表
- 直接调试接口
- 查看请求和响应结构
注册通常是:
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
开发环境启用:
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
10.2 全局异常处理
如果没有统一异常处理,接口错误可能直接变成杂乱的 500 响应。
实际项目通常会加:
- 全局异常处理中间件
- 统一错误响应结构
- 日志记录
10.3 统一响应结构
很多团队会把接口响应统一成类似结构:
{
"code": 0,
"message": "success",
"data": {}
}
这不是框架强制要求,但对前后端协作很常见。
第十一章 开发 Web API 时常见的基础能力
一个普通接口项目,通常会逐步补齐这些能力:
- 参数接收与模型绑定
- 数据校验
- 统一异常处理
- 统一响应结构
- Swagger 文档
- 认证授权
- 日志记录
- CORS 配置
- 文件上传下载
这些能力并不一定一开始就全部写上,但它们构成了一个成熟接口项目的基础骨架。
第十二章 一个简单但完整的控制器示例
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
[HttpGet]
public IActionResult GetList(int page = 1, int pageSize = 10)
{
var data = new[]
{
new { Id = 1, Name = "Tom" },
new { Id = 2, Name = "Alice" }
};
return Ok(new
{
Page = page,
PageSize = pageSize,
Items = data
});
}
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
return Ok(new { Id = id, Name = $"User-{id}" });
}
[HttpPost]
public IActionResult Create(CreateUserRequest request)
{
return Ok(new
{
Id = 100,
request.UserName,
request.Email
});
}
}
public class CreateUserRequest
{
public string UserName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
这段代码虽然简单,但已经覆盖了:
- 路由
- 查询参数
- 路径参数
- JSON 请求体
- JSON 响应
第十三章 初学者最容易踩的坑
13.1 中间件顺序不对
这会导致认证、授权、异常处理全部出现莫名其妙的问题。
13.2 不理解依赖注入生命周期
例如把不该做成单例的服务注册成 Singleton,可能会引发隐藏问题。
13.3 只会写控制器,不会做分层
控制器里写满业务逻辑会让项目很快失控。
13.4 配置读取到处乱写
缺少选项类和集中管理后,后期维护会变得很痛苦。
13.5 只关注成功路径
真正的接口开发还要考虑:
- 参数错误
- 资源不存在
- 权限不足
- 并发冲突
- 服务器异常
第十四章 入门时最应该真正理解什么
学习 ASP.NET Core 时,建议先把这几个问题彻底搞明白:
- 请求是如何经过中间件进入控制器的?
- 路由为什么能匹配到你的方法?
- 参数为什么能自动绑定到对象上?
- 服务为什么要通过依赖注入获取?
- 配置文件和环境变量是如何共同生效的?
只要这些问题搞清楚了,后面的 Web 开发就会顺得多。
下一步阅读
- 如果你想把接口层的错误治理做完整,阅读
ASP.NET Core 统一异常处理与统一返回 - 如果你想继续补齐认证和权限控制,阅读
.NET 认证与权限基础 - 如果你想把接口和数据库真正打通,阅读
EF Core 基础入门
本文小结
ASP.NET Core 的核心不是背多少 API,而是建立对“请求处理流水线”的整体理解。真正重要的是把下面几块串起来:
- 中间件
- 路由
- 控制器或 Minimal API
- 依赖注入
- 模型绑定
- 配置系统
一旦你把这条主线理解透了,后面继续学认证授权、数据库访问、日志、缓存和测试时,就会越来越顺手。