ASP.NET Core 基础

系统讲解 ASP.NET Core 的请求处理流程、中间件、路由、控制器、Minimal API、依赖注入和配置系统。

ASP.NET Core 基础

如果你要做 .NET 后端开发,那么 ASP.NET Core 几乎就是主线框架。它负责把 HTTP 请求接进来,再通过路由、中间件、控制器、依赖注入、配置系统等一整套机制,把请求最终处理成响应结果。

很多初学者学 ASP.NET Core 时容易陷入一个误区:只记住了几个 API 写法,却没有真正理解它的请求处理模型。这样一旦项目复杂起来,就很容易搞不清楚中间件顺序、依赖注入注册位置、路由匹配方式,甚至连 401403 或参数绑定错误都不知道从哪里排查。

这篇文章的目标,就是帮你把这条主线完整串起来。


第一章 ASP.NET Core 是做什么的

ASP.NET Core.NET 平台上的现代 Web 框架,常用于:

  • 构建 Web API
  • 构建 MVC 网站
  • 开发后台管理系统
  • 开发微服务接口
  • 编写后台任务和托管服务

对大多数后端开发者来说,最常接触的是 ASP.NET Core Web API


第二章 一个 Web 请求是如何被处理的

先建立整体模型,后面学每个模块时就不容易乱。

一个请求的大致流程如下:

  1. 客户端发起 HTTP 请求
  2. 请求进入 Kestrel 服务器
  3. 请求依次经过中间件 pipeline
  4. 路由系统匹配目标端点
  5. 执行控制器或 Minimal API 处理逻辑
  6. 返回响应给客户端

你可以把这个过程想成一条流水线:

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/1
  • POST /api/users
  • PUT /api/users/1
  • DELETE /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.json
  • appsettings.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 时,建议先把这几个问题彻底搞明白:

  1. 请求是如何经过中间件进入控制器的?
  2. 路由为什么能匹配到你的方法?
  3. 参数为什么能自动绑定到对象上?
  4. 服务为什么要通过依赖注入获取?
  5. 配置文件和环境变量是如何共同生效的?

只要这些问题搞清楚了,后面的 Web 开发就会顺得多。


下一步阅读


本文小结

ASP.NET Core 的核心不是背多少 API,而是建立对“请求处理流水线”的整体理解。真正重要的是把下面几块串起来:

  • 中间件
  • 路由
  • 控制器或 Minimal API
  • 依赖注入
  • 模型绑定
  • 配置系统

一旦你把这条主线理解透了,后面继续学认证授权、数据库访问、日志、缓存和测试时,就会越来越顺手。