引言:C# 中的 REST API — 其实很简单
REST API(表述性状态转移)是一种架构风格,允许客户端和服务器通过 HTTP 交换数据。在现代 Web 开发中,API 是为移动应用、网站和微服务创建后端的事实标准。在 .NET 平台(C#)上,REST API 开发已经高度优化:即使你是初学者,也能在半小时内获得一个可工作的原型。
在本文中,我们将创建一个完整的 REST API 来管理待办事项列表。你将学习如何设置 ASP.NET Core Web API 项目、创建数据模型、实现 CRUD 操作(创建、读取、更新、删除),以及通过 Swagger 测试 API。整个过程不会超过 30 分钟。
1. 创建项目并安装依赖
首先,打开 Visual Studio 2022(或已安装 .NET SDK 6.0/7.0/8.0 的 Visual Studio Code)。按照以下步骤操作:
- 选择 创建新项目 → ASP.NET Core Web API。
- 为项目命名,例如
TodoApi。 - 确保选择框架 .NET 8.0(或更高版本)。
- 如果希望使用最小 API,请取消勾选 使用控制器,但本教程将使用经典控制器 — 保持勾选状态。
- 点击 创建。
创建项目后,结构将如下所示:
TodoApi/├── Controllers/├── Models/├── Program.cs└── appsettings.json无需安装额外的包 — 所有必要组件都已内置于 ASP.NET Core 中。
2. 创建数据模型和数据库上下文
2.1. TodoItem 模型
创建一个 Models 文件夹,并向其中添加 TodoItem.cs 类:
namespace TodoApi.Models;
public class TodoItem{ public int Id { get; set; } public string? Title { get; set; } public bool IsComplete { get; set; }}这是一个最小的模型:一个标识符、一个任务标题和一个完成标志。
2.2. 数据库上下文
现在让我们为 Entity Framework Core 创建一个上下文。在 Data 文件夹(请创建它)中,添加 TodoContext.cs 类:
using Microsoft.EntityFrameworkCore;using TodoApi.Models;
namespace TodoApi.Data;
public class TodoContext : DbContext{ public TodoContext(DbContextOptions<TodoContext> options) : base(options) { }
public DbSet<TodoItem> TodoItems { get; set; }}2.3. 配置 Program.cs
打开 Program.cs 并添加上下文注册和 Swagger:
using Microsoft.EntityFrameworkCore;using TodoApi.Data;
var builder = WebApplication.CreateBuilder(args);
// 添加控制器builder.Services.AddControllers();
// 注册数据库上下文(为简化使用内存数据库)builder.Services.AddDbContext<TodoContext>(opt => opt.UseInMemoryDatabase("TodoList"));
// Swaggerbuilder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment()){ app.UseSwagger(); app.UseSwaggerUI();}
app.UseHttpsRedirection();app.UseAuthorization();app.MapControllers();
app.Run();注意:我们使用了 UseInMemoryDatabase — 这对原型开发很方便。在实际项目中,请将其替换为 SQL Server、PostgreSQL 或其他数据库。
3. 实现带有 CRUD 操作的控制器
现在是最重要的部分 — 控制器。在 Controllers 文件夹中创建文件 TodoItemsController.cs:
using Microsoft.AspNetCore.Mvc;using Microsoft.EntityFrameworkCore;using TodoApi.Data;using TodoApi.Models;
namespace TodoApi.Controllers;
[Route("api/[controller]")][ApiController]public class TodoItemsController : ControllerBase{ private readonly TodoContext _context;
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(int 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();
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem); }
// PUT: api/TodoItems/5 [HttpPut("{id}")] public async Task<IActionResult> PutTodoItem(int id, TodoItem todoItem) { if (id != todoItem.Id) { return BadRequest(); }
_context.Entry(todoItem).State = EntityState.Modified;
try { await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!TodoItemExists(id)) { return NotFound(); } else { throw; } }
return NoContent(); }
// DELETE: api/TodoItems/5 [HttpDelete("{id}")] public async Task<IActionResult> DeleteTodoItem(int id) { var todoItem = await _context.TodoItems.FindAsync(id); if (todoItem == null) { return NotFound(); }
_context.TodoItems.Remove(todoItem); await _context.SaveChangesAsync();
return NoContent(); }
private bool TodoItemExists(int id) { return _context.TodoItems.Any(e => e.Id == id); }}