1 api功能一览表
2个查询+增删改
API | 描述 | 请求正文 | 响应正文 |
---|---|---|---|
GET /api/todoitems | 获取所有待办事项 | None | 待办事项的数组 |
GET /api/todoitems/{id} | 按 ID 获取项 | None | 待办事项 |
POST /api/todoitems | 添加新项 | 待办事项 | 待办事项 |
PUT /api/todoitems/{id} | 更新现有项 | 待办事项 | None |
DELETE /api/todoitems/{id} | 删除项 | None | None |
2 创建ASP.NET Core Web API”模板项目
选择ASP.NET Core Web API”模板
项目命名为“TodoApi”
取消https
“框架”为“.NET 6.0 (长期支持)”
选中“使用控制器(取消选中以使用最小 API)”
2.1 更新 launchUrl
Properties\launchSettings.json
"launchUrl": "api/todoitems",
2.2 添加模型类
namespace TodoApi.Models
{
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
}
2.3 添加数据库上下文
TodoDbContext.cs
添加nuget包:Microsoft.EntityFrameworkCore
using Microsoft.EntityFrameworkCore;
using System.Diagnostics.CodeAnalysis;
namespace TodoApi.Models
{
public class TodoDbContext : DbContext
{
public TodoContext(DbContextOptions<TodoContext> options)
: base(options)
{
}
public DbSet<TodoItem> TodoItems { get; set; } = null!;
}
}
2.3 注册数据库上下文(InMemory)
内存数据库
添加nuget包:Microsoft.EntityFrameworkCore.InMemory
public static void Main(string[] args)
{
var builder= WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new() { Title = "TodoApi", Version = "v1" });
});
builder.Services.AddDbContext<TodoDbContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
var app = builder.Build();
if (builder.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "TodoApi v1"));
}
app.MapControllers();
app.Run();
}
2.4 创建api控制器
基于Entityframeworkcore实体的api控制器需要依赖包
添加其操作使用实体框架的 API 控制器
“模型类”中选择“TodoItem (TodoApi.Models)”
在“数据上下文类”中选择“TodoDbContext (TodoAPI.Models)”
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.23" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.23" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.23">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.16" />
</ItemGroup>
TodoItemsController.cs生成如下
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using WebApiTest1.Models.Entities.TodoSystem;
namespace WebApiTest1.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
private readonly TodoDbContext _context;
public TodoItemsController(TodoDbContext context)
{
_context = context;
}
// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
{
if (_context.TodoItems == null)
{
return NotFound();
}
return await _context.TodoItems.ToListAsync();
}
// GET: api/TodoItems/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
if (_context.TodoItems == null)
{
return NotFound();
}
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
// PUT: api/TodoItems/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long 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();
}
// POST: api/TodoItems
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
if (_context.TodoItems == null)
{
return Problem("Entity set 'TodoDbContext.TodoItems' is null.");
}
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
}
// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
if (_context.TodoItems == null)
{
return NotFound();
}
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
private bool TodoItemExists(long id)
{
return (_context.TodoItems?.Any(e => e.Id == id)).GetValueOrDefault();
}
}
}
2.5 更新 PostTodoItem create 方法
使用 nameof 运算符使用nameof(GetTodoItem)
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
if (_context.TodoItems == null)
{
return Problem("Entity set 'TodoDbContext.TodoItems' is null.");
}
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}
2.6 防止过度发布
生产应用通常使用模型的子集来限制输入和返回的数据。
模型的子集通常称为数据传输对象 (DTO)、输入模型或视图模型。
DTO 可用于:
- 防止过度发布。
- 隐藏客户端不应查看的属性。
- 省略一些属性以缩减有效负载大小。
- 平展包含嵌套对象的对象图。 对客户端而言,平展的对象图可能更方便。
如实体模型中含有Secret字段
namespace TodoApi.Models
{
public class TodoItem
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
public string? Secret { get; set; }
}
}
在DTO中不包含Secret字段
namespace TodoApi.Models
{
public class TodoItemDTO
{
public long Id { get; set; }
public string? Name { get; set; }
public bool IsComplete { get; set; }
}
}
添加转换方法ItemToDTO
private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
{
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
};
修改后
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
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<TodoItemDTO>>> GetTodoItems()
{
return await _context.TodoItems
.Select(x => ItemToDTO(x))
.ToListAsync();
}
// GET: api/TodoItems/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return ItemToDTO(todoItem);
}
// PUT: api/TodoItems/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPut("{id}")]
public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
{
if (id != todoItemDTO.Id)
{
return BadRequest();
}
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
todoItem.Name = todoItemDTO.Name;
todoItem.IsComplete = todoItemDTO.IsComplete;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
{
return NotFound();
}
return NoContent();
}
// POST: api/TodoItems
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPost]
public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItemDTO)
{
var todoItem = new TodoItem
{
IsComplete = todoItemDTO.IsComplete,
Name = todoItemDTO.Name
};
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(
nameof(GetTodoItem),
new { id = todoItem.Id },
ItemToDTO(todoItem));
}
// 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();
}
private bool TodoItemExists(long id)
{
return _context.TodoItems.Any(e => e.Id == id);
}
private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
{
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
};
}
}
2.7 使用Mssqlserver数据库
2.7.1 安装nuget包
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.23" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.23">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.16" />
2.7.2 数据库连接字符串配置
"ConnectionStrings": {
"Default": "Data Source=.;Initial Catalog=WebApiTest1Db;User ID=sa;Password=123456"
}
2.7.3 注册TodoDbContext
var connString = builder.Configuration.GetConnectionString("Default");
builder.Services.AddDbContext<TodoDbContext>(opt =>
{
opt.UseSqlServer(connString);
});
2.7.4 数据迁移
nuget包管理器控制台输入:
启动项目选中好,默认项目选中好
# 初始化迁移文件
add-migration init
# 第一次迁移到数据库,会创建数据库
update-database
2.7.5 添加或修改字段
修改TodoItem的Title字段
public string? Title { get; set; }
改为
public string Title { get; set; } 或者
public string Title { get; set; }=null!;
生成新的迁移文件
add-migration TodoItem_Title_require
# 再次迁移到数据库,会在迁移表添加一条记录
update-database
使用dto时要注意:
public string Title { get; set; } 或者
public string Title { get; set; }=null!;
在有[ApiController]的情况下能验证是否为必填参数
`