
一步步教你進行 Python REST API 身份驗證
Program.cs
中注入 DbContext
、AutoMapper、各資源的倉儲(AddScoped)項目結(jié)構(gòu):
/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
在 Interfaces/ICategoryRepository.cs
,定義三種查詢方法:
public interface ICategoryRepository
{
IReadOnlyCollection < Category > GetCategories(); // 列表
Category GetCategory(int id); // 詳情
IReadOnlyCollection < Pokemon > GetPokemonsByCategory(int id); // 關(guān)聯(lián)查詢
bool CategoryExists(int id); // 存在性校驗
}
在 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);
}
在 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 > (); // 關(guān)聯(lián)查詢時使用
在 Controllers/CategoryController.cs
中注入倉儲與映射器,并實現(xiàn)三條 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));
}
}
在 Interfaces/ICountryRepository.cs
:
public interface ICountryRepository
{
IReadOnlyCollection < Country > GetCountries(); // 列表
Country GetCountry(int id); // 詳情
IReadOnlyCollection < Owner > GetOwnersByCountry(int id); // 關(guān)聯(lián)查詢
bool CountryExists(int id); // 存在性校驗
}
在 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);
}
在 DTOs/CountryDto.cs
:
public class CountryDto
{
public int Id { get; set; }
public string Name { get; set; }
}
MappingProfiles
中添加:
CreateMap < Country, CountryDto > ();
CreateMap < Owner, OwnerDto > (); // 后續(xù) Owner GET 關(guān)聯(lián)時使用
[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));
}
}
/swagger
)。Category 測試:
GET /api/category
→ 全部分類GET /api/category/1
→ 單個分類詳情GET /api/category/1/pokemons
→ 該分類下所有 PokemonCountry 測試:
GET /api/country
→ 全部國家GET /api/country/2
→ 單個國家詳情GET /api/country/2/owners
→ 該國家下所有 Owner確認返回狀態(tài)碼與 DTO 格式符合預期,確保數(shù)據(jù)準確且不泄露多余字段。
IRepository
接口,再實現(xiàn),保證松耦合與可測性。Select
或 Include
,避免默認未加載導致 Null。/resource
、詳情 /resource/{id}
、子資源 /resource/{id}/sub}
。通過本篇示例,你已掌握為多種模型批量構(gòu)建 GET 接口的完整流程,進一步夯實了 API 開發(fā)實戰(zhàn)能力。下一篇,我們將繼續(xù)完成剩余 Controller,并切入 POST/PUT/DELETE 操作,敬請期待!
原文引自YouTube視頻:https://www.youtube.com/watch?v=bSvYErXVRtQ