.NET Core API 完整教程:从零开始构建现代 API

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

.net core api 教程
(图片来源网络,侵删)

目录

  1. 第一部分:准备工作与环境搭建

    • 安装 .NET SDK
    • 选择代码编辑器
    • 创建第一个 API 项目
  2. 第二部分:项目结构与核心概念

    • 解析项目文件
    • 理解核心文件
    • 运行与调试
  3. 第三部分:创建数据模型与数据库上下文

    • 定义实体模型
    • 创建数据库上下文
    • 配置依赖注入
  4. 第四部分:实现 CRUD 操作 (控制器)

    .net core api 教程
    (图片来源网络,侵删)
    • 创建控制器
    • 实现 GET 读取操作
    • 实现 POST 创建操作
    • 实现 PUT/PATCH 更新操作
    • 实现 DELETE 删除操作
  5. 第五部分:数据验证与错误处理

    • 使用 Data Annotations 进行验证
    • 处理模型验证错误
    • 全局异常处理
  6. 第六部分:高级功能:异步、日志与Swagger

    • 异步编程最佳实践
    • 集成日志记录
    • 使用 Swagger/OpenAPI 自动生成文档
  7. 第七部分:部署到生产环境

    • 发布应用
    • 部署选项简介 (IIS, Docker, Azure App Service)

第一部分:准备工作与环境搭建

安装 .NET SDK

.NET SDK (Software Development Kit) 包含了构建和运行 .NET 应用所需的一切,你可以从 官方 .NET 下载页面 下载并安装适合你操作系统的版本,安装后,打开终端(命令提示符或 PowerShell)并运行以下命令验证安装:

.net core api 教程
(图片来源网络,侵删)
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(),这会导致死锁。
  • Taskasync 方法的返回类型,除非是 void(仅用于事件处理程序)。

集成日志记录

.NET Core 内置了强大的日志记录系统,你只需要在 Program.cs 中配置它即可。

Program.csbuilder 部分添加:

// ... 其他 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 会自动读取它们并显示在文档中。

    1. MyFirstApi.csproj 文件中,添加 <GenerateDocumentationFile>true</GenerateDocumentationFile>
    2. 在你的代码中,使用 来添加注释。

    示例:

    /// <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):

    1. 在 Windows Server 上安装 IIS 和 URL Rewrite 模块。
    2. publish 文件夹中的内容复制到 IIS 网站目录。
    3. 在 IIS 管理器中,为你的网站添加一个应用程序池,并选择 "无托管代码"。
    4. 配置网站,并将处理程序映射添加为 aspnetCore,指向 dotnet.exe
  • 部署到 Docker (容器化):

    1. 创建一个 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"]
    2. 在终端中构建并运行 Docker 镜像:

      docker build -t myfirstapi .
      docker run -d -p 8080:80 --name myapi-container myfirstapi
    3. 你的 API 就在 Docker 容器中运行,并通过主机的 8080 端口访问。

  • 部署到 Azure App Service: 这是最简单的方式之一,你只需要一个 Azure 账户。

    1. 登录 Azure 门户
    2. 创建一个新的 "App Service"。
    3. 在 "部署中心" 选项卡中,连接你的 GitHub 仓库。
    4. Azure 会自动检测到这是一个 .NET 项目,并在每次你推送代码到 GitHub 时自动构建和部署你的应用。

总结与后续学习

恭喜!你已经成功地从零开始构建、测试并了解了如何部署一个 .NET Core API。

后续可以学习的方向:

  1. 身份验证与授权: 学习如何使用 JWT Bearer Token、OAuth2 或 ASP.NET Core Identity 来保护你的 API。
  2. 数据库迁移: 使用 EF Core 的迁移功能来管理数据库架构的变更 (dotnet ef migrations add ...)。
  3. API 版本控制: 学习如何为你的 API 实现版本控制,以支持新旧客户端。
  4. 性能优化: 探索缓存、响应压缩、数据库查询优化等。
  5. 更复杂的架构: 了解领域驱动设计、CQRS 等更高级的架构模式。
  6. 使用 gRPC: 对于需要高性能、低延迟的内部服务通信,可以学习 gRPC。

这份教程为你打下了坚实的基础,希望你在 .NET Core 的世界里探索愉快!