.NET Core API 完整教程:从零开始构建现代 API
本教程将引导你使用 .NET 6 (或更高版本) 和 C# 创建一个功能齐全的 RESTful API,我们将遵循最佳实践,包括分层架构、依赖注入、数据验证和异步编程。

目录
-
- 安装 .NET SDK
- 选择代码编辑器
- 创建第一个 API 项目
-
- 解析项目文件
- 理解核心文件
- 运行与调试
-
- 定义实体模型
- 创建数据库上下文
- 配置依赖注入
-
(图片来源网络,侵删)- 创建控制器
- 实现 GET 读取操作
- 实现 POST 创建操作
- 实现 PUT/PATCH 更新操作
- 实现 DELETE 删除操作
-
- 使用 Data Annotations 进行验证
- 处理模型验证错误
- 全局异常处理
-
- 异步编程最佳实践
- 集成日志记录
- 使用 Swagger/OpenAPI 自动生成文档
-
- 发布应用
- 部署选项简介 (IIS, Docker, Azure App Service)
第一部分:准备工作与环境搭建
安装 .NET SDK
.NET SDK (Software Development Kit) 包含了构建和运行 .NET 应用所需的一切,你可以从 官方 .NET 下载页面 下载并安装适合你操作系统的版本,安装后,打开终端(命令提示符或 PowerShell)并运行以下命令验证安装:

dotnet --version
你应该能看到已安装的 .NET 版本(.NET 6.0.x)。
选择代码编辑器
强烈推荐使用 Visual Studio 2025 (Community 版免费) 或 Visual Studio Code (免费、跨平台)。
- Visual Studio 2025: 提供了强大的“开箱即用”体验,包括模板、智能感知、调试器和发布工具,对于 Windows 开发者是首选。
- Visual Studio Code: 轻量级但功能强大,通过安装 C# Dev Kit 扩展可以获得近乎 VS 的体验,适合所有平台的开发者。
创建第一个 API 项目
打开终端,导航到你想要创建项目的文件夹,然后运行以下命令:
# 创建一个新的 Web API 项目 dotnet new webapi -n MyFirstApi # 进入项目目录 cd MyFirstApi
dotnet new: .NET 的模板命令。webapi: 指定使用 Web API 模板。-n MyFirstApi: 指定项目名称为MyFirstApi。
你的第一个 API 项目已经创建好了!你可以通过运行以下命令来启动它:
dotnet run
终端会显示类似以下信息,并告诉你 API 正在运行:
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:7123
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5123
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
打开浏览器,访问 https://localhost:7123/weatherforecast (端口可能不同),你会看到一个 JSON 数组,这是项目模板默认生成的示例 API。
第二部分:项目结构与核心概念
使用你喜欢的编辑器打开 MyFirstApi 文件夹,你会看到以下关键文件和文件夹:
MyFirstApi.csproj: 项目的配置文件,定义了项目依赖、目标框架等。Program.cs: 应用的入口点,在 .NET 6+ 的 Minimal API 模式下,这里配置了所有服务(依赖注入)和 HTTP 请求管道(中间件)。Properties/launchSettings.json: 应用的启动配置文件,定义了不同的环境(如 IIS Express, Kestrel)和启动 URL。Controllers/WeatherForecastController.cs: 模板生成的示例控制器。
解析 Program.cs (这是核心!)
// 1. 创建 WebApplication 构建器
var builder = WebApplication.CreateBuilder(args);
// 2. 配置服务 (依赖注入容器)
// 添加服务到容器中。
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// 3. 构建 WebApplication 实例
var app = builder.Build();
// 4. 配置 HTTP 请求管道
// 配置 HTTP 请求管道。
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers(); // 将路由映射到控制器
app.Run();
builder.Services.AddControllers(): 注册 MVC 控制器所需的服务。builder.Services.AddSwaggerGen(): 注册 Swagger 服务,用于自动生成 API 文档。app.UseSwagger()/app.UseSwaggerUI(): 在开发环境中启用 Swagger UI 界面。app.UseHttpsRedirection(): 强制将 HTTP 请求重定向到 HTTPS。app.MapControllers(): 告诉应用去查找[ApiController]和[Route]特性,并自动设置路由。
第三部分:创建数据模型与数据库上下文
我们将创建一个简单的 "Todo List" API,包含 TodoItem 模型。
定义实体模型
在项目根目录下创建一个名为 Models 的文件夹,并添加一个 TodoItem.cs 文件:
Models/TodoItem.cs
using System.ComponentModel.DataAnnotations;
namespace MyFirstApi.Models
{
public class TodoItem
{
public long Id { get; set; }
[Required]
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
}
Id: 唯一标识符。Name: 待办事项的名称,使用[Required]特性标记为必填。IsComplete: 标记任务是否完成。
创建数据库上下文
数据库上下文 (DbContext) 是 Entity Framework Core (EF Core) 的核心,它负责与数据库进行交互。
安装 EF Core 的 In-Memory 提供程序,用于开发阶段的本地测试:
dotnet add package Microsoft.EntityFrameworkCore.InMemory
在项目根目录下创建一个名为 Data 的文件夹,并添加一个 TodoContext.cs 文件:
Data/TodoContext.cs
using Microsoft.EntityFrameworkCore;
using MyFirstApi.Models;
namespace MyFirstApi.Data
{
public class TodoContext : DbContext
{
public TodoContext(DbContextOptions<TodoContext> options)
: base(options)
{
}
public DbSet<TodoItem> TodoItems { get; set; } = null!;
}
}
DbContext: 继承自 EF Core 的基类。DbSet<TodoItem>: 表示数据库中TodoItems表的集合。
配置依赖注入
我们需要在 Program.cs 中注册 TodoContext,以便在应用中可以通过依赖注入来使用它。
修改 Program.cs:
// ... 其他 using 语句
using Microsoft.EntityFrameworkCore;
using MyFirstApi.Data;
using MyFirstApi.Models;
var builder = WebApplication.CreateBuilder(args);
// 1. 添加 DbContext 服务
// 注册 TodoContext,并使用 In-Memory 数据库
builder.Services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
// 2. 添加其他服务
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// ... 管道配置部分不变
builder.Services.AddDbContext<TodoContext>(): 将TodoContext注册到 DI 容器中。opt.UseInMemoryDatabase("TodoList"): 配置 EF Core 使用内存数据库,并给它一个名字 "TodoList"。
第四部分:实现 CRUD 操作 (控制器)
控制器是处理 HTTP 请求并返回响应的类,我们将创建一个 TodoItemsController。
在 Controllers 文件夹下,创建 TodoItemsController.cs 文件:
Controllers/TodoItemsController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MyFirstApi.Data;
using MyFirstApi.Models;
namespace MyFirstApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
private readonly TodoContext _context;
// TodoContext 通过构造函数注入,这是依赖注入的标准做法
public TodoItemsController(TodoContext context)
{
_context = context;
}
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
{
return await _context.TodoItems.ToListAsync();
}
// GET: api/TodoItems/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
// POST: api/TodoItems
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
// 返回 201 Created 响应,并在 Location 头中指向新创建的资源
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}
// PUT: api/TodoItems/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
if (id != todoItem.Id)
{
return BadRequest(); // ID 不匹配
}
_context.Entry(todoItem).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!_context.TodoItems.Any(e => e.Id == id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent(); // 成功更新,返回 204 No Content
}
// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
}
}
代码解释:
[Route("api/[controller]")]: 设置控制器的路由模板。[controller]是一个令牌,会被替换为控制器的名称(这里是 "TodoItems")。[ApiController]: 启用 API 特定的行为,如自动模型验证和 400 响应。- 依赖注入:
TodoContext通过构造函数注入,这使得控制器与具体的数据实现解耦。 - 异步方法: 所有数据库操作都使用了
async/await,这是 I/O 密集型操作的最佳实践,可以避免阻塞线程,提高服务器吞吐量。 - HTTP 方法特性:
[HttpGet],[HttpPost]等将方法映射到相应的 HTTP 动词。 - 响应类型:
Ok(): 返回 200 OK 和数据。NotFound(): 返回 404 Not Found。CreatedAtAction(): 返回 201 Created,并包含新创建资源的链接。NoContent(): 返回 204 No Content,常用于 PUT 和 DELETE 成功后。
运行你的应用 (dotnet run),然后你可以使用 Postman、curl 或 Swagger UI (https://localhost:<port>/swagger) 来测试你的 API。
第五部分:数据验证与错误处理
使用 Data Annotations 进行验证
我们已经为 TodoItem.Name 添加了 [Required] 特性,当你发送一个没有 Name 字段的 POST 请求时,EF Core 和 [ApiController] 会自动返回一个 400 Bad Request 错误,并包含验证错误信息。
发送一个空的 JSON 请求体:
{}
你会得到类似这样的响应:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",: "One or more validation errors occurred.",
"status": 400,
"errors": {
"Name": [
"The Name field is required."
]
}
}
处理模型验证错误
为了更优雅地处理验证错误,可以创建一个自定义的过滤器,但更推荐在控制器方法内部进行显式检查,以获得更精细的控制。
修改 PostTodoItem 方法:
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
// 显式检查模型状态
if (!ModelState.IsValid)
{
return BadRequest(ModelState); // 返回详细的验证错误
}
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}
全局异常处理
为了捕获应用中未处理的异常并返回一个友好的错误响应,可以使用中间件。
在 Program.cs 中,在 app.UseHttpsRedirection(); 之后添加以下代码:
// ... 其他配置
app.UseHttpsRedirection();
// 全局异常处理中间件
app.UseExceptionHandler(appError =>
{
appError.Run(async context =>
{
context.Response.ContentType = "application/json";
var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
if (contextFeature != null)
{
// 可以根据异常类型返回不同的状态码和信息
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var message = contextFeature.Error switch
{
KeyNotFoundException => "Resource not found",
_ => "An unexpected error occurred."
};
await context.Response.WriteAsync(new ErrorDetails()
{
StatusCode = context.Response.StatusCode,
Message = message
}.ToString());
}
});
});
app.UseAuthorization();
// ...
你需要创建一个 ErrorDetails 类来序列化错误信息:
Models/ErrorDetails.cs
using System.Text.Json;
namespace MyFirstApi.Models
{
public class ErrorDetails
{
public int StatusCode { get; set; }
public string? Message { get; set; }
public override string ToString() => JsonSerializer.Serialize(this);
}
}
并在 Program.cs 顶部添加 using 语句:
using System.Net; using MyFirstApi.Models;
当你的应用抛出一个未处理的异常时,它会返回一个结构化的 JSON 错误,而不是一个通用的 500 错误页面。
第六部分:高级功能:异步、日志与Swagger
异步编程最佳实践
我们已经在前面的控制器中使用了 async/await,请记住以下原则:
- 所有 I/O 操作都应该是异步的:如数据库查询、文件读写、HTTP 调用等。
- 不要在
async方法中使用.Result或.Wait(),这会导致死锁。 Task是async方法的返回类型,除非是void(仅用于事件处理程序)。
集成日志记录
.NET Core 内置了强大的日志记录系统,你只需要在 Program.cs 中配置它即可。
在 Program.cs 的 builder 部分添加:
// ... 其他 using 语句 using Microsoft.Extensions.Logging; var builder = WebApplication.CreateBuilder(args); // 添加日志记录服务,并配置为输出到控制台 builder.Logging.AddConsole(); builder.Logging.AddDebug(); // ... 其他服务注册
你可以在任何通过依赖注入获取 ILogger<T> 的地方记录日志。
在 TodoItemsController 中:
public class TodoItemsController : ControllerBase
{
private readonly TodoContext _context;
private readonly ILogger<TodoItemsController> _logger;
public TodoItemsController(TodoContext context, ILogger<TodoItemsController> logger)
{
_context = context;
_logger = logger;
}
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
_logger.LogInformation("Getting TodoItem with id: {Id}", id);
var todoItem = await _context.TodoItems.FindAsync(id);
// ...
}
}
使用 Swagger/OpenAPI 自动生成文档
Swagger 已经在模板中配置好了,当你运行应用时,访问 https://localhost:<port>/swagger,你会看到一个交互式的 API 文档页面。
-
描述你的 API: 你可以为控制器和方法添加 XML 注释,Swagger 会自动读取它们并显示在文档中。
- 在
MyFirstApi.csproj文件中,添加<GenerateDocumentationFile>true</GenerateDocumentationFile>。 - 在你的代码中,使用 来添加注释。
示例:
/// <summary> /// 获取所有的待办事项 /// </summary> /// <returns>待办事项列表</returns> [HttpGet] public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems() { // ... } - 在
-
测试 API: Swagger UI 允许你直接在页面上测试你的 API 端点,非常方便。
第七部分:部署到生产环境
开发完成后,你需要将应用部署到服务器上。
发布应用
在终端中,运行以下命令来发布你的应用:
dotnet publish -c Release -o ./publish
-c Release: 以发布模式编译,会进行优化并移除调试符号。-o ./publish: 将发布输出到publish文件夹。
publish 文件夹中包含了运行应用所需的一切(.dll 文件、依赖项、配置文件等)。
部署选项简介
-
部署为独立应用:
- 命令:
dotnet publish -c Release -r win-x64 --self-contained true -o ./publish-self-contained - 这种方式会包含 .NET 运行时,因此可以在任何安装了 Windows 的机器上运行,无需预装 .NET SDK,文件体积较大。
- 命令:
-
部署到 IIS (Windows Server):
- 在 Windows Server 上安装 IIS 和 URL Rewrite 模块。
- 将
publish文件夹中的内容复制到 IIS 网站目录。 - 在 IIS 管理器中,为你的网站添加一个应用程序池,并选择 "无托管代码"。
- 配置网站,并将处理程序映射添加为
aspnetCore,指向dotnet.exe。
-
部署到 Docker (容器化):
-
创建一个
Dockerfile在项目根目录:# 使用官方的 .NET 6 运行时镜像作为基础镜像 FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 # 使用官方的 .NET 6 SDK 镜像进行构建 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["MyFirstApi.csproj", "MyFirstApi/"] RUN dotnet restore "MyFirstApi/MyFirstApi.csproj" COPY . . WORKDIR "/src/MyFirstApi" RUN dotnet build "MyFirstApi.csproj" -c Release -o /app/build # 从构建阶段发布应用 FROM build AS publish RUN dotnet publish "MyFirstApi.csproj" -c Release -o /app/publish /p:UseAppHost=false # 最终阶段,基于基础镜像并复制发布的应用 FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "MyFirstApi.dll"]
-
在终端中构建并运行 Docker 镜像:
docker build -t myfirstapi . docker run -d -p 8080:80 --name myapi-container myfirstapi
-
你的 API 就在 Docker 容器中运行,并通过主机的 8080 端口访问。
-
-
部署到 Azure App Service: 这是最简单的方式之一,你只需要一个 Azure 账户。
- 登录 Azure 门户。
- 创建一个新的 "App Service"。
- 在 "部署中心" 选项卡中,连接你的 GitHub 仓库。
- Azure 会自动检测到这是一个 .NET 项目,并在每次你推送代码到 GitHub 时自动构建和部署你的应用。
总结与后续学习
恭喜!你已经成功地从零开始构建、测试并了解了如何部署一个 .NET Core API。
后续可以学习的方向:
- 身份验证与授权: 学习如何使用 JWT Bearer Token、OAuth2 或 ASP.NET Core Identity 来保护你的 API。
- 数据库迁移: 使用 EF Core 的迁移功能来管理数据库架构的变更 (
dotnet ef migrations add ...)。 - API 版本控制: 学习如何为你的 API 实现版本控制,以支持新旧客户端。
- 性能优化: 探索缓存、响应压缩、数据库查询优化等。
- 更复杂的架构: 了解领域驱动设计、CQRS 等更高级的架构模式。
- 使用 gRPC: 对于需要高性能、低延迟的内部服务通信,可以学习 gRPC。
这份教程为你打下了坚实的基础,希望你在 .NET Core 的世界里探索愉快!
