WebAPI-02-2 实现WebAPI+CRUD示例
一、GetPlayerById 通过主键获取详细
1、IPlayerRepository仓储定义接口
Player? GetPlayerById(Guid playerId);
2、实现类PlayerRepository中实现,条件查询,返回可空实体类
public Player? GetPlayerById(Guid playerId) { return FindByCondition(player => player.Id == playerId) .FirstOrDefault(); }
3、控制器,
{id}是请求传过来的/1
加Name = "PlayerById",生成路由路径
[HttpGet("{id}", Name = "PlayerById")] public IActionResult GetPlayerById(Guid id) { try { var player = repository.Player.GetPlayerById(id); if (player is null) { return NotFound(); } var result = mapper.Map<PlayerDto>(player); return Ok(result); } catch (Exception ex) { logger.LogError($"{ex.Message}"); return StatusCode(500); } }
返回实体默认是不包括导航属性的,一个玩家有多个角色
二、GetPlayerWithCharacters 要包含返回子集,要创建两个DTO
1)创建子集角色的DTO,不包含导航
public class CharacterDto { public Guid Id { get; set; } public string Nickname { get; set; } public string Classes { get; set; } public int Level { get; set; } public DateTime DateCreated { get; set; } }
2)创建关系WithDto,继承PlayerDto,不需要额外的外键信息,只在控制器里面转换使用
public class PlayerWithCharactersDto : PlayerDto { public IEnumerable<CharacterDto> Characters { get; set; } }
3)映射
CreateMap<Player, PlayerWithCharactersDto>();
CreateMap<Character, CharacterDto>();
4)仓储接口
Player? GetPlayerWithCharacters(Guid playerId);
5)实现
Include包含查询,返回的实体还是Player,这次包含了导航子集ICollection<Character>
public Player? GetPlayerWithCharacters(Guid playerId) { return FindByCondition(player => player.Id == playerId) .Include(player => player.Characters) .FirstOrDefault(); }
6)控制器,id之后又加了个导航属性 character
[HttpGet("{id}/character")] public IActionResult GetPlayerWithCharacters(Guid id) { try { var player = repository.Player.GetPlayerWithCharacters(id); if (player is null) { return NotFound(); } var result = mapper.Map<PlayerWithCharactersDto>(player); return Ok(result); } catch (Exception ex) { logger.LogError(ex.Message); return StatusCode(500); } }
三、创建
1、接收DTO
id和时间自动生成
public class PlayerForCreationDto { [Required(ErrorMessage = "账号不能为空")] [StringLength(20, ErrorMessage = "账号长度不能大于50")] public string Account { get; set; } [Required(ErrorMessage = "账号类型不能为空")] [StringLength(10, ErrorMessage = "账号类型不能大于10")] public string AccountType { get; set; } }
2、映射,接收Dto--->实体
CreateMap<PlayerForCreationDto, Player>();
3、仓储接口
void CreatePlayer(Player player);
4、实现,也可以不写,直接调用基类Create
public void CreatePlayer(Player player) { Create(player); }
5、控制器
[HttpPost]请求
请求体是复杂类型,需要使用[FromBody]来修饰,参数自动绑定,对参数进行基本验证,是否传参
ModelState.IsValid验证,控制器对传入的模型验证的结果,规则就是传入DTO里面添加的特性Required、StringLength等,同MVC视图模型
两个映射操作
1、必须使用实体才能数据库创建
2、返回创建好的完整数据
CreatedAtRoute:返回状态码为201的响应,代表已经创建的意思
响应体response body:中返回新创建的对象
响应头response headers:通过前面的获取PlayerById生成一个url,再加上id参数,一起组成资源地址location参数返回到响应头里,可以直接通过地址直接打开返回的资源
[HttpPost] public IActionResult CreatePlayer([FromBody]PlayerForCreationDto player) { try { if (!ModelState.IsValid) { return BadRequest("无效的请求数据"); } var playerEntity = mapper.Map<Player>(player); repository.Player.CreatePlayer(playerEntity); repository.SaveChangesAsync(); var createdPlayer = mapper.Map<PlayerDto>(playerEntity); return CreatedAtRoute("PlayerById", new { id = createdPlayer.Id }, createdPlayer); } catch (Exception ex) { logger.LogError(ex.Message); return StatusCode(500); } }
四、更新
1、更新DTO
虽然现在内容和创建DTO一样,但是
1)语义区别,让代码具有扩展性
2)有可能存在差异,有些字段可以创建后只读,修改不支持编辑
public class PlayerForUpdateDto { [Required(ErrorMessage = "账号不能为空")] [StringLength(20, ErrorMessage = "账号长度不能大于50")] public string Account { get; set; } [Required(ErrorMessage = "账号类型不能为空")] [StringLength(10, ErrorMessage = "账号类型不能大于10")] public string AccountType { get; set; } }
2、映射,接收Dto--->实体
CreateMap<PlayerForUpdateDto, Player>();
3、仓储接口
4、实现
另外一种,不创建仓储方法
5、控制器
1)HttpPut:代表更新
2)参数:要更新的ID,最新的实体
3)mapper.Map(player, playerEntity);
直接赋值给实体,相同的值会覆盖,直接修改了实体里面的值
然后可以直接调用基类里面的方法repository.Player.Update(playerEntity);
可以不用创建仓储接口
4)NoContent();
修改成功返回204无内容状态码
[HttpPut("{id}")] public IActionResult UpdatePlayer(Guid id, [FromBody] PlayerForUpdateDto player) { try { if (!ModelState.IsValid) { return BadRequest("无效的请求对象"); } var playerEntity = repository.Player.GetPlayerById(id); if (playerEntity is null) { return NotFound("待修改的玩家不存在"); } mapper.Map(player, playerEntity); repository.Player.UpdatePlayer(playerEntity); repository.SaveChangesAsync(); return NoContent(); } catch (Exception ex) { logger.LogError(ex.Message); return StatusCode(500); } }
五、删除
1、控制器
GetPlayerWithCharacters获取id详情
player.Characters.Any()判断是否有关联角色
BadRequest返回400状态码
repository.Player.Delete(player);也可以直接调用基类方法,不创建仓储方法
[HttpDelete("{id}")] public IActionResult DeletePlayer(Guid id) { try { var player = repository.Player.GetPlayerWithCharacters(id); if (player is null) { return BadRequest("该玩家不存在"); } if (player.Characters.Any()) { return BadRequest("该玩家有关联人物角色,不能删除!"); } repository.Player.DeletePlayer(player); repository.SaveChangesAsync(); return NoContent(); } catch (Exception ex) { logger.LogError(ex.Message); return StatusCode(500); } }
swagger/index.html
结果


浙公网安备 33010602011771号