ASP.NET Core Web API GET 请求进阶|Category 与 Country 控制器实战
作者:xiaoxin.gao · 2025-07-02 · 阅读时间:7分钟
1. 引言 在上一节《GET 请求全攻略》中,我们为 Pokemon 资源构建了完整的 GET 接口。作为系列 […]
1. 引言
在上一节《GET 请求全攻略》中,我们为 Pokemon 资源构建了完整的 GET 接口。作为系列的第二篇,本教程将继续深入,面向 Category 与 Country 两种新模型,快速搭建四类常见的 GET Endpoint,并配合 Repository 模式、DTO 限制、AutoMapper 映射和 Swagger 测试,全方位提升你的 API 设计水平。
2. 前置条件与项目结构回顾
- 技术栈:.NET 6+、C#、Entity Framework Core、AutoMapper
- 依赖:已在
Program.cs
中注入DbContext
、AutoMapper、各资源的仓储(AddScoped) -
项目结构:
/Interfaces
IPokemonRepository.cs
ICategoryRepository.cs
ICountryRepository.cs
…
/Repositories
PokemonRepository.cs
CategoryRepository.cs
CountryRepository.cs
…
/DTOs
PokemonDto.cs
CategoryDto.cs
CountryDto.cs
…
/Helpers
MappingProfiles.cs
/Controllers
PokemonController.cs
CategoryController.cs
CountryController.cs
/Data
AppDbContext.cs
3. Category GET 接口实现
3.1 定义接口(ICategoryRepository)
在 Interfaces/ICategoryRepository.cs
,定义三种查询方法:
public interface ICategoryRepository
{
IReadOnlyCollection < Category > GetCategories(); // 列表
Category GetCategory(int id); // 详情
IReadOnlyCollection < Pokemon > GetPokemonsByCategory(int id); // 关联查询
bool CategoryExists(int id); // 存在性校验
}
3.2 仓储实现(CategoryRepository)
在 Repositories/CategoryRepository.cs
:
public class CategoryRepository : ICategoryRepository
{
private readonly AppDbContext _context;
public CategoryRepository(AppDbContext context) = > _context = context;
public IReadOnlyCollection < Category > GetCategories() = >
_context.Categories.ToList();
public Category GetCategory(int id) = >
_context.Categories.FirstOrDefault(c = > c.Id == id);
public IReadOnlyCollection < Pokemon > GetPokemonsByCategory(int id) = >
_context.PokemonCategories
.Where(pc = > pc.Category.Id == id)
.Select(pc = > pc.Pokemon)
.ToList();
public bool CategoryExists(int id) = >
_context.Categories.Any(c = > c.Id == id);
}
3.3 DTO 与映射配置(CategoryDto)
-
在
DTOs/CategoryDto.cs
:public class CategoryDto { public int Id { get; set; } public string Name { get; set; } }
-
在
Helpers/MappingProfiles.cs
中添加映射:CreateMap < Category, CategoryDto > (); CreateMap < Pokemon, PokemonDto > (); // 关联查询时使用
3.4 控制器实现(CategoryController)
在 Controllers/CategoryController.cs
中注入仓储与映射器,并实现三条 GET:
[ApiController]
[Route("api/[controller]")]
public class CategoryController : ControllerBase
{
private readonly ICategoryRepository _repo;
private readonly IMapper _mapper;
public CategoryController(ICategoryRepository repo, IMapper mapper)
{
_repo = repo;
_mapper = mapper;
}
// GET api/category
[HttpGet]
public ActionResult < IReadOnlyCollection < CategoryDto > > GetCategories()
{
var categories = _repo.GetCategories();
return Ok(_mapper.Map < IReadOnlyCollection < CategoryDto > > (categories));
}
// GET api/category/{id}
[HttpGet("{id}")]
public ActionResult < CategoryDto > GetCategory(int id)
{
if (!_repo.CategoryExists(id))
return NotFound($"Category {id} 未找到。");
var category = _repo.GetCategory(id);
return Ok(_mapper.Map < CategoryDto > (category));
}
// GET api/category/{id}/pokemons
[HttpGet("{id}/pokemons")]
public ActionResult < IReadOnlyCollection < PokemonDto > > GetPokemonsByCategory(int id)
{
if (!_repo.CategoryExists(id))
return NotFound($"Category {id} 未找到。");
var pokemons = _repo.GetPokemonsByCategory(id);
return Ok(_mapper.Map < IReadOnlyCollection < PokemonDto > > (pokemons));
}
}
4. Country GET 接口实现
4.1 定义接口(ICountryRepository)
在 Interfaces/ICountryRepository.cs
:
public interface ICountryRepository
{
IReadOnlyCollection < Country > GetCountries(); // 列表
Country GetCountry(int id); // 详情
IReadOnlyCollection < Owner > GetOwnersByCountry(int id); // 关联查询
bool CountryExists(int id); // 存在性校验
}
4.2 仓储实现(CountryRepository)
在 Repositories/CountryRepository.cs
:
public class CountryRepository : ICountryRepository
{
private readonly AppDbContext _context;
public CountryRepository(AppDbContext context) = > _context = context;
public IReadOnlyCollection < Country > GetCountries() = >
_context.Countries.ToList();
public Country GetCountry(int id) = >
_context.Countries.FirstOrDefault(c = > c.Id == id);
public IReadOnlyCollection < Owner > GetOwnersByCountry(int id) = >
_context.Owners
.Where(o = > o.Country.Id == id)
.ToList();
public bool CountryExists(int id) = >
_context.Countries.Any(c = > c.Id == id);
}
4.3 DTO 与映射配置(CountryDto)
-
在
DTOs/CountryDto.cs
:public class CountryDto { public int Id { get; set; } public string Name { get; set; } }
-
MappingProfiles
中添加:CreateMap < Country, CountryDto > (); CreateMap < Owner, OwnerDto > (); // 后续 Owner GET 关联时使用
4.4 控制器实现(CountryController)
[ApiController]
[Route("api/[controller]")]
public class CountryController : ControllerBase
{
private readonly ICountryRepository _repo;
private readonly IMapper _mapper;
public CountryController(ICountryRepository repo, IMapper mapper)
{
_repo = repo;
_mapper = mapper;
}
// GET api/country
[HttpGet]
public ActionResult < IReadOnlyCollection<CountryDto > > GetCountries() = >
Ok(_mapper.Map < IReadOnlyCollection<CountryDto > > (_repo.GetCountries()));
// GET api/country/{id}
[HttpGet("{id}")]
public ActionResult < CountryDto > GetCountry(int id)
{
if (!_repo.CountryExists(id))
return NotFound($"Country {id} 未找到。");
return Ok(_mapper.Map < CountryDto > (_repo.GetCountry(id)));
}
// GET api/country/{id}/owners
[HttpGet("{id}/owners")]
public ActionResult < IReadOnlyCollection < OwnerDto > > GetOwnersByCountry(int id)
{
if (!_repo.CountryExists(id))
return NotFound($"Country {id} 未找到。");
var owners = _repo.GetOwnersByCountry(id);
return Ok(_mapper.Map < IReadOnlyCollection < OwnerDto > > (owners));
}
}
5. 测试与验证(Swagger)
- 运行项目后打开 Swagger UI(
/swagger
)。 -
Category 测试:
GET /api/category
→ 全部分类GET /api/category/1
→ 单个分类详情GET /api/category/1/pokemons
→ 该分类下所有 Pokemon
-
Country 测试:
GET /api/country
→ 全部国家GET /api/country/2
→ 单个国家详情GET /api/country/2/owners
→ 该国家下所有 Owner
确认返回状态码与 DTO 格式符合预期,确保数据准确且不泄露多余字段。
6. 小结与最佳实践
- 接口优先:先定义
IRepository
接口,再实现,保证松耦合与可测性。 - DTO 限制:精准返回所需字段,避免泄露敏感信息。
- AutoMapper:简化实体→DTO 映射,保证 Controller 层清爽。
- 关联查询:对导航属性显式使用
Select
或Include
,避免默认未加载导致 Null。 - REST 风格:列表
/resource
、详情/resource/{id}
、子资源/resource/{id}/sub}
。 - Swagger 文档:及时测试并更新接口文档,为前后端协同提供清晰契约。
通过本篇示例,你已掌握为多种模型批量构建 GET 接口的完整流程,进一步夯实了 API 开发实战能力。下一篇,我们将继续完成剩余 Controller,并切入 POST/PUT/DELETE 操作,敬请期待!
原文引自YouTube视频:https://www.youtube.com/watch?v=bSvYErXVRtQ
热门推荐
一个账号试用1000+ API
助力AI无缝链接物理世界 · 无需多次注册
3000+提示词助力AI大模型
和专业工程师共享工作效率翻倍的秘密
热门API
- 1. AI文本生成
- 2. AI图片生成_文生图
- 3. AI图片生成_图生图
- 4. AI图像编辑
- 5. AI视频生成_文生视频
- 6. AI视频生成_图生视频
- 7. AI语音合成_文生语音
- 8. AI文本生成(中国)
最新文章
- GPT-OSS 模型驱动在线编程课 AI 助教,3 天打造追问式对话 API
- Kimi K2-0905 256K上下文API状态管理优化:长流程复杂任务实战
- Go工程化(四) API 设计上: 项目结构 & 设计
- 如何获取Dify AI API开放平台秘钥(分步指南)
- 手机号查询API:获取个人信息的便捷工具
- 大型项目中如何规避 Claude 限流风险?开发实战指南
- 为什么要编写高质量的在线API文档?
- 基于DeepSeek-V3.1开源技术的开发者社区应用审核API指南
- 2025 PHP REST API 快速入门指南:从零构建到实战
- TikTok API使用指南:短视频图像生成实践案例
- Java 生鲜电商平台 – API 接口设计之 token、timestamp、sign 具体架构与实现
- HIP-1217热点:DeFi镜像节点API实时gRPC流式余额校验实战