所有文章 > API开发 > ASP.NET Core Web API GET 请求进阶|Category 与 Country 控制器实战
ASP.NET Core Web API GET 请求进阶|Category 与 Country 控制器实战

ASP.NET Core Web API GET 请求进阶|Category 与 Country 控制器实战

1. 引言

在上一节《GET 请求全攻略》中,我们为 Pokemon 资源构建了完整的 GET 接口。作为系列的第二篇,本教程将继续深入,面向 CategoryCountry 两种新模型,快速搭建四类常见的 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)

  1. 运行项目后打开 Swagger UI(/swagger)。
  2. Category 测试:

    • GET /api/category → 全部分类
    • GET /api/category/1 → 单个分类详情
    • GET /api/category/1/pokemons → 该分类下所有 Pokemon
  3. Country 测试:

    • GET /api/country → 全部国家
    • GET /api/country/2 → 单个国家详情
    • GET /api/country/2/owners → 该国家下所有 Owner

确认返回状态码与 DTO 格式符合预期,确保数据准确且不泄露多余字段。


6. 小结与最佳实践

  • 接口优先:先定义 IRepository 接口,再实现,保证松耦合与可测性。
  • DTO 限制:精准返回所需字段,避免泄露敏感信息。
  • AutoMapper:简化实体→DTO 映射,保证 Controller 层清爽。
  • 关联查询:对导航属性显式使用 SelectInclude,避免默认未加载导致 Null。
  • REST 风格:列表 /resource、详情 /resource/{id}、子资源 /resource/{id}/sub}
  • Swagger 文档:及时测试并更新接口文档,为前后端协同提供清晰契约。

通过本篇示例,你已掌握为多种模型批量构建 GET 接口的完整流程,进一步夯实了 API 开发实战能力。下一篇,我们将继续完成剩余 Controller,并切入 POST/PUT/DELETE 操作,敬请期待!

原文引自YouTube视频:https://www.youtube.com/watch?v=bSvYErXVRtQ

#你可能也喜欢这些API文章!

我们有何不同?

API服务商零注册

多API并行试用

数据驱动选型,提升决策效率

查看全部API→
🔥

热门场景实测,选对API

#AI文本生成大模型API

对比大模型API的内容创意新颖性、情感共鸣力、商业转化潜力

25个渠道
一键对比试用API 限时免费

#AI深度推理大模型API

对比大模型API的逻辑推理准确性、分析深度、可视化建议合理性

10个渠道
一键对比试用API 限时免费